From d963327ed4cd17eb2a2dbb4da81c5360cdbdd092 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Fri, 2 Dec 2016 03:13:54 -0800 Subject: [PATCH 01/53] Make it run again --- eos/db/gamedata/queries.py | 7 +- gui/boosterView.py | 12 +-- gui/builtinContextMenus/ammoPattern.py | 3 +- gui/builtinContextMenus/amount.py | 3 +- gui/builtinContextMenus/cargo.py | 6 +- .../changeAffectingSkills.py | 9 +- gui/builtinContextMenus/damagePattern.py | 11 ++- gui/builtinContextMenus/droneRemoveStack.py | 3 +- gui/builtinContextMenus/droneSplit.py | 3 +- gui/builtinContextMenus/factorReload.py | 6 +- gui/builtinContextMenus/fighterAbilities.py | 3 +- gui/builtinContextMenus/implantSets.py | 5 +- gui/builtinContextMenus/itemRemove.py | 3 +- gui/builtinContextMenus/itemStats.py | 4 +- gui/builtinContextMenus/marketJump.py | 4 +- gui/builtinContextMenus/metaSwap.py | 7 +- gui/builtinContextMenus/moduleAmmoPicker.py | 7 +- .../moduleGlobalAmmoPicker.py | 3 +- gui/builtinContextMenus/priceClear.py | 4 +- gui/builtinContextMenus/project.py | 6 +- gui/builtinContextMenus/shipJump.py | 4 +- gui/builtinContextMenus/tacticalMode.py | 7 +- gui/builtinContextMenus/targetResists.py | 9 +- gui/builtinContextMenus/whProjector.py | 7 +- gui/builtinGraphs/fitDps.py | 5 +- .../pyfaCrestPreferences.py | 11 ++- .../pyfaGeneralPreferences.py | 8 +- .../pyfaHTMLExportPreferences.py | 5 +- .../pyfaNetworkPreferences.py | 11 ++- .../pyfaUpdatePreferences.py | 5 +- gui/builtinStatsViews/firepowerViewFull.py | 3 +- gui/builtinStatsViews/miningyieldViewFull.py | 9 +- gui/builtinStatsViews/priceViewFull.py | 4 +- gui/builtinStatsViews/rechargeViewFull.py | 4 +- gui/builtinStatsViews/resistancesViewFull.py | 1 - gui/builtinViewColumns/attributeDisplay.py | 8 +- gui/builtinViewColumns/baseName.py | 9 +- gui/builtinViewColumns/capacitorUse.py | 4 +- gui/builtinViewColumns/maxRange.py | 5 +- gui/builtinViewColumns/misc.py | 12 +-- gui/builtinViewColumns/price.py | 6 +- gui/builtinViewColumns/propertyDisplay.py | 3 +- gui/builtinViews/emptyView.py | 3 +- gui/builtinViews/entityEditor.py | 1 - gui/builtinViews/fittingView.py | 36 +++---- gui/builtinViews/fleetView.py | 1 - gui/builtinViews/implantEditor.py | 8 +- gui/cargoView.py | 16 ++-- gui/characterEditor.py | 51 +++++----- gui/characterSelection.py | 23 ++--- gui/chromeTabs.py | 5 +- gui/commandView.py | 16 ++-- gui/crestFittings.py | 29 +++--- gui/droneView.py | 17 ++-- gui/fighterView.py | 16 ++-- gui/gangView.py | 39 ++++---- gui/graphFrame.py | 6 +- gui/implantView.py | 18 ++-- gui/itemStats.py | 21 ++-- gui/mainFrame.py | 57 ++++++----- gui/mainMenuBar.py | 7 +- gui/marketBrowser.py | 9 +- gui/notesView.py | 8 +- gui/patternEditor.py | 20 ++-- gui/projectedView.py | 18 ++-- gui/propertyEditor.py | 13 +-- gui/resistsEditor.py | 18 ++-- gui/setEditor.py | 22 ++--- gui/shipBrowser.py | 39 ++++---- gui/statsPane.py | 4 +- gui/updateDialog.py | 1 - gui/utils/exportHtml.py | 12 ++- service/__init__.py | 19 ---- service/character.py | 14 ++- service/conversions/__init__.py | 4 +- service/crest.py | 12 +-- service/damagePattern.py | 9 +- service/eveapi.py | 6 +- service/fit.py | 43 +++++---- service/fleet.py | 6 +- service/implantSet.py | 12 +-- service/market.py | 18 ++-- service/network.py | 19 ++-- service/port.py | 95 ++++++++++--------- service/prefetch.py | 16 ++-- service/price.py | 17 ++-- service/pycrest/__init__.py | 2 - service/pycrest/eve.py | 20 ++-- service/pycrest/weak_ciphers.py | 33 +++---- service/server.py | 8 +- service/settings.py | 9 +- service/targetResists.py | 15 +-- service/update.py | 20 ++-- tests/test_package.py | 34 +++++++ 94 files changed, 631 insertions(+), 583 deletions(-) create mode 100644 tests/test_package.py diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py index b2db92437..66c5ac9b4 100644 --- a/eos/db/gamedata/queries.py +++ b/eos/db/gamedata/queries.py @@ -21,6 +21,7 @@ from sqlalchemy.orm import join, exc from sqlalchemy.sql import and_, or_, select import eos.config +from eos.gamedata import Item, Attribute from eos.db import gamedata_session from eos.db.gamedata.metaGroup import metatypes_table, items_table from eos.db.util import processEager, processWhere @@ -280,9 +281,9 @@ def directAttributeRequest(itemIDs, attrIDs): if not isinstance(itemID, int): raise TypeError("All itemIDs must be integer") - q = select((eos.types.Item.typeID, eos.types.Attribute.attributeID, eos.types.Attribute.value), - and_(eos.types.Attribute.attributeID.in_(attrIDs), eos.types.Item.typeID.in_(itemIDs)), - from_obj=[join(eos.types.Attribute, eos.types.Item)]) + q = select((Item.typeID, Attribute.attributeID, Attribute.value), + and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)), + from_obj=[join(Attribute, Item)]) result = gamedata_session.execute(q).fetchall() return result diff --git a/gui/boosterView.py b/gui/boosterView.py index 5c8dca993..d7e52a4ac 100644 --- a/gui/boosterView.py +++ b/gui/boosterView.py @@ -18,12 +18,12 @@ #=============================================================================== import wx -import service import gui.display as d import gui.globalEvents as GE import gui.marketBrowser as mb from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu +from service.fit import Fit class BoosterViewDrop(wx.PyDropTarget): def __init__(self, dropFn): @@ -85,7 +85,7 @@ class BoosterView(d.Display): event.Skip() def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) @@ -115,7 +115,7 @@ class BoosterView(d.Display): event.Skip() def addItem(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -139,7 +139,7 @@ class BoosterView(d.Display): def removeBooster(self, booster): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeBooster(fitID, self.origional.index(booster)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -150,7 +150,7 @@ class BoosterView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleBooster(fitID, row) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -163,7 +163,7 @@ class BoosterView(d.Display): def spawnMenu(self): sel = self.GetFirstSelected() if sel != -1: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) item = fit.boosters[sel] diff --git a/gui/builtinContextMenus/ammoPattern.py b/gui/builtinContextMenus/ammoPattern.py index d8d789b68..5e38ec66c 100644 --- a/gui/builtinContextMenus/ammoPattern.py +++ b/gui/builtinContextMenus/ammoPattern.py @@ -1,6 +1,5 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import wx import gui.globalEvents as GE @@ -25,7 +24,7 @@ class AmmoPattern(ContextMenu): def activate(self, fullContext, selection, i): item = selection[0] fit = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.setAsPattern(fit, item) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit)) diff --git a/gui/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py index 99acdf247..8b8a2ae26 100644 --- a/gui/builtinContextMenus/amount.py +++ b/gui/builtinContextMenus/amount.py @@ -2,7 +2,6 @@ from gui.contextMenu import ContextMenu from gui.itemStats import ItemStatsDialog import eos.types import gui.mainFrame -import service import gui.globalEvents as GE import wx @@ -46,7 +45,7 @@ class AmountChanger(wx.Dialog): self.button.Bind(wx.EVT_BUTTON, self.change) def change(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() mainFrame = gui.mainFrame.MainFrame.getInstance() fitID = mainFrame.getActiveFit() diff --git a/gui/builtinContextMenus/cargo.py b/gui/builtinContextMenus/cargo.py index 0b4c031b4..3de85804d 100644 --- a/gui/builtinContextMenus/cargo.py +++ b/gui/builtinContextMenus/cargo.py @@ -2,16 +2,16 @@ from gui.contextMenu import ContextMenu from gui.itemStats import ItemStatsDialog import eos.types import gui.mainFrame -import service import gui.globalEvents as GE import wx +from service.fit import Fit class Cargo(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() def display(self, srcContext, selection): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -24,7 +24,7 @@ class Cargo(ContextMenu): return "Add {0} to Cargo".format(itmContext) def activate(self, fullContext, selection, i): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() typeID = int(selection[0].ID) diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index 5111a8d27..df7d896b0 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- from gui.contextMenu import ContextMenu import gui.mainFrame -import service import wx from gui.bitmapLoader import BitmapLoader from eos.types import Skill import gui.globalEvents as GE +from service.fit import Fit +from service.character import Character class ChangeAffectingSkills(ContextMenu): def __init__(self): @@ -15,8 +16,8 @@ class ChangeAffectingSkills(ContextMenu): if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "fittingCharge", "fittingShip"): return False - self.sChar = service.Character.getInstance() - self.sFit = service.Fit.getInstance() + self.sChar = Character.getInstance() + self.sFit = Fit.getInstance() fit = self.sFit.getFit(self.mainFrame.getActiveFit()) self.charID = fit.character.ID @@ -26,7 +27,7 @@ class ChangeAffectingSkills(ContextMenu): if srcContext == "fittingShip": fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() self.stuff = sFit.getFit(fitID).ship cont = sFit.getFit(fitID).ship.itemModifiedAttributes elif srcContext == "fittingCharge": diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index 8d9b6be32..df14e0c9b 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -1,9 +1,10 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE import wx from gui.bitmapLoader import BitmapLoader +from service.fit import Fit +from service.damagePattern import DamagePattern as import_DamagePattern try: from collections import OrderedDict @@ -18,8 +19,8 @@ class DamagePattern(ContextMenu): return srcContext == "resistancesViewFull" and self.mainFrame.getActiveFit() is not None def getText(self, itmContext, selection): - sDP = service.DamagePattern.getInstance() - sFit = service.Fit.getInstance() + sDP = import_DamagePattern.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() self.fit = sFit.getFit(fitID) @@ -59,7 +60,7 @@ class DamagePattern(ContextMenu): menuItem.pattern = pattern # determine active pattern - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() f = sFit.getFit(fitID) dp = f.damagePattern @@ -98,7 +99,7 @@ class DamagePattern(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.setDamagePattern(fitID, pattern) setattr(self.mainFrame,"_activeDmgPattern", pattern) diff --git a/gui/builtinContextMenus/droneRemoveStack.py b/gui/builtinContextMenus/droneRemoveStack.py index f5c94853b..bc608ea1e 100644 --- a/gui/builtinContextMenus/droneRemoveStack.py +++ b/gui/builtinContextMenus/droneRemoveStack.py @@ -1,6 +1,5 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import wx import gui.globalEvents as GE @@ -15,7 +14,7 @@ class ItemRemove(ContextMenu): return "Remove {0} Stack".format(itmContext) def activate(self, fullContext, selection, i): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) diff --git a/gui/builtinContextMenus/droneSplit.py b/gui/builtinContextMenus/droneSplit.py index f4d2b5a44..20cdf0caa 100644 --- a/gui/builtinContextMenus/droneSplit.py +++ b/gui/builtinContextMenus/droneSplit.py @@ -2,7 +2,6 @@ from gui.contextMenu import ContextMenu from gui.itemStats import ItemStatsDialog import gui.mainFrame import gui.globalEvents as GE -import service import wx class DroneSplit(ContextMenu): @@ -48,7 +47,7 @@ class DroneSpinner(wx.Dialog): self.button.Bind(wx.EVT_BUTTON, self.split) def split(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() mainFrame = gui.mainFrame.MainFrame.getInstance() fitID = mainFrame.getActiveFit() if self.context == "droneItem": diff --git a/gui/builtinContextMenus/factorReload.py b/gui/builtinContextMenus/factorReload.py index c258240ea..ec7c1acdb 100644 --- a/gui/builtinContextMenus/factorReload.py +++ b/gui/builtinContextMenus/factorReload.py @@ -1,9 +1,9 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE import wx from gui.bitmapLoader import BitmapLoader +from service.fit import Fit class FactorReload(ContextMenu): def __init__(self): @@ -16,14 +16,14 @@ class FactorReload(ContextMenu): return "Factor in Reload Time" def activate(self, fullContext, selection, i): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.serviceFittingOptions["useGlobalForceReload"] = not sFit.serviceFittingOptions["useGlobalForceReload"] fitID = self.mainFrame.getActiveFit() sFit.refreshFit(fitID) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) def getBitmap(self, context, selection): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) if fit.factorReload: diff --git a/gui/builtinContextMenus/fighterAbilities.py b/gui/builtinContextMenus/fighterAbilities.py index 9a86c23b1..3a659c905 100644 --- a/gui/builtinContextMenus/fighterAbilities.py +++ b/gui/builtinContextMenus/fighterAbilities.py @@ -1,7 +1,6 @@ import wx from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE class FighterAbility(ContextMenu): @@ -48,7 +47,7 @@ class FighterAbility(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.toggleFighterAbility(fitID, ability) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/builtinContextMenus/implantSets.py b/gui/builtinContextMenus/implantSets.py index 8dcf6dd1f..0cbfef508 100644 --- a/gui/builtinContextMenus/implantSets.py +++ b/gui/builtinContextMenus/implantSets.py @@ -1,6 +1,5 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE import wx @@ -59,7 +58,7 @@ class ImplantSets(ContextMenu): if self.context == "implantEditor": # we are calling from character editor, the implant source is different - sChar = service.Character.getInstance() + sChar = Character.getInstance() charID = self.selection.getActiveCharacter() for implant in set.implants: @@ -67,7 +66,7 @@ class ImplantSets(ContextMenu): wx.PostEvent(self.selection, GE.CharChanged()) else: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() for implant in set.implants: sFit.addImplant(fitID, implant.item.ID, recalc=implant == set.implants[-1]) diff --git a/gui/builtinContextMenus/itemRemove.py b/gui/builtinContextMenus/itemRemove.py index c355d98b5..5fbac825f 100644 --- a/gui/builtinContextMenus/itemRemove.py +++ b/gui/builtinContextMenus/itemRemove.py @@ -1,6 +1,5 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import wx import gui.globalEvents as GE @@ -21,7 +20,7 @@ class ItemRemove(ContextMenu): def activate(self, fullContext, selection, i): srcContext = fullContext[0] - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index d8d7e04ac..8c5bc0cd6 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -1,8 +1,8 @@ from gui.contextMenu import ContextMenu from gui.itemStats import ItemStatsDialog import gui.mainFrame -import service import wx +from service.fit import Fit class ItemStats(ContextMenu): def __init__(self): @@ -27,7 +27,7 @@ class ItemStats(ContextMenu): srcContext = fullContext[0] if srcContext == "fittingShip": fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() stuff = sFit.getFit(fitID).ship else: stuff = selection[0] diff --git a/gui/builtinContextMenus/marketJump.py b/gui/builtinContextMenus/marketJump.py index 75884576d..d5804eada 100644 --- a/gui/builtinContextMenus/marketJump.py +++ b/gui/builtinContextMenus/marketJump.py @@ -1,7 +1,7 @@ from gui.contextMenu import ContextMenu from gui.itemStats import ItemStatsDialog import gui.mainFrame -import service +from service.market import Market class MarketJump(ContextMenu): def __init__(self): @@ -19,7 +19,7 @@ class MarketJump(ContextMenu): if not srcContext in validContexts or selection is None or len(selection) < 1: return False - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() item = getattr(selection[0], "item", selection[0]) mktGrp = sMkt.getMarketGroupByItem(item) diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py index 0b2d847ac..38e339074 100644 --- a/gui/builtinContextMenus/metaSwap.py +++ b/gui/builtinContextMenus/metaSwap.py @@ -2,9 +2,10 @@ from gui.contextMenu import ContextMenu from gui.itemStats import ItemStatsDialog import gui.mainFrame -import service import wx import gui.globalEvents as GE +from service.market import Market +from service.fit import Fit class MetaSwap(ContextMenu): def __init__(self): @@ -17,7 +18,7 @@ class MetaSwap(ContextMenu): # Check if list of variations is same for all of selection # If not - don't show the menu - mkt = service.Market.getInstance() + mkt = Market.getInstance() self.variations = None for i in selection: variations = mkt.getVariationsByItems([i.item]) @@ -88,7 +89,7 @@ class MetaSwap(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 3e7ee02cd..6edcf6deb 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- from gui.contextMenu import ContextMenu import gui.mainFrame -import service import wx from gui.bitmapLoader import BitmapLoader from eos.types import Hardpoint import gui.globalEvents as GE +from service.market import Market +from service.fit import Fit class ModuleAmmoPicker(ContextMenu): DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal") @@ -40,7 +41,7 @@ class ModuleAmmoPicker(ContextMenu): return False self.modules = modules - self.charges = list(filter(lambda charge: service.Market.getInstance().getPublicityByItem(charge), validCharges)) + self.charges = list(filter(lambda charge: Market.getInstance().getPublicityByItem(charge), validCharges)) return len(self.charges) > 0 def getText(self, itmContext, selection): @@ -214,7 +215,7 @@ class ModuleAmmoPicker(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.setAmmo(fitID, charge.ID if charge is not None else None, self.modules) diff --git a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py index 41f969b35..29eedf521 100644 --- a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py +++ b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from gui.contextMenu import ContextMenu import gui.mainFrame -import service import wx from gui.bitmapLoader import BitmapLoader from eos.types import Hardpoint @@ -26,7 +25,7 @@ class ModuleGlobalAmmoPicker(ModuleAmmoPicker): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = eos.db.getFit(fitID) diff --git a/gui/builtinContextMenus/priceClear.py b/gui/builtinContextMenus/priceClear.py index da33bfc21..14e4d23b4 100644 --- a/gui/builtinContextMenus/priceClear.py +++ b/gui/builtinContextMenus/priceClear.py @@ -2,7 +2,7 @@ from gui.contextMenu import ContextMenu import gui.mainFrame import wx import gui.globalEvents as GE -import service +from service.market import Market class PriceClear(ContextMenu): def __init__(self): @@ -15,7 +15,7 @@ class PriceClear(ContextMenu): return "Reset Price Cache" def activate(self, fullContext, selection, i): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sMkt.clearPriceCache() wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) diff --git a/gui/builtinContextMenus/project.py b/gui/builtinContextMenus/project.py index 4299f67b6..b40058a79 100644 --- a/gui/builtinContextMenus/project.py +++ b/gui/builtinContextMenus/project.py @@ -1,9 +1,9 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE import wx import eos.db +from service.fit import Fit class Project(ContextMenu): def __init__(self): @@ -13,7 +13,7 @@ class Project(ContextMenu): if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: return False - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -27,7 +27,7 @@ class Project(ContextMenu): return "Project {0} onto Fit".format(itmContext) def activate(self, fullContext, selection, i): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() trigger = sFit.project(fitID, selection[0]) if trigger: diff --git a/gui/builtinContextMenus/shipJump.py b/gui/builtinContextMenus/shipJump.py index bc332e4a4..3cf6b1f12 100644 --- a/gui/builtinContextMenus/shipJump.py +++ b/gui/builtinContextMenus/shipJump.py @@ -1,8 +1,8 @@ import wx from gui.contextMenu import ContextMenu import gui.mainFrame -import service from gui.shipBrowser import Stage3Selected +from service.fit import Fit class ShipJump(ContextMenu): def __init__(self): @@ -16,7 +16,7 @@ class ShipJump(ContextMenu): def activate(self, fullContext, selection, i): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() stuff = sFit.getFit(fitID).ship groupID = stuff.item.group.ID diff --git a/gui/builtinContextMenus/tacticalMode.py b/gui/builtinContextMenus/tacticalMode.py index 3b5cc5043..07dda7bc4 100644 --- a/gui/builtinContextMenus/tacticalMode.py +++ b/gui/builtinContextMenus/tacticalMode.py @@ -1,8 +1,9 @@ import wx from gui.contextMenu import ContextMenu import gui.mainFrame -import service + import gui.globalEvents as GE +from service.fit import Fit class TacticalMode(ContextMenu): def __init__(self): @@ -12,7 +13,7 @@ class TacticalMode(ContextMenu): if self.mainFrame.getActiveFit() is None or srcContext != "fittingShip": return False - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -52,7 +53,7 @@ class TacticalMode(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.setMode(fitID, self.modeIds[event.Id]) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index c0f9bf00c..557a7f276 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -1,9 +1,10 @@ from gui.contextMenu import ContextMenu import gui.mainFrame -import service import gui.globalEvents as GE import wx from gui.bitmapLoader import BitmapLoader +from service import targetResists as svc_targetResists +from service.fit import Fit try: from collections import OrderedDict @@ -18,7 +19,7 @@ class TargetResists(ContextMenu): if self.mainFrame.getActiveFit() is None or srcContext != "firepowerViewFull": return False - sTR = service.TargetResists.getInstance() + sTR = svc_targetResists.TargetResists.getInstance() self.patterns = sTR.getTargetResistsList() self.patterns.sort(key=lambda p: (p.name in ["None"], p.name)) @@ -33,7 +34,7 @@ class TargetResists(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.setTargetResists(fitID, pattern) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -50,7 +51,7 @@ class TargetResists(ContextMenu): item.pattern = pattern # determine active pattern - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() f = sFit.getFit(fitID) tr = f.targetResists diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py index ba0677e20..b47ded3da 100644 --- a/gui/builtinContextMenus/whProjector.py +++ b/gui/builtinContextMenus/whProjector.py @@ -1,8 +1,9 @@ from gui.contextMenu import ContextMenu import gui.mainFrame import gui.globalEvents as GE -import service import wx +from service.market import Market +from service.fit import Fit class WhProjector(ContextMenu): def __init__(self): @@ -16,7 +17,7 @@ class WhProjector(ContextMenu): def getSubMenu(self, context, selection, rootMenu, i, pitem): msw = True if "wxMSW" in wx.PlatformInfo else False - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() effdata = sMkt.getSystemWideEffects() self.idmap = {} @@ -48,7 +49,7 @@ class WhProjector(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.project(fitID, swObj) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/builtinGraphs/fitDps.py b/gui/builtinGraphs/fitDps.py index ef2782674..0e0c4f12e 100644 --- a/gui/builtinGraphs/fitDps.py +++ b/gui/builtinGraphs/fitDps.py @@ -18,12 +18,11 @@ #=============================================================================== from gui.graph import Graph -import service from gui.bitmapLoader import BitmapLoader from eos.graph.fitDps import FitDpsGraph as FitDps from eos.graph import Data import gui.mainFrame -import service +from service.attribute import Attribute class FitDpsGraph(Graph): propertyAttributeMap = {"angle": "maxVelocity", @@ -53,7 +52,7 @@ class FitDpsGraph(Graph): def getIcons(self): icons = {} - sAttr = service.Attribute.getInstance() + sAttr = Attribute.getInstance() for key, attrName in self.propertyAttributeMap.iteritems(): iconFile = sAttr.getAttributeInfo(attrName).icon.iconFile bitmap = BitmapLoader.getBitmap(iconFile, "icons") diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py index 081ccf871..56f559068 100644 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ b/gui/builtinPreferenceViews/pyfaCrestPreferences.py @@ -4,8 +4,9 @@ from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -import service from service.crest import CrestModes +from service.crest import Crest +from service.settings import CRESTSettings from wx.lib.intctrl import IntCtrl @@ -15,7 +16,7 @@ class PFCrestPref ( PreferenceView): def populatePanel( self, panel ): self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.settings = service.settings.CRESTSettings.getInstance() + self.settings = CRESTSettings.getInstance() self.dirtySettings = False dlgWidth = panel.GetParent().GetParent().ClientSize.width mainSizer = wx.BoxSizer( wx.VERTICAL ) @@ -107,16 +108,16 @@ class PFCrestPref ( PreferenceView): def OnModeChange(self, event): self.settings.set('mode', event.GetInt()) self.ToggleProxySettings(self.settings.get('mode')) - service.Crest.restartService() + Crest.restartService() def OnServerChange(self, event): self.settings.set('server', event.GetInt()) - service.Crest.restartService() + Crest.restartService() def OnBtnApply(self, event): self.settings.set('clientID', self.inputClientID.GetValue().strip()) self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip()) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() sCrest.delAllCharacters() def ToggleProxySettings(self, mode): diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index 2d8104bdd..4ea5924bc 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -4,9 +4,9 @@ from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -import service import gui.globalEvents as GE - +from service.settings import SettingsProvider +from service.fit import Fit class PFGeneralPref ( PreferenceView): title = "General" @@ -14,7 +14,7 @@ class PFGeneralPref ( PreferenceView): def populatePanel( self, panel ): self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.dirtySettings = False - self.openFitsSettings = service.SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) + self.openFitsSettings = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) mainSizer = wx.BoxSizer( wx.VERTICAL ) @@ -70,7 +70,7 @@ class PFGeneralPref ( PreferenceView): defCharSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.sFit = service.Fit.getInstance() + self.sFit = Fit.getInstance() self.cbGlobalChar.SetValue(self.sFit.serviceFittingOptions["useGlobalCharacter"]) self.cbGlobalDmgPattern.SetValue(self.sFit.serviceFittingOptions["useGlobalDamagePattern"]) diff --git a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py index c80a7a33e..f018efe8f 100644 --- a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py +++ b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py @@ -5,9 +5,10 @@ from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -import service import gui.globalEvents as GE +from service.settings import HTMLExportSettings + class PFHTMLExportPref ( PreferenceView): title = "HTML Export" @@ -22,7 +23,7 @@ class PFHTMLExportPref ( PreferenceView): def populatePanel( self, panel ): self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.HTMLExportSettings = service.settings.HTMLExportSettings.getInstance() + self.HTMLExportSettings = HTMLExportSettings.getInstance() self.dirtySettings = False dlgWidth = panel.GetParent().GetParent().ClientSize.width mainSizer = wx.BoxSizer( wx.VERTICAL ) diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index bd79dd88c..cda14dd3b 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -4,7 +4,8 @@ from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -import service +from service.settings import NetworkSettings +from service.network import Network class PFNetworkPref ( PreferenceView): title = "Network" @@ -12,8 +13,8 @@ class PFNetworkPref ( PreferenceView): def populatePanel( self, panel ): self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.settings = service.settings.NetworkSettings.getInstance() - self.network = service.Network.getInstance() + self.settings = NetworkSettings.getInstance() + self.network = Network.getInstance() self.dirtySettings = False mainSizer = wx.BoxSizer( wx.VERTICAL ) @@ -164,7 +165,7 @@ class PFNetworkPref ( PreferenceView): self.UpdateApplyButtonState() - if self.nMode is not service.settings.NetworkSettings.PROXY_MODE_MANUAL: # == 2 + if self.nMode is not NetworkSettings.PROXY_MODE_MANUAL: # == 2 self.ToggleProxySettings(False) else: self.ToggleProxySettings(True) @@ -236,7 +237,7 @@ class PFNetworkPref ( PreferenceView): self.UpdateApplyButtonState() - if choice is not service.settings.NetworkSettings.PROXY_MODE_MANUAL: + if choice is not NetworkSettings.PROXY_MODE_MANUAL: self.ToggleProxySettings(False) else: self.ToggleProxySettings(True) diff --git a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py index c49bd0c94..c4ec70039 100644 --- a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py +++ b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py @@ -1,11 +1,10 @@ import wx -import service import os from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader +from service.settings import UpdateSettings -import service import gui.globalEvents as GE @@ -17,7 +16,7 @@ class PFUpdatePref (PreferenceView): "suppressed release notifications, if any." def populatePanel( self, panel ): - self.UpdateSettings = service.settings.UpdateSettings.getInstance() + self.UpdateSettings = UpdateSettings.getInstance() self.dirtySettings = False dlgWidth = panel.GetParent().GetParent().ClientSize.width diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 0e8d1c070..26789a0df 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -18,7 +18,6 @@ #=============================================================================== import wx -import service import gui.mainFrame from gui.statsView import StatsView from gui.bitmapLoader import BitmapLoader @@ -114,7 +113,7 @@ class FirepowerViewFull(StatsView): def switchToMiningYieldView(self, event): # Getting the active fit mainFrame = gui.mainFrame.MainFrame.getInstance() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(mainFrame.getActiveFit()) # Remove ourselves from statsPane's view list self.parent.views.remove(self) diff --git a/gui/builtinStatsViews/miningyieldViewFull.py b/gui/builtinStatsViews/miningyieldViewFull.py index 65ce9ae96..f96def013 100644 --- a/gui/builtinStatsViews/miningyieldViewFull.py +++ b/gui/builtinStatsViews/miningyieldViewFull.py @@ -18,7 +18,6 @@ #=============================================================================== import wx -import service import gui.mainFrame from gui.statsView import StatsView from gui.bitmapLoader import BitmapLoader @@ -103,7 +102,7 @@ class MiningYieldViewFull(StatsView): def switchToFirepowerView(self, event): # Getting the active fit mainFrame = gui.mainFrame.MainFrame.getInstance() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(mainFrame.getActiveFit()) # Remove ourselves from statsPane's view list self.parent.views.remove(self) @@ -124,9 +123,9 @@ class MiningYieldViewFull(StatsView): def refreshPanel(self, fit): #If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats = (("labelFullminingyieldMiner", lambda: fit.minerYield * 3600, 3, 0, 0, u"%s m\u00B3/h",None), - ("labelFullminingyieldDrone", lambda: fit.droneYield * 3600, 3, 0, 0, u"%s m\u00B3/h", None), - ("labelFullminingyieldTotal", lambda: fit.totalYield * 3600, 3, 0, 0, u"%s m\u00B3/h", None)) + stats = (("labelFullminingyieldMiner", lambda: fit.minerYield, 3, 0, 0, u"%s m\u00B3/s",None), + ("labelFullminingyieldDrone", lambda: fit.droneYield, 3, 0, 0, u"%s m\u00B3/s", None), + ("labelFullminingyieldTotal", lambda: fit.totalYield, 3, 0, 0, u"%s m\u00B3/s", None)) counter = 0 for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats: diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index 16935a136..2829a3fa2 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -22,7 +22,7 @@ from gui.statsView import StatsView from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount -import service +from service.market import Market class PriceViewFull(StatsView): name = "priceViewFull" @@ -92,7 +92,7 @@ class PriceViewFull(StatsView): for _ in xrange(cargo.amount): typeIDs.append(cargo.itemID) - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sMkt.getPrices(typeIDs, self.processPrices) self.labelEMStatus.SetLabel("Updating prices...") else: diff --git a/gui/builtinStatsViews/rechargeViewFull.py b/gui/builtinStatsViews/rechargeViewFull.py index c73aaf0b4..5d8c1424a 100644 --- a/gui/builtinStatsViews/rechargeViewFull.py +++ b/gui/builtinStatsViews/rechargeViewFull.py @@ -23,7 +23,7 @@ from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount import gui.mainFrame import gui.builtinStatsViews.resistancesViewFull as rvf -import service +from service.fit import Fit class RechargeViewFull(StatsView): name = "rechargeViewFull" @@ -43,7 +43,7 @@ class RechargeViewFull(StatsView): def toggleEffective(self, event): self.effective = event.effective - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() self.refreshPanel(sFit.getFit(self.mainFrame.getActiveFit())) event.Skip() diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index 55daa813d..f12860a85 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -23,7 +23,6 @@ from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader from gui import pygauge as PG from gui.utils.numberFormatter import formatAmount -import service import gui.mainFrame import gui.builtinViews.fittingView as fv import gui.globalEvents as GE diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index 47d9aa043..c8bda870c 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -22,14 +22,16 @@ from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount -import service +from service.attribute import Attribute +from service.market import Market + import wx class AttributeDisplay(ViewColumn): name = "attr" def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) - sAttr = service.Attribute.getInstance() + sAttr = Attribute.getInstance() info = sAttr.getAttributeInfo(params["attribute"]) self.info = info if params["showIcon"]: @@ -57,7 +59,7 @@ class AttributeDisplay(ViewColumn): self.direct = True self.view = fittingView originalRefresh = fittingView.refresh - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() #Hack into our master view and add a callback for ourselves to know when to query def refresh(stuff): self.directInfo = sMkt.directAttrRequest(stuff, info) if stuff else None diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index 77b4698bd..9381509a0 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -21,9 +21,10 @@ from gui.viewColumn import ViewColumn import gui.mainFrame +#from eos.saveddata.fit import Fit +from service.fit import Fit import wx -from eos.types import Drone, Cargo, Fit, Module, Slot, Rack, Implant, Fighter -import service +from eos.types import Drone, Cargo, Module, Slot, Rack, Implant, Fighter class BaseName(ViewColumn): name = "Base Name" @@ -51,7 +52,7 @@ class BaseName(ViewColumn): else: return "%s (%s)" % (stuff.name, stuff.ship.item.name) elif isinstance(stuff, Rack): - if service.Fit.getInstance().serviceFittingOptions["rackLabels"]: + if Fit.getInstance().serviceFittingOptions["rackLabels"]: if stuff.slot == Slot.MODE: return u'─ Tactical Mode ─' else: @@ -68,7 +69,7 @@ class BaseName(ViewColumn): else: item = getattr(stuff, "item", stuff) - if service.Fit.getInstance().serviceFittingOptions["showMarketShortcuts"]: + if Fit.getInstance().serviceFittingOptions["showMarketShortcuts"]: marketShortcut = getattr(item, "marketShortcut", None) if marketShortcut: diff --git a/gui/builtinViewColumns/capacitorUse.py b/gui/builtinViewColumns/capacitorUse.py index 4a3407ab9..9648d4fde 100644 --- a/gui/builtinViewColumns/capacitorUse.py +++ b/gui/builtinViewColumns/capacitorUse.py @@ -18,12 +18,12 @@ #=============================================================================== import wx -import service from gui.utils.numberFormatter import formatAmount from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader from eos.types import Mode +from service.attribute import Attribute class CapacitorUse(ViewColumn): name = "Capacitor Usage" @@ -32,7 +32,7 @@ class CapacitorUse(ViewColumn): self.mask = wx.LIST_MASK_IMAGE - sAttr = service.Attribute.getInstance() + sAttr = Attribute.getInstance() info = sAttr.getAttributeInfo("capacitorNeed") self.imageId = fittingView.imageList.GetImageIndex("capacitorRecharge_small", "gui") self.bitmap = BitmapLoader.getBitmap("capacitorRecharge_small", "gui") diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py index eb8f8d693..8f3b4f4eb 100644 --- a/gui/builtinViewColumns/maxRange.py +++ b/gui/builtinViewColumns/maxRange.py @@ -20,11 +20,12 @@ from gui import builtinViewColumns from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader -import service from gui.utils.numberFormatter import formatAmount import wx from eos.types import Mode +from service.attribute import Attribute + class MaxRange(ViewColumn): name = "Max Range" def __init__(self, fittingView, params = None): @@ -33,7 +34,7 @@ class MaxRange(ViewColumn): "displayName": False} ViewColumn.__init__(self, fittingView) - sAttr = service.Attribute.getInstance() + sAttr = Attribute.getInstance() info = sAttr.getAttributeInfo("maxRange") self.info = info if params["showIcon"]: diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index bea5c945c..0c7f9ef8f 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -405,9 +405,9 @@ class Miscellanea(ViewColumn): cycleTime = stuff.cycleTime if not miningAmount or not cycleTime: return "", None - minePerHour = (float(miningAmount) * 1000 / cycleTime) * 3600 - text = "{0}/h".format(formatAmount(minePerHour, 3, 0, 3)) - tooltip = "Mining Yield per hour" + minePerSec = float(miningAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(minePerSec, 3, 0, 3)) + tooltip = "Yield per second" return text, tooltip elif itemGroup == "Logistic Drone": armorAmount = stuff.getModifiedItemAttr("armorDamageAmount") @@ -442,9 +442,9 @@ class Miscellanea(ViewColumn): cycleTime = stuff.getModifiedItemAttr("duration") if not miningAmount or not cycleTime: return "", None - minePerHour = (float(miningAmount) * 1000 / cycleTime) * 3600 - text = "{0}/h".format(formatAmount(minePerHour, 3, 0, 3)) - tooltip = "Mining Yield per hour" + minePerSec = float(miningAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(minePerSec, 3, 0, 3)) + tooltip = "Yield per second" return text, tooltip elif itemGroup == "Micro Jump Drive": cycleTime = stuff.getModifiedItemAttr("duration") / 1000 diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 74d2fc890..6f1f95eb2 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -22,7 +22,7 @@ from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount from eos.types import Drone, Cargo import wx -import service +from service.market import Market class Price(ViewColumn): name = "Price" @@ -36,7 +36,7 @@ class Price(ViewColumn): if stuff.item is None or stuff.item.group.name == "Ship Modifiers": return "" - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() price = sMkt.getPriceNow(stuff.item.ID) if not price or not price.price or not price.isValid: @@ -50,7 +50,7 @@ class Price(ViewColumn): return formatAmount(price, 3, 3, 9, currency=True) def delayedText(self, mod, display, colItem): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() def callback(item): price = sMkt.getPriceNow(item.ID) text = formatAmount(price.price, 3, 3, 9, currency=True) if price.price else "" diff --git a/gui/builtinViewColumns/propertyDisplay.py b/gui/builtinViewColumns/propertyDisplay.py index 0bfee5899..cb1008fb1 100644 --- a/gui/builtinViewColumns/propertyDisplay.py +++ b/gui/builtinViewColumns/propertyDisplay.py @@ -21,13 +21,12 @@ from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount import wx -import service class PropertyDisplay(ViewColumn): name = "prop" def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) - sAttr = service.Attribute.getInstance() + sAttr = Attribute.getInstance() attributeSlave = params["attributeSlave"] or params["property"] # This function can throw an exception if the database isn't sane # We need to do a sanity check before this runs diff --git a/gui/builtinViews/emptyView.py b/gui/builtinViews/emptyView.py index 2ea3dbeb1..3088c65a1 100644 --- a/gui/builtinViews/emptyView.py +++ b/gui/builtinViews/emptyView.py @@ -2,7 +2,6 @@ import wx import gui.globalEvents as GE import gui.chromeTabs import gui.mainFrame -import service class BlankPage(wx.Panel): def __init__(self, parent): @@ -23,7 +22,7 @@ class BlankPage(wx.Panel): def pageChanged(self, event): if self.parent.IsActive(self): fitID = None -# sFit = service.Fit.getInstance() +# sFit = Fit.getInstance() # sFit.switchFit(fitID) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index b62060357..c9bffe951 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -1,6 +1,5 @@ import wx from gui.bitmapLoader import BitmapLoader -import service class BaseValidator(wx.PyValidator): def __init__(self): diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 348562e24..f4cac9753 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -19,7 +19,6 @@ import wx import wx.lib.newevent -import service import gui.mainFrame import gui.marketBrowser import gui.display as d @@ -32,6 +31,9 @@ from gui.bitmapLoader import BitmapLoader import gui.builtinViews.emptyView from gui.utils.exportHtml import exportHtml +from service.fit import Fit +from service.market import Market + import gui.globalEvents as GE #Tab spawning handler @@ -55,7 +57,7 @@ class FitSpawner(gui.multiSwitch.TabSpawner): pass if count < 0: startup = getattr(event, "startup", False) # see OpenFitsThread in gui.mainFrame - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() openFitInNew = sFit.serviceFittingOptions["openFitInNew"] mstate = wx.GetMouseState() @@ -207,7 +209,7 @@ class FittingView(d.Display): def pageChanged(self, event): if self.parent.IsActive(self): fitID = self.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.switchFit(fitID) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -262,7 +264,7 @@ class FittingView(d.Display): try: # Sometimes there is no active page after deletion, hence the try block - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.refreshFit(self.getActiveFit()) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) except wx._core.PyDeadObjectError: @@ -282,7 +284,7 @@ class FittingView(d.Display): fitID = event.fitID startup = getattr(event, "startup", False) self.activeFitID = fitID - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() self.updateTab() if not startup or startup == 2: # see OpenFitsThread in gui.mainFrame self.Show(fitID is not None) @@ -293,7 +295,7 @@ class FittingView(d.Display): event.Skip() def updateTab(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.getActiveFit(), basic=True) bitmap = BitmapLoader.getImage("race_%s_small" % fit.ship.item.race, "gui") @@ -308,7 +310,7 @@ class FittingView(d.Display): itemID = event.itemID fitID = self.activeFitID if fitID != None: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() if sFit.isAmmo(itemID): modules = [] sel = self.GetFirstSelected() @@ -337,7 +339,7 @@ class FittingView(d.Display): self.click(event) def removeModule(self, module): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) populate = sFit.removeModule(self.activeFitID, fit.modules.index(module)) @@ -351,7 +353,7 @@ class FittingView(d.Display): dstRow, _ = self.HitTest((x, y)) if dstRow != -1 and dstRow not in self.blanks: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() moduleChanged = sFit.changeModule(fitID, self.mods[dstRow].position, srcIdx) if moduleChanged is None: @@ -367,7 +369,7 @@ class FittingView(d.Display): if dstRow != -1 and dstRow not in self.blanks: module = self.mods[dstRow] - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, srcIdx, mstate.CmdDown() and module.isEmpty) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) @@ -375,7 +377,7 @@ class FittingView(d.Display): def swapItems(self, x, y, srcIdx): '''Swap two modules in fitting window''' mstate = wx.GetMouseState() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) if mstate.CmdDown(): @@ -408,7 +410,7 @@ class FittingView(d.Display): known to the display, and not the backend, so it's safe. ''' - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) slotOrder = [Slot.SUBSYSTEM, Slot.HIGH, Slot.MED, Slot.LOW, Slot.RIG, Slot.SERVICE] @@ -481,7 +483,7 @@ class FittingView(d.Display): if self.activeFitID is None: return - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() selection = [] sel = self.GetFirstSelected() contexts = [] @@ -505,7 +507,7 @@ class FittingView(d.Display): sel = self.GetNextSelected(sel) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) contexts.append(("fittingShip", "Ship" if not fit.isStructure else "Citadel")) @@ -537,7 +539,7 @@ class FittingView(d.Display): else: mods = self.getSelectedMods() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() ctrl = wx.GetMouseState().CmdDown() or wx.GetMouseState().MiddleDown() click = "ctrl" if ctrl is True else "right" if event.GetButton() == 3 else "left" @@ -571,7 +573,7 @@ class FittingView(d.Display): self.Freeze() d.Display.refresh(self, stuff) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) slotMap = {} @@ -637,7 +639,7 @@ class FittingView(d.Display): for i in xrange(len(self.DEFAULT_COLS)): columnsWidths.append(0) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() try: fit = sFit.getFit(self.activeFitID) except: diff --git a/gui/builtinViews/fleetView.py b/gui/builtinViews/fleetView.py index bc6eef9a2..7112778f3 100644 --- a/gui/builtinViews/fleetView.py +++ b/gui/builtinViews/fleetView.py @@ -1,6 +1,5 @@ import wx.gizmos import gui.fleetBrowser -import service from gui.bitmapLoader import BitmapLoader #Tab spawning handler diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index bcdb96c7b..851f49754 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -1,10 +1,10 @@ import wx -import service import gui.display as d from gui.bitmapLoader import BitmapLoader import gui.PFSearchBox as SBox from gui.marketBrowser import SearchBox from wx.lib.buttons import GenBitmapButton +from service.market import Market class BaseImplantEditorView (wx.Panel): def addMarketViewImage(self, iconFile): @@ -71,7 +71,7 @@ class BaseImplantEditorView (wx.Panel): # Populate the market tree - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() for mktGrp in sMkt.getImplantTree(): iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp)) childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) @@ -126,7 +126,7 @@ class BaseImplantEditorView (wx.Panel): def expandLookup(self, event): tree = self.availableImplantsTree - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() parent = event.Item child, _ = tree.GetFirstChild(parent) text = tree.GetItemText(child) @@ -235,7 +235,7 @@ class ItemView(d.Display): self.update(self.items) def scheduleSearch(self, event=None): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() search = self.searchBox.GetLineText(0) # Make sure we do not count wildcard as search symbol diff --git a/gui/cargoView.py b/gui/cargoView.py index 91bfb2b5f..ed58e4ba3 100644 --- a/gui/cargoView.py +++ b/gui/cargoView.py @@ -18,12 +18,12 @@ #=============================================================================== import wx -import service import gui.display as d import gui.marketBrowser as mb from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import globalEvents as GE +from service.fit import Fit class CargoViewDrop(wx.PyDropTarget): def __init__(self, dropFn): @@ -75,7 +75,7 @@ class CargoView(d.Display): if data[0] == "fitting": self.swapModule(x, y, int(data[1])) elif data[0] == "market": - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.addCargo(self.mainFrame.getActiveFit(), int(data[1]), 1) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) @@ -94,7 +94,7 @@ class CargoView(d.Display): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() row = self.GetFirstSelected() if row != -1: sFit.removeCargo(fitID, self.GetItemData(row)) @@ -103,7 +103,7 @@ class CargoView(d.Display): def swapModule(self, x, y, modIdx): '''Swap a module from fitting window with cargo''' - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) dstRow, _ = self.HitTest((x, y)) mstate = wx.GetMouseState() @@ -125,7 +125,7 @@ class CargoView(d.Display): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) @@ -161,7 +161,7 @@ class CargoView(d.Display): col = self.getColumn(event.Position) if col != self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() cargo = self.cargo[self.GetItemData(row)] sFit.removeCargo(fitID, self.original.index(cargo)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -174,11 +174,11 @@ class CargoView(d.Display): def spawnMenu(self): sel = self.GetFirstSelected() if sel != -1: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) cargo = fit.cargo[sel] - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sourceContext = "cargoItem" itemContext = sMkt.getCategoryByItem(cargo.item).name diff --git a/gui/characterEditor.py b/gui/characterEditor.py index bf7db18bf..e551ce9fa 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -22,11 +22,14 @@ import wx import wx.lib.newevent import wx.gizmos from gui.bitmapLoader import BitmapLoader -import service from gui.contextMenu import ContextMenu import gui.globalEvents as GE from gui.builtinViews.implantEditor import BaseImplantEditorView from gui.builtinViews.entityEditor import EntityEditor, BaseValidator +from service.fit import Fit +from service.character import Character +from service.network import AuthenticationError, TimeoutError +from service.market import Market class CharacterTextValidor(BaseValidator): @@ -60,7 +63,7 @@ class CharacterEntityEditor(EntityEditor): self.SetEditorValidator(CharacterTextValidor) def getEntitiesFromContext(self): - sChar = service.Character.getInstance() + sChar = Character.getInstance() charList = sorted(sChar.getCharacterList(), key=lambda c: c.name) # Do some processing to ensure that we have All 0 and All 5 at the top @@ -76,21 +79,21 @@ class CharacterEntityEditor(EntityEditor): return charList def DoNew(self, name): - sChar = service.Character.getInstance() + sChar = Character.getInstance() return sChar.new(name) def DoRename(self, entity, name): - sChar = service.Character.getInstance() + sChar = Character.getInstance() sChar.rename(entity, name) def DoCopy(self, entity, name): - sChar = service.Character.getInstance() + sChar = Character.getInstance() copy = sChar.copy(entity) sChar.rename(copy, name) return copy def DoDelete(self, entity): - sChar = service.Character.getInstance() + sChar = Character.getInstance() sChar.delete(entity) @@ -104,7 +107,7 @@ class CharacterEditor(wx.Frame): self.mainFrame = parent #self.disableWin = wx.WindowDisabler(self) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) @@ -181,7 +184,7 @@ class CharacterEditor(wx.Frame): self.Destroy() def saveChar(self, event): - sChr = service.Character.getInstance() + sChr = Character.getInstance() char = self.entityEditor.getActiveEntity() sChr.saveCharacter(char.ID) wx.PostEvent(self, GE.CharListUpdated()) @@ -192,7 +195,7 @@ class CharacterEditor(wx.Frame): dlg.ShowModal() def revertChar(self, event): - sChr = service.Character.getInstance() + sChr = Character.getInstance() char = self.entityEditor.getActiveEntity() sChr.revertCharacter(char.ID) wx.PostEvent(self, GE.CharListUpdated()) @@ -223,7 +226,7 @@ class CharacterEditor(wx.Frame): event.Skip() def Destroy(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() if fitID is not None: sFit.clearFit(fitID) @@ -296,7 +299,7 @@ class SkillTreeView (wx.Panel): self.Layout() def populateSkillTree(self, event=None): - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() dirtyGroups = set([skill.item.group.ID for skill in char.dirtySkills]) @@ -326,7 +329,7 @@ class SkillTreeView (wx.Panel): tree.Delete(child) #Get the real intrestin' stuff - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() for id, name in sChar.getSkills(tree.GetPyData(root)): iconId = self.skillBookImageId @@ -348,7 +351,7 @@ class SkillTreeView (wx.Panel): return char = self.charEditor.entityEditor.getActiveEntity() - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() if char.name not in ("All 0", "All 5"): self.levelChangeMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item)) self.PopupMenu(self.levelChangeMenu) @@ -359,7 +362,7 @@ class SkillTreeView (wx.Panel): def changeLevel(self, event): level = self.levelIds.get(event.Id) - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() selection = self.skillTreeListCtrl.GetSelection() skillID = self.skillTreeListCtrl.GetPyData(selection) @@ -405,19 +408,19 @@ class ImplantEditorView(BaseImplantEditorView): self.determineEnabled() def getImplantsFromContext(self): - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.Parent.Parent.entityEditor.getActiveEntity() return sChar.getImplants(char.ID) def addImplantToContext(self, item): - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.Parent.Parent.entityEditor.getActiveEntity() sChar.addImplant(char.ID, item.ID) def removeImplantFromContext(self, implant): - sChar = service.Character.getInstance() + sChar = Character.getInstance() char = self.Parent.Parent.entityEditor.getActiveEntity() sChar.removeImplant(char.ID, implant) @@ -536,7 +539,7 @@ class APIView (wx.Panel): self.charChanged(None) def charChanged(self, event): - sChar = service.Character.getInstance() + sChar = Character.getInstance() activeChar = self.charEditor.entityEditor.getActiveEntity() ID, key, char, chars = sChar.getApiDetails(activeChar.ID) @@ -575,13 +578,13 @@ class APIView (wx.Panel): self.stStatus.SetLabel("Invalid keyID or vCode!") return - sChar = service.Character.getInstance() + sChar = Character.getInstance() try: activeChar = self.charEditor.entityEditor.getActiveEntity() list = sChar.apiCharList(activeChar.ID, self.inputID.GetLineText(0), self.inputKey.GetLineText(0)) - except service.network.AuthenticationError, e: + except AuthenticationError, e: self.stStatus.SetLabel("Authentication failure. Please check keyID and vCode combination.") - except service.network.TimeoutError, e: + except TimeoutError, e: self.stStatus.SetLabel("Request timed out. Please check network connectivity and/or proxy settings.") except Exception, e: self.stStatus.SetLabel("Error:\n%s"%e.message) @@ -601,7 +604,7 @@ class APIView (wx.Panel): charName = self.charChoice.GetString(self.charChoice.GetSelection()) if charName: try: - sChar = service.Character.getInstance() + sChar = Character.getInstance() activeChar = self.charEditor.entityEditor.getActiveEntity() sChar.apiFetch(activeChar.ID, charName) self.stStatus.SetLabel("Successfully fetched %s\'s skills from EVE API." % charName) @@ -614,7 +617,7 @@ class SaveCharacterAs(wx.Dialog): wx.Dialog.__init__(self, parent, title="Save Character As...", size=wx.Size(300, 60)) self.charID = charID self.parent = parent - sChar = service.Character.getInstance() + sChar = Character.getInstance() name = sChar.getCharName(charID) bSizer1 = wx.BoxSizer(wx.HORIZONTAL) @@ -631,7 +634,7 @@ class SaveCharacterAs(wx.Dialog): self.button.Bind(wx.EVT_BUTTON, self.change) def change(self, event): - sChar = service.Character.getInstance() + sChar = Character.getInstance() sChar.saveCharacterAs(self.charID, self.input.GetLineText(0)) wx.PostEvent(self.parent, GE.CharListUpdated()) diff --git a/gui/characterSelection.py b/gui/characterSelection.py index 47df7e018..7ff6be57e 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -18,10 +18,11 @@ #=============================================================================== import wx -import service from gui.bitmapLoader import BitmapLoader import gui.globalEvents as GE import gui.mainFrame +from service.character import Character +from service.fit import Fit class CharacterSelection(wx.Panel): def __init__(self, parent): @@ -76,7 +77,7 @@ class CharacterSelection(wx.Panel): def refreshCharacterList(self, event=None): choice = self.charChoice - sChar = service.Character.getInstance() + sChar = Character.getInstance() activeChar = self.getActiveCharacter() choice.Clear() @@ -94,7 +95,7 @@ class CharacterSelection(wx.Panel): charID = sChar.all5ID() self.selectChar(charID) fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.changeChar(fitID, charID) choice.Append(u"\u2015 Open Character Editor \u2015", -1) @@ -104,7 +105,7 @@ class CharacterSelection(wx.Panel): event.Skip() def refreshApi(self, event): - sChar = service.Character.getInstance() + sChar = Character.getInstance() ID, key, charName, chars = sChar.getApiDetails(self.getActiveCharacter()) if charName: try: @@ -117,7 +118,7 @@ class CharacterSelection(wx.Panel): def charChanged(self, event): fitID = self.mainFrame.getActiveFit() charID = self.getActiveCharacter() - sChar = service.Character.getInstance() + sChar = Character.getInstance() if charID == -1: # revert to previous character @@ -129,7 +130,7 @@ class CharacterSelection(wx.Panel): else: self.btnRefresh.Enable(False) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.changeChar(fitID, charID) self.charCache = self.charChoice.GetCurrentSelection() wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -148,7 +149,7 @@ class CharacterSelection(wx.Panel): def fitChanged(self, event): self.charChoice.Enable(event.fitID != None) choice = self.charChoice - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() currCharID = choice.GetClientData(choice.GetCurrentSelection()) fit = sFit.getFit(event.fitID) newCharID = fit.character.ID if fit is not None else None @@ -156,7 +157,7 @@ class CharacterSelection(wx.Panel): self.skillReqsStaticBitmap.SetBitmap(self.cleanSkills) self.skillReqsStaticBitmap.SetToolTipString("No active fit") else: - sCharacter = service.Character.getInstance() + sCharacter = Character.getInstance() reqs = sCharacter.checkRequirements(fit) sCharacter.skillReqsDict = {'charname':fit.character.name, 'skills':[]} if len(reqs) == 0: @@ -175,7 +176,7 @@ class CharacterSelection(wx.Panel): self.skillReqsStaticBitmap.SetToolTipString(tip.strip()) if newCharID == None: - sChar = service.Character.getInstance() + sChar = Character.getInstance() self.selectChar(sChar.all5ID()) elif currCharID != newCharID: @@ -187,7 +188,7 @@ class CharacterSelection(wx.Panel): def _buildSkillsTooltip(self, reqs, currItem = "", tabulationLevel = 0): tip = "" - sCharacter = service.Character.getInstance() + sCharacter = Character.getInstance() if tabulationLevel == 0: for item, subReqs in reqs.iteritems(): @@ -210,7 +211,7 @@ class CharacterSelection(wx.Panel): return tip def _buildSkillsTooltipCondensed(self, reqs, currItem = "", tabulationLevel = 0, skillsMap = {}): - sCharacter = service.Character.getInstance() + sCharacter = Character.getInstance() if tabulationLevel == 0: for item, subReqs in reqs.iteritems(): diff --git a/gui/chromeTabs.py b/gui/chromeTabs.py index bfc3adeb4..db1ad694c 100644 --- a/gui/chromeTabs.py +++ b/gui/chromeTabs.py @@ -25,7 +25,8 @@ import gui.utils.fonts as fonts from gui.bitmapLoader import BitmapLoader import gui.utils.fonts as fonts -import service +from service.fit import Fit + _PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() _PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() @@ -699,7 +700,7 @@ class PFTabsContainer(wx.Panel): self.containerHeight = height self.startDrag = False self.dragging = False - self.sFit = service.Fit.getInstance() + self.sFit = Fit.getInstance() self.inclination = 7 if canAdd: diff --git a/gui/commandView.py b/gui/commandView.py index 5971e537c..ad262b6bf 100644 --- a/gui/commandView.py +++ b/gui/commandView.py @@ -20,11 +20,13 @@ import wx import gui.display as d import gui.globalEvents as GE -import service + import gui.droneView from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import eos.types +from service.fit import Fit +from service.market import Market class DummyItem: @@ -88,7 +90,7 @@ class CommandView(d.Display): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() row = self.GetFirstSelected() if row != -1: sFit.removeCommand(fitID, self.get(row)) @@ -99,7 +101,7 @@ class CommandView(d.Display): if type == "fit": activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() draggedFit = sFit.getFit(fitID) sFit.addCommandFit(activeFit, draggedFit) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) @@ -118,7 +120,7 @@ class CommandView(d.Display): return fit.name def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) @@ -168,7 +170,7 @@ class CommandView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleCommandFit(fitID, item) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -183,7 +185,7 @@ class CommandView(d.Display): if sel != -1: item = self.get(sel) if item is None: return - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() fitSrcContext = "commandFit" fitItemContext = item.name context = ((fitSrcContext,fitItemContext),) @@ -204,6 +206,6 @@ class CommandView(d.Display): col = self.getColumn(event.Position) if col != self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeCommand(fitID, self.get(row)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 0b2cf54cf..d51693a59 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -4,7 +4,6 @@ import json import wx import requests -import service from service.crest import CrestModes from eos.types import Cargo @@ -23,7 +22,7 @@ class CrestFittings(wx.Frame): self.mainFrame = parent mainSizer = wx.BoxSizer(wx.VERTICAL) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() characterSelectSizer = wx.BoxSizer( wx.HORIZONTAL ) @@ -90,7 +89,7 @@ class CrestFittings(wx.Frame): event.Skip() def updateCharList(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() chars = sCrest.getCrestCharacters() if len(chars) == 0: @@ -123,7 +122,7 @@ class CrestFittings(wx.Frame): event.Skip() def getActiveCharacter(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() if sCrest.settings.get('mode') == CrestModes.IMPLICIT: return sCrest.implicitCharacter.ID @@ -132,7 +131,7 @@ class CrestFittings(wx.Frame): return self.charChoice.GetClientData(selection) if selection is not None else None def fetchFittings(self, event): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() try: waitDialog = wx.BusyInfo("Fetching fits, please wait...", parent=self) fittings = sCrest.getFittings(self.getActiveCharacter()) @@ -150,12 +149,12 @@ class CrestFittings(wx.Frame): if not selection: return data = self.fitTree.fittingsTreeCtrl.GetPyData(selection) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fits = sFit.importFitFromBuffer(data) self.mainFrame._openAfterImport(fits) def deleteFitting(self, event): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() selection = self.fitView.fitSelection if not selection: return @@ -180,7 +179,7 @@ class ExportToEve(wx.Frame): self.mainFrame = parent self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() mainSizer = wx.BoxSizer(wx.VERTICAL) hSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -217,7 +216,7 @@ class ExportToEve(wx.Frame): self.Centre(wx.BOTH) def updateCharList(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() chars = sCrest.getCrestCharacters() if len(chars) == 0: @@ -245,7 +244,7 @@ class ExportToEve(wx.Frame): event.Skip() def getActiveCharacter(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() if sCrest.settings.get('mode') == CrestModes.IMPLICIT: return sCrest.implicitCharacter.ID @@ -254,7 +253,7 @@ class ExportToEve(wx.Frame): return self.charChoice.GetClientData(selection) if selection is not None else None def exportFitting(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() self.statusbar.SetStatusText("", 0) @@ -264,7 +263,7 @@ class ExportToEve(wx.Frame): return self.statusbar.SetStatusText("Sending request and awaiting response", 1) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() try: data = sFit.exportCrest(fitID) @@ -321,7 +320,7 @@ class CrestMgmt(wx.Dialog): event.Skip() def popCharList(self): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() chars = sCrest.getCrestCharacters() self.lcCharacters.DeleteAllItems() @@ -335,7 +334,7 @@ class CrestMgmt(wx.Dialog): self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE) def addChar(self, event): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() uri = sCrest.startServer() webbrowser.open(uri) @@ -343,7 +342,7 @@ class CrestMgmt(wx.Dialog): item = self.lcCharacters.GetFirstSelected() if item > -1: charID = self.lcCharacters.GetItemData(item) - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() sCrest.delCrestCharacter(charID) self.popCharList() diff --git a/gui/droneView.py b/gui/droneView.py index 531c28d05..89dbc4dd4 100644 --- a/gui/droneView.py +++ b/gui/droneView.py @@ -19,12 +19,13 @@ import wx -import service import gui.globalEvents as GE import gui.marketBrowser as mb import gui.display as d from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu +from service.fit import Fit +from service.market import Market class DroneViewDrop(wx.PyDropTarget): def __init__(self, dropFn): @@ -141,7 +142,7 @@ class DroneView(d.Display): wx.PostEvent(self.mainFrame, mb.ItemSelected(itemID=int(data[1]))) def _merge(self, src, dst): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() if sFit.mergeDrones(fitID, self.drones[src], self.drones[dst]): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -151,7 +152,7 @@ class DroneView(d.Display): 'Fighter Bombers', 'Combat Utility Drones', 'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones') def droneKey(self, drone): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() groupName = sMkt.getMarketGroupByItem(drone.item).name @@ -159,7 +160,7 @@ class DroneView(d.Display): drone.item.name) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) @@ -193,7 +194,7 @@ class DroneView(d.Display): def addItem(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -218,7 +219,7 @@ class DroneView(d.Display): def removeDrone(self, drone): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeDrone(fitID, self.original.index(drone)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -229,7 +230,7 @@ class DroneView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() drone = self.drones[row] sFit.toggleDrone(fitID, self.original.index(drone)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -244,7 +245,7 @@ class DroneView(d.Display): if sel != -1: drone = self.drones[sel] - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sourceContext = "droneItem" itemContext = sMkt.getCategoryByItem(drone.item).name menu = ContextMenu.getMenu((drone,), (sourceContext, itemContext)) diff --git a/gui/fighterView.py b/gui/fighterView.py index b4468c94f..63e7b3b22 100644 --- a/gui/fighterView.py +++ b/gui/fighterView.py @@ -19,7 +19,6 @@ import wx -import service import gui.globalEvents as GE import gui.marketBrowser as mb import gui.mainFrame @@ -27,6 +26,7 @@ import gui.display as d from gui.builtinViewColumns.state import State from eos.types import Slot from gui.contextMenu import ContextMenu +from service.fit import Fit class FighterViewDrop(wx.PyDropTarget): def __init__(self, dropFn): @@ -81,7 +81,7 @@ class FighterView(wx.Panel): self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() activeFitID = self.mainFrame.getActiveFit() fit = sFit.getFit(activeFitID) @@ -215,7 +215,7 @@ class FighterDisplay(d.Display): 'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones', 'Light Fighters', 'Heavy Fighters', 'Support Fighters') def droneKey(self, drone): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() groupName = sMkt.getMarketGroupByItem(drone.item).name print groupName @@ -224,7 +224,7 @@ class FighterDisplay(d.Display): ''' def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.Parent.DisablePage(self.Parent, not fit) @@ -259,7 +259,7 @@ class FighterDisplay(d.Display): def addItem(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() trigger = sFit.addFighter(fitID, event.itemID) if trigger: @@ -278,7 +278,7 @@ class FighterDisplay(d.Display): def removeFighter(self, fighter): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeFighter(fitID, self.original.index(fighter)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -289,7 +289,7 @@ class FighterDisplay(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fighter = self.fighters[row] sFit.toggleFighter(fitID, self.original.index(fighter)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -304,7 +304,7 @@ class FighterDisplay(d.Display): if sel != -1: fighter = self.fighters[sel] - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sourceContext = "fighterItem" itemContext = sMkt.getCategoryByItem(fighter.item).name menu = ContextMenu.getMenu((fighter,), (sourceContext, itemContext)) diff --git a/gui/gangView.py b/gui/gangView.py index 106f14f98..57f7cffa0 100644 --- a/gui/gangView.py +++ b/gui/gangView.py @@ -19,11 +19,16 @@ import wx from wx.lib.scrolledpanel import ScrolledPanel -import service import gui.mainFrame import gui.shipBrowser import gui.globalEvents as GE +from service.fit import Fit +from service.fleet import Fleet +from service.character import Character +from service.market import Market + + from gui import characterEditor as CharEditor class GangView ( ScrolledPanel ): @@ -157,14 +162,14 @@ class GangView ( ScrolledPanel ): event.Skip() return - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() - fleetSrv = service.Fleet.getInstance() + fleetSrv = Fleet.getInstance() activeFitID = self.mainFrame.getActiveFit() fit = sFit.getFit(activeFitID) - sChar = service.Character.getInstance() + sChar = Character.getInstance() charList = sChar.getCharacterList() if activeFitID: @@ -211,11 +216,11 @@ class GangView ( ScrolledPanel ): for id in self.fleet: if location == self.fleet[id]['stText']: type = id - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() boostee = sFit.getFit(activeFitID) booster = None - fleetSrv = service.Fleet.getInstance() + fleetSrv = Fleet.getInstance() if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) if type == 1: fleetSrv.setLinearWingCom(boostee, booster) @@ -231,7 +236,7 @@ class GangView ( ScrolledPanel ): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) def fitRenamed(self, event): - fleetSrv = service.Fleet.getInstance() + fleetSrv = Fleet.getInstance() activeFitID = self.mainFrame.getActiveFit() if activeFitID: @@ -241,10 +246,10 @@ class GangView ( ScrolledPanel ): def fitSelected(self, event): ''' Fires when active fit is selected and when booster is saved to fit. Update the UI to reflect changes ''' - fleetSrv = service.Fleet.getInstance() + fleetSrv = Fleet.getInstance() activeFitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID or activeFitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) @@ -291,12 +296,12 @@ class GangView ( ScrolledPanel ): activeFitID = self.mainFrame.getActiveFit() if activeFitID: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() boostee = sFit.getFit(activeFitID) booster = sFit.getFit(fitID) - fleetSrv = service.Fleet.getInstance() + fleetSrv = Fleet.getInstance() if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) if type == 1: fleetSrv.setLinearWingCom(boostee, booster) @@ -306,8 +311,8 @@ class GangView ( ScrolledPanel ): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) def RefreshBoosterFits(self, event = None): - sFit = service.Fit.getInstance() - sMkt = service.Market.getInstance() + sFit = Fit.getInstance() + sMkt = Market.getInstance() fitList = sFit.getBoosterFits() for id in self.fleet: @@ -336,7 +341,7 @@ class GangView ( ScrolledPanel ): choice.SetSelection(0) def RefreshCharacterList(self, event = None): - sChar = service.Character.getInstance() + sChar = Character.getInstance() charList = sChar.getCharacterList() for id in self.fleet: choice = self.fleet[id]['chChar'] @@ -365,7 +370,7 @@ class GangView ( ScrolledPanel ): #Those are drags coming from pyfa sources, NOT builtin wx drags self.draggedFitID = None if type == "fit": - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) if fit and not fit.isStructure: @@ -384,7 +389,7 @@ class GangView ( ScrolledPanel ): type = self.options.index(menuItem.GetText()) if self.draggedFitID: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() draggedFit = sFit.getFit(self.draggedFitID) self.AddCommander(draggedFit.ID, type) @@ -392,7 +397,7 @@ class GangView ( ScrolledPanel ): def OnFitChoiceSelected(self, event): ''' Fired when booster choice is selected ''' - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() # set type via choice box used chFit = event.GetEventObject() diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 2cedc84d1..fb3480b33 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -24,8 +24,8 @@ import gui.display import gui.globalEvents as GE from gui.graph import Graph -import service import gui.mainFrame +from service.fit import Fit enabled = True mplImported = False @@ -78,7 +78,7 @@ class GraphFrame(wx.Frame): self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.mainSizer) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) self.fits = [fit] if fit is not None else [] self.fitList = FitList(self) @@ -242,7 +242,7 @@ class GraphFrame(wx.Frame): self.draw() def AppendFitToList(self, fitID): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(fitID) if fit not in self.fits: self.fits.append(fit) diff --git a/gui/implantView.py b/gui/implantView.py index 15da4d097..85991cf24 100644 --- a/gui/implantView.py +++ b/gui/implantView.py @@ -18,7 +18,6 @@ #=============================================================================== import wx -import service import gui.display as d import gui.marketBrowser as mb import gui.mainFrame @@ -26,6 +25,7 @@ from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import globalEvents as GE from eos.types import ImplantLocation +from service.fit import Fit class ImplantView(wx.Panel): @@ -56,7 +56,7 @@ class ImplantView(wx.Panel): self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() activeFitID = self.mainFrame.getActiveFit() fit = sFit.getFit(activeFitID) if fit: @@ -70,7 +70,7 @@ class ImplantView(wx.Panel): def OnRadioSelect(self, event): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleImplantSource(fitID, ImplantLocation.FIT if self.rbFit.GetValue() else ImplantLocation.CHARACTER) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -107,7 +107,7 @@ class ImplantDisplay(d.Display): event.Skip() def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.Parent.DisablePage(self.Parent, not fit or fit.isStructure) @@ -137,7 +137,7 @@ class ImplantDisplay(d.Display): event.Skip() def addItem(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) @@ -161,7 +161,7 @@ class ImplantDisplay(d.Display): def removeImplant(self, implant): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeImplant(fitID, self.original.index(implant)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -172,7 +172,7 @@ class ImplantDisplay(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleImplant(fitID, row) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -185,7 +185,7 @@ class ImplantDisplay(d.Display): sel = self.GetFirstSelected() menu = None - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) if not fit: @@ -194,7 +194,7 @@ class ImplantDisplay(d.Display): if sel != -1: implant = fit.appliedImplants[sel] - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sourceContext = "implantItem" if fit.implantSource == ImplantLocation.FIT else "implantItemChar" itemContext = sMkt.getCategoryByItem(implant.item).name diff --git a/gui/itemStats.py b/gui/itemStats.py index 35fb48355..4942c67d6 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -26,11 +26,12 @@ import wx.lib.mixins.listctrl as listmix import wx.html from eos.types import Fit, Ship, Citadel, Module, Skill, Booster, Implant, Drone, Mode, Fighter from gui.utils.numberFormatter import formatAmount -import service import config from gui.contextMenu import ContextMenu from gui.utils.numberFormatter import formatAmount import csv +from service.market import Market +from service.attribute import Attribute try: from collections import OrderedDict @@ -72,7 +73,7 @@ class ItemStatsDialog(wx.Dialog): itmContext = None item = getattr(victim, "item", None) if srcContext.lower() not in ("projectedcharge", "fittingcharge") else getattr(victim, "charge", None) if item is None: - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() item = sMkt.getItem(victim.ID) victim = None self.context = itmContext @@ -152,7 +153,7 @@ class ItemStatsContainer ( wx.Panel ): def __init__( self, parent, stuff, item, context = None): wx.Panel.__init__ ( self, parent ) - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() mainSizer = wx.BoxSizer( wx.VERTICAL ) @@ -494,15 +495,15 @@ class ItemParams (wx.Panel): def TranslateValueUnit(self, value, unitName, unitDisplayName): def itemIDCallback(): - item = service.Market.getInstance().getItem(value) + item = Market.getInstance().getItem(value) return "%s (%d)" % (item.name, value) if item is not None else str(value) def groupIDCallback(): - group = service.Market.getInstance().getGroup(value) + group = Market.getInstance().getGroup(value) return "%s (%d)" % (group.name, value) if group is not None else str(value) def attributeIDCallback(): - attribute = service.Attribute.getInstance().getAttributeInfo(value) + attribute = Attribute.getInstance().getAttributeInfo(value) return "%s (%d)" % (attribute.name.capitalize(), value) trans = {"Inverse Absolute Percent": (lambda: (1-value)*100, unitName), @@ -664,7 +665,7 @@ class ItemCompare(wx.Panel): self.paramList.InsertColumn(len(self.attrs)+1, "Price") self.paramList.SetColumnWidth(len(self.attrs)+1, 60) - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sMkt.getPrices([x.ID for x in self.items], self.processPrices) for item in self.items: @@ -687,15 +688,15 @@ class ItemCompare(wx.Panel): def TranslateValueUnit(self, value, unitName, unitDisplayName): def itemIDCallback(): - item = service.Market.getInstance().getItem(value) + item = Market.getInstance().getItem(value) return "%s (%d)" % (item.name, value) if item is not None else str(value) def groupIDCallback(): - group = service.Market.getInstance().getGroup(value) + group = Market.getInstance().getGroup(value) return "%s (%d)" % (group.name, value) if group is not None else str(value) def attributeIDCallback(): - attribute = service.Attribute.getInstance().getAttributeInfo(value) + attribute = Attribute.getInstance().getAttributeInfo(value) return "%s (%d)" % (attribute.name.capitalize(), value) trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName), diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 49fb9385a..480d50681 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -29,7 +29,6 @@ from codecs import open from wx._core import PyDeadObjectError from wx.lib.wordwrap import wordwrap -import service import config import threading import webbrowser @@ -58,6 +57,13 @@ from gui.utils.clipboard import toClipboard, fromClipboard from gui.fleetBrowser import FleetBrowser from gui.updateDialog import UpdateDialog from gui.builtinViews import * +from gui import graphFrame + +from service.settings import SettingsProvider +from service.fit import Fit +from service.character import Character +from service.crest import Crest +from service.update import Update # import this to access override setting from eos.modifiedAttributeDict import ModifiedAttributeDict @@ -208,7 +214,7 @@ class MainFrame(wx.Frame): self.LoadPreviousOpenFits() #Check for updates - self.sUpdate = service.Update.getInstance() + self.sUpdate = Update.getInstance() self.sUpdate.CheckUpdate(self.ShowUpdateBox) if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): @@ -223,9 +229,9 @@ class MainFrame(wx.Frame): dlg.ShowModal() def LoadPreviousOpenFits(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() - self.prevOpenFits = service.SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) + self.prevOpenFits = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) fits = self.prevOpenFits['pyfaOpenFits'] # Remove any fits that cause exception when fetching (non-existent fits) @@ -245,7 +251,7 @@ class MainFrame(wx.Frame): def LoadMainFrameAttribs(self): mainFrameDefaultAttribs = {"wnd_width": 1000, "wnd_height": 700, "wnd_maximized": False, "browser_width": 300, "market_height": 0, "fitting_height": -200} - self.mainFrameAttribs = service.SettingsProvider.getInstance().getSettings("pyfaMainWindowAttribs", mainFrameDefaultAttribs) + self.mainFrameAttribs = SettingsProvider.getInstance().getSettings("pyfaMainWindowAttribs", mainFrameDefaultAttribs) if self.mainFrameAttribs["wnd_maximized"]: width = mainFrameDefaultAttribs["wnd_width"] @@ -319,7 +325,7 @@ class MainFrame(wx.Frame): self.prevOpenFits['pyfaOpenFits'].append(m()) # save all teh settingz - service.SettingsProvider.getInstance().saveAll() + SettingsProvider.getInstance().saveAll() event.Skip() def ExitApp(self, event): @@ -372,7 +378,7 @@ class MainFrame(wx.Frame): def showExportDialog(self, event): """ Export active fit """ - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.getActiveFit()) defaultFile = "%s - %s.xml"%(fit.ship.item.name, fit.name) if fit else None @@ -540,7 +546,7 @@ class MainFrame(wx.Frame): dlg.Show() def updateTitle(self, event): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() char = sCrest.implicitCharacter if char: t = time.gmtime(char.eve.expires-time.time()) @@ -575,7 +581,7 @@ class MainFrame(wx.Frame): self.SetTitle(self.title) menu = self.GetMenuBar() - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() if type == CrestModes.IMPLICIT: menu.SetLabel(menu.ssoLoginId, "Login to EVE") @@ -588,7 +594,7 @@ class MainFrame(wx.Frame): menu.Enable(menu.exportToEveId, not enable) def ssoHandler(self, event): - sCrest = service.Crest.getInstance() + sCrest = Crest.getInstance() if sCrest.settings.get('mode') == CrestModes.IMPLICIT: if sCrest.implicitCharacter is not None: sCrest.logout() @@ -610,7 +616,7 @@ class MainFrame(wx.Frame): menu.SetLabel(menu.toggleOverridesId, "Turn Overrides Off" if ModifiedAttributeDict.OVERRIDES else "Turn Overrides On") def saveChar(self, event): - sChr = service.Character.getInstance() + sChr = Character.getInstance() charID = self.charSelection.getActiveCharacter() sChr.saveCharacter(charID) wx.PostEvent(self, GE.CharListUpdated()) @@ -621,7 +627,7 @@ class MainFrame(wx.Frame): dlg.ShowModal() def revertChar(self, event): - sChr = service.Character.getInstance() + sChr = Character.getInstance() charID = self.charSelection.getActiveCharacter() sChr.revertCharacter(charID) wx.PostEvent(self, GE.CharListUpdated()) @@ -659,31 +665,31 @@ class MainFrame(wx.Frame): self.marketBrowser.search.Focus() def clipboardEft(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() toClipboard(sFit.exportFit(self.getActiveFit())) def clipboardEftImps(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() toClipboard(sFit.exportEftImps(self.getActiveFit())) def clipboardDna(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() toClipboard(sFit.exportDna(self.getActiveFit())) def clipboardCrest(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() toClipboard(sFit.exportCrest(self.getActiveFit())) def clipboardXml(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() toClipboard(sFit.exportXml(None, self.getActiveFit())) def clipboardMultiBuy(self): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() toClipboard(sFit.exportMultiBuy(self.getActiveFit())) def importFromClipboard(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() try: fits = sFit.importFitFromBuffer(fromClipboard(), self.getActiveFit()) except: @@ -709,7 +715,7 @@ class MainFrame(wx.Frame): def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ - sCharacter = service.Character.getInstance() + sCharacter = Character.getInstance() saveDialog = wx.FileDialog(self, "Export Skills Needed As...", wildcard = "EVEMon skills training file (*.emp)|*.emp|" \ "EVEMon skills training XML file (*.xml)|*.xml|" \ @@ -737,7 +743,7 @@ class MainFrame(wx.Frame): def fileImportDialog(self, event): """Handles importing single/multiple EVE XML / EFT cfg fit files""" - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() dlg = wx.FileDialog(self, "Open One Or More Fitting Files", wildcard = "EVE XML fitting files (*.xml)|*.xml|" \ "EFT text fitting files (*.cfg)|*.cfg|" \ @@ -767,7 +773,7 @@ class MainFrame(wx.Frame): if '.' not in os.path.basename(filePath): filePath += ".xml" - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() max = sFit.countAllFits() self.progressDialog = wx.ProgressDialog("Backup fits", @@ -779,7 +785,7 @@ class MainFrame(wx.Frame): def exportHtml(self, event): from gui.utils.exportHtml import exportHtml - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() settings = service.settings.HTMLExportSettings.getInstance() max = sFit.countAllFits() @@ -865,7 +871,7 @@ class MainFrame(wx.Frame): if dlg.ShowModal() == wx.ID_OK: self.waitDialog = wx.BusyInfo("Importing Character...") - sCharacter = service.Character.getInstance() + sCharacter = Character.getInstance() sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback) def importCharacterCallback(self): @@ -878,7 +884,8 @@ class MainFrame(wx.Frame): def openGraphFrame(self, event): if not self.graphFrame: self.graphFrame = GraphFrame(self) - if gui.graphFrame.enabled: + + if graphFrame.enabled: self.graphFrame.Show() else: self.graphFrame.SetFocus() diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 287866909..88cc6de2e 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -23,7 +23,8 @@ from gui.bitmapLoader import BitmapLoader import gui.mainFrame import gui.graphFrame import gui.globalEvents as GE -import service +from service.crest import Crest +from service.character import Character if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): from service.crest import CrestModes @@ -120,7 +121,7 @@ class MainMenuBar(wx.MenuBar): windowMenu.AppendItem(preferencesItem) if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): - self.sCrest = service.Crest.getInstance() + self.sCrest = Crest.getInstance() # CREST Menu crestMenu = wx.Menu() @@ -164,7 +165,7 @@ class MainMenuBar(wx.MenuBar): self.Enable(wx.ID_COPY, enable) self.Enable(self.exportSkillsNeededId, enable) - sChar = service.Character.getInstance() + sChar = Character.getInstance() charID = self.mainFrame.charSelection.getActiveCharacter() char = sChar.getCharacter(charID) diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index 14dea666c..a06b89d39 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -18,11 +18,12 @@ #=============================================================================== import wx -import service import gui.display as d from gui.cachingImageList import CachingImageList from gui.contextMenu import ContextMenu import gui.PFSearchBox as SBox +from service.market import Market +from service.attribute import Attribute from gui.bitmapLoader import BitmapLoader @@ -45,7 +46,7 @@ class MarketBrowser(wx.Panel): vbox.Add(self.splitter, 1, wx.EXPAND) # Grab market service instance and create child objects - self.sMkt = service.Market.getInstance() + self.sMkt = Market.getInstance() self.searchMode = False self.marketView = MarketTree(self.splitter, self) self.itemView = ItemView(self.splitter, self) @@ -416,7 +417,7 @@ class ItemView(d.Display): def populate(self, items): if len(items) > 0: # Get dictionary with meta level attribute - sAttr = service.Attribute.getInstance() + sAttr = Attribute.getInstance() attrs = sAttr.getAttributeInfo("metaLevel") sMkt = self.sMkt self.metalvls = sMkt.directAttrRequest(items, attrs) @@ -432,7 +433,7 @@ class ItemView(d.Display): def refresh(self, items): if len(items) > 1: # Get dictionary with meta level attribute - sAttr = service.Attribute.getInstance() + sAttr = Attribute.getInstance() attrs = sAttr.getAttributeInfo("metaLevel") sMkt = self.sMkt self.metalvls = sMkt.directAttrRequest(items, attrs) diff --git a/gui/notesView.py b/gui/notesView.py index efa933bb9..c8f648440 100644 --- a/gui/notesView.py +++ b/gui/notesView.py @@ -1,8 +1,10 @@ import wx -import service + import gui.globalEvents as GE import gui.mainFrame +from service.fit import Fit + class NotesView(wx.Panel): def __init__(self, parent): @@ -19,7 +21,7 @@ class NotesView(wx.Panel): self.Bind(wx.EVT_TIMER, self.delayedSave, self.saveTimer) def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) @@ -38,7 +40,7 @@ class NotesView(wx.Panel): self.saveTimer.Start(1000, True) def delayedSave(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.lastFitId) newNotes = self.editNotes.GetValue() fit.notes = newNotes diff --git a/gui/patternEditor.py b/gui/patternEditor.py index 171eed7a5..cbfb11207 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -19,11 +19,11 @@ import wx from gui.bitmapLoader import BitmapLoader -import service from wx.lib.intctrl import IntCtrl from gui.utils.clipboard import toClipboard, fromClipboard from service.damagePattern import ImportError from gui.builtinViews.entityEditor import EntityEditor, BaseValidator +from service.damagePattern import DamagePattern, ImportError ########################################################################### ## Class DmgPatternEditorDlg ########################################################################### @@ -59,26 +59,26 @@ class DmgPatternEntityEditor(EntityEditor): self.SetEditorValidator(DmgPatternTextValidor) def getEntitiesFromContext(self): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() choices = sorted(sDP.getDamagePatternList(), key=lambda p: p.name) return [c for c in choices if c.name != "Selected Ammo"] def DoNew(self, name): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() return sDP.newPattern(name) def DoRename(self, entity, name): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() sDP.renamePattern(entity, name) def DoCopy(self, entity, name): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() copy = sDP.copyPattern(entity) sDP.renamePattern(copy, name) return copy def DoDelete(self, entity): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() sDP.deletePattern(entity) class DmgPatternEditorDlg(wx.Dialog): @@ -208,7 +208,7 @@ class DmgPatternEditorDlg(wx.Dialog): if event is not None: event.Skip() - service.DamagePattern.getInstance().saveChanges(p) + DamagePattern.getInstance().saveChanges(p) def restrict(self): for type in self.DAMAGE_TYPES: @@ -251,11 +251,11 @@ class DmgPatternEditorDlg(wx.Dialog): def importPatterns(self, event): text = fromClipboard() if text: - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() try: sDP.importPatterns(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") - except service.damagePattern.ImportError, e: + except ImportError, e: self.stNotice.SetLabel(str(e)) except Exception, e: self.stNotice.SetLabel("Could not import from clipboard: unknown errors") @@ -265,6 +265,6 @@ class DmgPatternEditorDlg(wx.Dialog): self.stNotice.SetLabel("Could not import from clipboard") def exportPatterns(self, event): - sDP = service.DamagePattern.getInstance() + sDP = DamagePattern.getInstance() toClipboard( sDP.exportPatterns() ) self.stNotice.SetLabel("Patterns exported to clipboard") diff --git a/gui/projectedView.py b/gui/projectedView.py index f805ead1c..cccf301af 100644 --- a/gui/projectedView.py +++ b/gui/projectedView.py @@ -20,11 +20,11 @@ import wx import gui.display as d import gui.globalEvents as GE -import service import gui.droneView from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import eos.types +from service.fit import Fit class DummyItem: @@ -91,7 +91,7 @@ class ProjectedView(d.Display): # if source is coming from projected, we are trying to combine drones. self.mergeDrones(x, y, int(data[1])) elif data[0] == "market": - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.project(fitID, int(data[1])) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) @@ -100,7 +100,7 @@ class ProjectedView(d.Display): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() row = self.GetFirstSelected() if row != -1: sFit.removeProjected(fitID, self.get(row)) @@ -111,7 +111,7 @@ class ProjectedView(d.Display): if type == "fit": activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() draggedFit = sFit.getFit(fitID) sFit.project(activeFit, draggedFit) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) @@ -135,7 +135,7 @@ class ProjectedView(d.Display): def _merge(self, src, dst): dstDrone = self.get(dst) if isinstance(dstDrone, eos.types.Drone): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() if sFit.mergeDrones(fitID, self.get(src), dstDrone, True): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -159,7 +159,7 @@ class ProjectedView(d.Display): return fit.name def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) @@ -231,7 +231,7 @@ class ProjectedView(d.Display): col = self.getColumn(event.Position) if col == self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleProjected(fitID, item, "right" if event.Button == 3 else "left") wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) @@ -246,7 +246,7 @@ class ProjectedView(d.Display): if sel != -1: item = self.get(sel) if item is None: return - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() if isinstance(item, eos.types.Drone): srcContext = "projectedDrone" itemContext = sMkt.getCategoryByItem(item.item).name @@ -287,6 +287,6 @@ class ProjectedView(d.Display): col = self.getColumn(event.Position) if col != self.getColIndex(State): fitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.removeProjected(fitID, self.get(row)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index 48c2689cd..018edb579 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -13,12 +13,13 @@ from gui.marketBrowser import SearchBox import gui.display as d import gui.globalEvents as GE from gui.bitmapLoader import BitmapLoader -import service import csv import eos.db import logging +from service.market import Market + logger = logging.getLogger(__name__) class AttributeEditor( wx.Frame ): @@ -108,7 +109,7 @@ class AttributeEditor( wx.Frame ): self.itemView.updateItems(True) def OnExport(self, event): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() items = sMkt.getItemsWithOverrides() defaultFile = "pyfa_overrides.csv" @@ -131,7 +132,7 @@ class AttributeEditor( wx.Frame ): "Confirm Delete", wx.YES | wx.NO | wx.ICON_EXCLAMATION) if dlg.ShowModal() == wx.ID_YES: - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() items = sMkt.getItemsWithOverrides() # We can't just delete overrides, as loaded items will still have # them assigned. Deleting them from the database won't propagate @@ -152,7 +153,7 @@ class ItemView(d.Display): def __init__(self, parent): d.Display.__init__(self, parent) - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() self.things = sMkt.getItemsWithOverrides() self.items = self.things @@ -173,14 +174,14 @@ class ItemView(d.Display): self.update(self.items) def updateItems(self, updateDisplay=False): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() self.things = sMkt.getItemsWithOverrides() self.items = self.things if updateDisplay: self.update(self.things) def scheduleSearch(self, event=None): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() search = self.searchBox.GetLineText(0) # Make sure we do not count wildcard as search symbol diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index 37d04b032..f28d370b1 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -19,10 +19,10 @@ import wx from gui.bitmapLoader import BitmapLoader -import service from gui.utils.clipboard import toClipboard, fromClipboard from service.targetResists import ImportError from gui.builtinViews.entityEditor import EntityEditor, BaseValidator +from service.targetResists import TargetResists class TargetResistsTextValidor(BaseValidator): @@ -56,26 +56,26 @@ class TargetResistsEntityEditor(EntityEditor): self.SetEditorValidator(TargetResistsTextValidor) def getEntitiesFromContext(self): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() choices = sorted(sTR.getTargetResistsList(), key=lambda p: p.name) return choices def DoNew(self, name): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() return sTR.newPattern(name) def DoRename(self, entity, name): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() sTR.renamePattern(entity, name) def DoCopy(self, entity, name): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() copy = sTR.copyPattern(entity) sTR.renamePattern(copy, name) return copy def DoDelete(self, entity): - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() sTR.deletePattern(entity) class ResistsEditorDlg(wx.Dialog): @@ -224,7 +224,7 @@ class ResistsEditorDlg(wx.Dialog): if event is not None: event.Skip() - service.TargetResists.getInstance().saveChanges(p) + TargetResists.getInstance().saveChanges(p) except ValueError: editObj.SetForegroundColour(wx.RED) @@ -264,7 +264,7 @@ class ResistsEditorDlg(wx.Dialog): text = fromClipboard() if text: - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() try: sTR.importPatterns(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") @@ -279,6 +279,6 @@ class ResistsEditorDlg(wx.Dialog): def exportPatterns(self, event): "Event fired when export to clipboard button is clicked" - sTR = service.TargetResists.getInstance() + sTR = TargetResists.getInstance() toClipboard( sTR.exportPatterns() ) self.stNotice.SetLabel("Patterns exported to clipboard") diff --git a/gui/setEditor.py b/gui/setEditor.py index 84090f6ff..5ddac5f4c 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -20,11 +20,11 @@ import wx from gui.bitmapLoader import BitmapLoader from gui.builtinViews.implantEditor import BaseImplantEditorView -import service from gui.utils.clipboard import toClipboard, fromClipboard from service.implantSet import ImportError import logging from gui.builtinViews.entityEditor import EntityEditor, BaseValidator +from service.implantSet import ImplantSets logger = logging.getLogger(__name__) @@ -59,25 +59,25 @@ class ImplantSetEntityEditor(EntityEditor): self.SetEditorValidator(ImplantTextValidor) def getEntitiesFromContext(self): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() return sorted(sIS.getImplantSetList(), key=lambda c: c.name) def DoNew(self, name): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() return sIS.newSet(name) def DoRename(self, entity, name): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() sIS.renameSet(entity, name) def DoCopy(self, entity, name): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() copy = sIS.copySet(entity) sIS.renameSet(copy, name) return copy def DoDelete(self, entity): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() sIS.deleteSet(entity) @@ -91,20 +91,20 @@ class ImplantSetEditor(BaseImplantEditorView): self.Parent.entityEditor.Bind(wx.EVT_CHOICE, self.contextChanged) def getImplantsFromContext(self): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() set = self.Parent.entityEditor.getActiveEntity() if set: return sIS.getImplants(set.ID) return [] def addImplantToContext(self, item): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() set = self.Parent.entityEditor.getActiveEntity() sIS.addImplant(set.ID, item.ID) def removeImplantFromContext(self, implant): - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() set = self.Parent.entityEditor.getActiveEntity() sIS.removeImplant(set.ID, implant) @@ -191,7 +191,7 @@ class ImplantSetEditorDlg(wx.Dialog): text = fromClipboard() if text: - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() try: sIS.importSets(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") @@ -209,6 +209,6 @@ class ImplantSetEditorDlg(wx.Dialog): def exportPatterns(self, event): "Event fired when export to clipboard button is clicked" - sIS = service.ImplantSets.getInstance() + sIS = ImplantSets.getInstance() toClipboard(sIS.exportSets()) self.stNotice.SetLabel("Sets exported to clipboard") diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index e1271b616..95e84b86a 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -19,7 +19,8 @@ import gui.sfBrowserItem as SFItem from gui.contextMenu import ContextMenu import gui.utils.fonts as fonts -import service +from service.fit import Fit +from service.market import Market import gui.utils.fonts as fonts FitRenamed, EVT_FIT_RENAMED = wx.lib.newevent.NewEvent() @@ -437,7 +438,7 @@ class NavigationPanel(SFItem.SFBrowserItem): if stage == 3: shipID = self.Parent.GetStageData(stage) shipName = self.Parent.GetStage3ShipName() - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = sFit.newFit(shipID, "%s fit" %shipName) self.shipBrowser.fitIDMustEditName = fitID wx.PostEvent(self.Parent,Stage3Selected(shipID=shipID)) @@ -671,8 +672,8 @@ class ShipBrowser(wx.Panel): self.navpanel.ShowNewFitButton(False) self.navpanel.ShowSwitchEmptyGroupsButton(False) - sMkt = service.Market.getInstance() - sFit = service.Fit.getInstance() + sMkt = Market.getInstance() + sFit = Fit.getInstance() self.lpane.ShowLoading(False) self.lpane.Freeze() @@ -718,7 +719,7 @@ class ShipBrowser(wx.Panel): categoryID = self._stage2Data ships = list(data[1]) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() ships.sort(key=self.raceNameKey) racesList = [] @@ -789,7 +790,7 @@ class ShipBrowser(wx.Panel): self.lpane.RemoveAllChildren() - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() sMkt.getShipListDelayed(categoryID, self.stage2Callback) self._stage2Data = categoryID @@ -819,8 +820,8 @@ class ShipBrowser(wx.Panel): self._lastStage = self._activeStage self._activeStage = 3 - sFit = service.Fit.getInstance() - sMkt = service.Market.getInstance() + sFit = Fit.getInstance() + sMkt = Market.getInstance() ship = sMkt.getItem(shipID) categoryID = ship.group.ID @@ -875,8 +876,8 @@ class ShipBrowser(wx.Panel): self._lastStage = self._activeStage self._activeStage = 4 - sMkt = service.Market.getInstance() - sFit = service.Fit.getInstance() + sMkt = Market.getInstance() + sFit = Fit.getInstance() query = event.text self.lpane.Freeze() @@ -1162,7 +1163,7 @@ class ShipItem(SFItem.SFBrowserItem): self.Bind(wx.EVT_CONTEXT_MENU, self.OnShowPopup) - self.marketInstance = service.Market.getInstance() + self.marketInstance = Market.getInstance() self.baseItem = self.marketInstance.getItem(self.shipID) #=======================================================================\ @@ -1249,7 +1250,7 @@ class ShipItem(SFItem.SFBrowserItem): def createNewFit(self, event=None): self.tcFitName.Show(False) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = sFit.newFit(self.shipID, self.tcFitName.GetValue()) wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID, back=False)) @@ -1538,7 +1539,7 @@ class FitItem(SFItem.SFBrowserItem): self.Bind(wx.EVT_RIGHT_UP, self.OnContextMenu) def OnToggleBooster(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() sFit.toggleBoostFit(self.fitID) self.fitBooster = not self.fitBooster self.boosterBtn.Show(self.fitBooster) @@ -1549,7 +1550,7 @@ class FitItem(SFItem.SFBrowserItem): def OnProjectToFit(self, event): activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() projectedFit = sFit.getFit(self.fitID) sFit.project(activeFit, projectedFit) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) @@ -1558,7 +1559,7 @@ class FitItem(SFItem.SFBrowserItem): def OnAddCommandFit(self, event): activeFit = self.mainFrame.getActiveFit() if activeFit: - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() commandFit = sFit.getFit(self.fitID) sFit.addCommandFit(activeFit, commandFit) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFit)) @@ -1576,7 +1577,7 @@ class FitItem(SFItem.SFBrowserItem): def OnContextMenu(self, event): ''' Handles context menu for fit. Dragging is handled by MouseLeftUp() ''' - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) if not fit: @@ -1666,7 +1667,7 @@ class FitItem(SFItem.SFBrowserItem): self.copyFit() def copyFit(self, event=None): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fitID = sFit.copyFit(self.fitID) self.shipBrowser.fitIDMustEditName = fitID wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID)) @@ -1686,7 +1687,7 @@ class FitItem(SFItem.SFBrowserItem): self.Refresh() def renameFit(self, event=None): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() self.tcFitName.Show(False) self.editWasShown = 0 fitName = self.tcFitName.GetValue() @@ -1720,7 +1721,7 @@ class FitItem(SFItem.SFBrowserItem): else: self.deleted = True - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(self.fitID) sFit.deleteFit(self.fitID) diff --git a/gui/statsPane.py b/gui/statsPane.py index 6c378878c..0b0b27413 100644 --- a/gui/statsPane.py +++ b/gui/statsPane.py @@ -19,7 +19,7 @@ import wx from gui.statsView import StatsView -import service +from service.fit import Fit from gui.pyfatogglepanel import TogglePanel import gui.builtinStatsViews from gui.contextMenu import ContextMenu @@ -33,7 +33,7 @@ class StatsPane(wx.Panel): "priceViewFull",] def fitChanged(self, event): - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) for view in self.views: view.refreshPanel(fit) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 0febdf3f8..21ccb0197 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -20,7 +20,6 @@ import wx from gui.bitmapLoader import BitmapLoader import config -import service import dateutil.parser class UpdateDialog(wx.Dialog): diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index 9fc20eff6..432dae740 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -1,7 +1,9 @@ import threading import time -import service import wx +from service.settings import HTMLExportSettings +from service.fit import Fit +from service.market import Market class exportHtml(): _instance = None @@ -16,7 +18,7 @@ class exportHtml(): self.thread = exportHtmlThread() def refreshFittingHtml(self, force=False, callback=False): - settings = service.settings.HTMLExportSettings.getInstance() + settings = HTMLExportSettings.getInstance() if force or settings.getEnabled(): self.thread.stop() @@ -39,9 +41,9 @@ class exportHtmlThread(threading.Thread): if self.stopRunning: return - sMkt = service.Market.getInstance() - sFit = service.Fit.getInstance() - settings = service.settings.HTMLExportSettings.getInstance() + sMkt = Market.getInstance() + sFit = Fit.getInstance() + settings = HTMLExportSettings.getInstance() timestamp = time.localtime(time.time()) localDate = "%d/%02d/%02d %02d:%02d" % (timestamp[0], timestamp[1], timestamp[2], timestamp[3], timestamp[4]) diff --git a/service/__init__.py b/service/__init__.py index b19c6db17..e69de29bb 100644 --- a/service/__init__.py +++ b/service/__init__.py @@ -1,19 +0,0 @@ -from service.market import Market -from service.fit import Fit -from service.attribute import Attribute -from service.character import Character -from service.damagePattern import DamagePattern -from service.targetResists import TargetResists -from service.settings import SettingsProvider -from service.fleet import Fleet -from service.update import Update -from service.price import Price -from service.network import Network -from service.eveapi import EVEAPIConnection, ParseXML -from service.implantSet import ImplantSets - -import wx -if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): - from service.pycrest import EVE - from service.server import StoppableHTTPServer, AuthHandler - from service.crest import Crest diff --git a/service/character.py b/service/character.py index 802d11092..c6d3ca98e 100644 --- a/service/character.py +++ b/service/character.py @@ -20,6 +20,7 @@ import copy import itertools import json +import logging import threading from codecs import open from xml.etree import ElementTree @@ -28,11 +29,10 @@ import gzip import wx +import config import eos.db import eos.types -import service -import config -import logging +from service.eveapi import EVEAPIConnection, ParseXML logger = logging.getLogger(__name__) @@ -49,7 +49,7 @@ class CharacterImportThread(threading.Thread): try: # we try to parse api XML data first with open(path, mode='r') as charFile: - sheet = service.ParseXML(charFile) + sheet = ParseXML(charFile) char = sCharacter.new(sheet.name+" (imported)") sCharacter.apiUpdateCharSheet(char.ID, sheet.skills) except: @@ -87,8 +87,6 @@ class SkillBackupThread(threading.Thread): def run(self): path = self.path sCharacter = Character.getInstance() - sFit = service.Fit.getInstance() - fit = sFit.getFit(self.activeFit) backupData = "" if self.saveFmt == "xml" or self.saveFmt == "emp": backupData = sCharacter.exportXml() @@ -284,7 +282,7 @@ class Character(object): char.apiID = userID char.apiKey = apiKey - api = service.EVEAPIConnection() + api = EVEAPIConnection() auth = api.auth(keyID=userID, vCode=apiKey) apiResult = auth.account.Characters() charList = map(lambda c: unicode(c.name), apiResult.characters) @@ -296,7 +294,7 @@ class Character(object): dbChar = eos.db.getCharacter(charID) dbChar.defaultChar = charName - api = service.EVEAPIConnection() + api = EVEAPIConnection() auth = api.auth(keyID=dbChar.apiID, vCode=dbChar.apiKey) apiResult = auth.account.Characters() charID = None diff --git a/service/conversions/__init__.py b/service/conversions/__init__.py index c9ee794c3..d8764af8b 100644 --- a/service/conversions/__init__.py +++ b/service/conversions/__init__.py @@ -8,6 +8,7 @@ elsewhere (in which case can be accessed with packs[name]) """ import pkgutil +import importlib # init parent dict all = {} @@ -15,10 +16,9 @@ all = {} # init container to store the separate conversion packs in case we need them packs = {} - prefix = __name__ + "." for importer, modname, ispkg in pkgutil.iter_modules(__path__, prefix): - conversionPack = __import__(modname, fromlist="dummy") + conversionPack = importlib.import_module(modname) all.update(conversionPack.CONVERSIONS) modname_tail = modname.rsplit('.', 1)[-1] packs[modname_tail] = conversionPack.CONVERSIONS diff --git a/service/crest.py b/service/crest.py index b2771242b..edb6af5c5 100644 --- a/service/crest.py +++ b/service/crest.py @@ -9,10 +9,10 @@ import time import eos.db from eos.enum import Enum from eos.types import CrestChar - -import service - import gui.globalEvents as GE +from service.settings import CRESTSettings +from service.server import StoppableHTTPServer, AuthHandler +from service.pycrest.eve import EVE logger = logging.getLogger(__name__) @@ -64,7 +64,7 @@ class Crest(): characters still in the cache (if USER mode) """ - self.settings = service.settings.CRESTSettings.getInstance() + self.settings = CRESTSettings.getInstance() self.scopes = ['characterFittingsRead', 'characterFittingsWrite'] # these will be set when needed @@ -73,7 +73,7 @@ class Crest(): self.ssoTimer = None # Base EVE connection that is copied to all characters - self.eve = service.pycrest.EVE( + self.eve = EVE( client_id=self.settings.get('clientID') if self.settings.get('mode') == CrestModes.USER else self.clientIDs.get(self.settings.get('server')), api_key=self.settings.get('clientSecret') if self.settings.get('mode') == CrestModes.USER else None, redirect_uri=self.clientCallback, @@ -161,7 +161,7 @@ class Crest(): if self.httpd: self.stopServer() time.sleep(1) # we need this to ensure that the previous get_request finishes, and then the socket will close - self.httpd = service.StoppableHTTPServer(('', 6461), service.AuthHandler) + self.httpd = StoppableHTTPServer(('', 6461), AuthHandler) thread.start_new_thread(self.httpd.serve, (self.handleLogin,)) self.state = str(uuid.uuid4()) diff --git a/service/damagePattern.py b/service/damagePattern.py index f617fa846..226bbf47e 100644 --- a/service/damagePattern.py +++ b/service/damagePattern.py @@ -17,15 +17,14 @@ # along with pyfa. If not, see . #=============================================================================== -import eos.db -import eos.types import copy -from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues +import eos.db +import eos.types + class ImportError(Exception): - pass - + pass class DamagePattern(): instance = None @classmethod diff --git a/service/eveapi.py b/service/eveapi.py index 4b19340b0..66c31f625 100644 --- a/service/eveapi.py +++ b/service/eveapi.py @@ -166,7 +166,7 @@ from xml.parsers import expat from time import strptime from calendar import timegm -import service +from service.network import Network proxy = None proxySSL = False @@ -395,7 +395,7 @@ class _RootContext(_Context): response = None if response is None: - network = service.Network.getInstance() + network = Network.getInstance() req = self._scheme+'://'+self._host+path @@ -414,7 +414,7 @@ class _RootContext(_Context): # implementor is handling fallbacks... try: return _ParseXML(response, True, store and (lambda obj: cache.store(self._host, path, kw, response, obj))) - except Error, e: + except Error as e: response = retrieve_fallback(self._host, path, kw, reason=e) if response is not None: return response diff --git a/service/fit.py b/service/fit.py index b2bb691ff..16e52ab82 100644 --- a/service/fit.py +++ b/service/fit.py @@ -27,16 +27,18 @@ from codecs import open import xml.parsers.expat import eos.db -import eos.types - -from eos.types import State, Slot +from eos.types import State, Slot, Module, Drone, Fighter +from eos.types import Fit as FitType from service.market import Market from service.damagePattern import DamagePattern from service.character import Character +from eos.saveddata.character import Character as saveddata_Character from service.fleet import Fleet from service.settings import SettingsProvider -from service.port import Port + +# TODO: port this to port.py +#from service.port import Port logger = logging.getLogger(__name__) @@ -90,7 +92,7 @@ class Fit(object): def __init__(self): self.pattern = DamagePattern.getInstance().getDamagePattern("Uniform") self.targetResists = None - self.character = Character.getInstance().all5() + self.character = saveddata_Character.getAll5() self.booster = False self.dirtyFitIDs = set() @@ -107,7 +109,7 @@ class Fit(object): "showMarketShortcuts": False, "enableGaugeAnimation": True, "exportCharges": True, - "openFitInNew":False + "openFitInNew": False } self.serviceFittingOptions = SettingsProvider.getInstance().getSettings( @@ -164,7 +166,7 @@ class Fit(object): ship = eos.types.Ship(eos.db.getItem(shipID)) except ValueError: ship = eos.types.Citadel(eos.db.getItem(shipID)) - fit = eos.types.Fit(ship) + fit = FitType(ship) fit.name = name if name is not None else "New %s" % fit.ship.item.name fit.damagePattern = self.pattern fit.targetResists = self.targetResists @@ -343,7 +345,7 @@ class Fit(object): thing = eos.db.getItem(thing, eager=("attributes", "group.category")) - if isinstance(thing, eos.types.Fit): + if isinstance(thing, FitType): if thing in fit.projectedFits: return @@ -414,7 +416,7 @@ class Fit(object): thing.state = self.__getProposedState(thing, click) if not thing.canHaveState(thing.state, fit): thing.state = State.OFFLINE - elif isinstance(thing, eos.types.Fit): + elif isinstance(thing, FitType): projectionInfo = thing.getProjectionInfo(fitID) if projectionInfo: projectionInfo.active = not projectionInfo.active @@ -559,7 +561,7 @@ class Fit(object): # Gather modules and convert Cargo item to Module, silently return if not a module try: - cargoP = eos.types.Module(cargo.item) + cargoP = Module(cargo.item) cargoP.owner = fit if cargoP.isValidState(State.ACTIVE): cargoP.state = State.ACTIVE @@ -692,10 +694,10 @@ class Fit(object): break ''' if fighter is None: - fighter = eos.types.Fighter(item) + fighter = Fighter(item) used = fit.getSlotsUsed(fighter.slot) total = fit.getNumSlots(fighter.slot) - standardAttackActive = False; + standardAttackActive = False for ability in fighter.abilities: if (ability.effect.isImplemented and ability.effect.handlerName == u'fighterabilityattackm'): # Activate "standard attack" if available @@ -704,7 +706,7 @@ class Fit(object): else: # Activate all other abilities (Neut, Web, etc) except propmods if no standard attack is active if (ability.effect.isImplemented - and standardAttackActive == False + and standardAttackActive is False and ability.effect.handlerName != u'fighterabilitymicrowarpdrive' and ability.effect.handlerName != u'fighterabilityevasivemaneuvers'): ability.active = True @@ -783,7 +785,7 @@ class Fit(object): d.amount = amount d.amountActive = amount if active else 0 - newD = eos.types.Drone(d.item) + newD = Drone(d.item) newD.amount = total - amount newD.amountActive = newD.amount if active else 0 l.append(newD) @@ -958,6 +960,8 @@ class Fit(object): fit.damagePattern = dp self.recalc(fit) + # TODO: port this to port.py + ''' def exportFit(self, fitID): fit = eos.db.getFit(fitID) return Port.exportEft(fit) @@ -981,6 +985,7 @@ class Fit(object): def exportMultiBuy(self, fitID): fit = eos.db.getFit(fitID) return Port.exportMultiBuy(fit) + ''' def backupFits(self, path, callback): thread = FitBackupThread(path, callback) @@ -1056,14 +1061,17 @@ class Fit(object): if codec_found is None: return False, "Proper codec could not be established for %s" % path + # TODO: port this to port.py + ''' try: _, fitsImport = Port.importAuto(srcString, path, callback=callback, encoding=codec_found) fits += fitsImport - except xml.parsers.expat.ExpatError, e: + except xml.parsers.expat.ExpatError: return False, "Malformed XML in %s" % path - except Exception, e: + except Exception: logger.exception("Unknown exception processing: %s", path) return False, "Unknown Error while processing %s" % path + ''' IDs = [] numFits = len(fits) @@ -1083,6 +1091,8 @@ class Fit(object): return True, fits + # TODO: port this to port.py + ''' def importFitFromBuffer(self, bufferStr, activeFit=None): _, fits = Port.importAuto(bufferStr, activeFit=activeFit) for fit in fits: @@ -1091,6 +1101,7 @@ class Fit(object): fit.targetResists = self.targetResists eos.db.save(fit) return fits + ''' def checkStates(self, fit, base): changed = False diff --git a/service/fleet.py b/service/fleet.py index cfad7f04b..68c344189 100644 --- a/service/fleet.py +++ b/service/fleet.py @@ -17,9 +17,11 @@ # along with pyfa. If not, see . #=============================================================================== -import eos.db -from eos.types import Fleet as Fleet_, Wing, Squad import copy +import eos.db +from eos.saveddata.fleet import Fleet as Fleet_ +from eos.saveddata.fleet import Fleet as Wing +from eos.saveddata.fleet import Fleet as Squad class Fleet(object): instance = None diff --git a/service/implantSet.py b/service/implantSet.py index 0d0df5808..e65b2e708 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -17,10 +17,11 @@ # along with pyfa. If not, see . #=============================================================================== +import copy + import eos.db import eos.types -import copy -import service.market +from service.market import Market class ImportError(Exception): pass @@ -75,9 +76,9 @@ class ImplantSets(): def saveChanges(self, s): eos.db.save(s) - + def importSets(self, text): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() lines = text.splitlines() newSets = [] errors = 0 @@ -117,9 +118,8 @@ class ImplantSets(): raise ImportError("No patterns found for import") if errors > 0: raise ImportError("%d sets imported from clipboard; %d errors"%(lenImports, errors)) - + def exportSets(self): patterns = self.getImplantSetList() patterns.sort(key=lambda p: p.name) return eos.types.ImplantSet.exportSets(*patterns) - diff --git a/service/market.py b/service/market.py index 7d1cdea19..4a297f00d 100644 --- a/service/market.py +++ b/service/market.py @@ -19,18 +19,18 @@ import re import threading -import wx - +import logging import Queue +import wx +from sqlalchemy.sql import or_ + import config import eos.db import eos.types -from sqlalchemy.sql import and_, or_ -from service.settings import SettingsProvider, NetworkSettings -import service -import service.conversions as conversions -import logging +from service import conversions +from service.settings import SettingsProvider +from service.price import Price try: from collections import OrderedDict @@ -86,7 +86,7 @@ class PriceWorkerThread(threading.Thread): # Grab prices, this is the time-consuming part if len(requests) > 0: - service.Price.fetchPrices(requests) + Price.fetchPrices(requests) wx.CallAfter(callback) queue.task_done() @@ -766,7 +766,7 @@ class Market(): def cb(): try: callback(requests) - except Exception, e: + except Exception: pass eos.db.commit() diff --git a/service/network.py b/service/network.py index f7383e03d..8923f8b57 100644 --- a/service/network.py +++ b/service/network.py @@ -17,12 +17,14 @@ # along with pyfa. If not, see . #=============================================================================== -from service.settings import NetworkSettings + import urllib2 import urllib -import config import socket +import config +from service.settings import NetworkSettings + # network timeout, otherwise pyfa hangs for a long while if no internet connection timeout = 3 socket.setdefaulttimeout(timeout) @@ -35,14 +37,13 @@ class RequestError(StandardError): pass class AuthenticationError(StandardError): - pass + pass class ServerError(StandardError): - pass + pass class TimeoutError(StandardError): - pass - + pass class Network(): # Request constants - every request must supply this, as it is checked if @@ -55,7 +56,7 @@ class Network(): _instance = None @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = Network() return cls._instance @@ -101,14 +102,14 @@ class Network(): request = urllib2.Request(url, headers=headers, data=urllib.urlencode(data) if data else None) try: return urllib2.urlopen(request) - except urllib2.HTTPError, error: + except urllib2.HTTPError as error: if error.code == 404: raise RequestError() elif error.code == 403: raise AuthenticationError() elif error.code >= 500: raise ServerError() - except urllib2.URLError, error: + except urllib2.URLError as error: if "timed out" in error.reason: raise TimeoutError() else: diff --git a/service/port.py b/service/port.py index a9b4089c7..9c662f706 100644 --- a/service/port.py +++ b/service/port.py @@ -20,15 +20,30 @@ import re import os import xml.dom - -from eos.types import State, Slot, Module, Cargo, Fit, Ship, Drone, Implant, Booster, Citadel -import service -import wx import logging -import config import collections import json +import wx + +from eos.types import State, Slot, Module, Cargo, Fit, Ship, Drone, Implant, Booster, Citadel +from service.crest import Crest +from service.market import Market + +import wx + +from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel +from service.crest import Crest +from service.market import Market +from service.fit import Fit + +import wx + +from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel +from service.crest import Crest +from service.market import Market +from service.fit import Fit + logger = logging.getLogger("pyfa.service.port") try: @@ -57,8 +72,8 @@ class Port(object): nested_dict = lambda: collections.defaultdict(nested_dict) fit = nested_dict() - sCrest = service.Crest.getInstance() - sFit = service.Fit.getInstance() + sCrest = Crest.getInstance() + sFit = Fit.getInstance() eve = sCrest.eve @@ -69,7 +84,7 @@ class Port(object): fit['ship']['id'] = ofit.ship.item.ID fit['ship']['name'] = '' - fit['description'] = ""%ofit.ID + fit['description'] = "" % ofit.ID fit['items'] = [] slotNum = {} @@ -86,7 +101,7 @@ class Port(object): slot = int(module.getModifiedItemAttr("subSystemSlot")) item['flag'] = slot else: - if not slot in slotNum: + if slot not in slotNum: slotNum[slot] = INV_FLAGS[slot] item['flag'] = slotNum[slot] @@ -99,7 +114,7 @@ class Port(object): fit['items'].append(item) if module.charge and sFit.serviceFittingOptions["exportCharges"]: - if not module.chargeID in charges: + if module.chargeID not in charges: charges[module.chargeID] = 0 # `or 1` because some charges (ie scripts) are without qty charges[module.chargeID] += module.numCharges or 1 @@ -168,7 +183,7 @@ class Port(object): @staticmethod def importCrest(str): fit = json.loads(str) - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() f = Fit() f.name = fit['name'] @@ -216,7 +231,7 @@ class Port(object): continue # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(f) + Fit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -226,7 +241,7 @@ class Port(object): @staticmethod def importDna(string): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() ids = map(int, re.findall(r'\d+', string)) for id in ids: @@ -291,7 +306,7 @@ class Port(object): moduleList.append(m) # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(f) + Fit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -304,7 +319,7 @@ class Port(object): @staticmethod def importEft(eftString): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() offineSuffix = " /OFFLINE" fit = Fit() @@ -371,12 +386,12 @@ class Port(object): if item.category.name == "Drone": extraAmount = int(extraAmount) if extraAmount is not None else 1 - if not modName in droneMap: + if modName not in droneMap: droneMap[modName] = 0 droneMap[modName] += extraAmount if len(modExtra) == 2 and item.category.name != "Drone": extraAmount = int(extraAmount) if extraAmount is not None else 1 - if not modName in cargoMap: + if modName not in cargoMap: cargoMap[modName] = 0 cargoMap[modName] += extraAmount elif item.category.name == "Implant": @@ -415,13 +430,13 @@ class Port(object): moduleList.append(m) # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(fit) + Fit.getInstance().recalc(fit) for m in moduleList: if m.fits(fit): m.owner = fit if not m.isValidState(m.state): - print "Error: Module", m, "cannot have state", m.state + print("Error: Module", m, "cannot have state", m.state) fit.modules.append(m) @@ -442,7 +457,7 @@ class Port(object): """Handle import from EFT config store file""" # Check if we have such ship in database, bail if we don't - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() try: sMkt.getItem(shipname) except: @@ -596,7 +611,7 @@ class Port(object): moduleList.append(m) # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(f) + Fit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -615,7 +630,7 @@ class Port(object): @staticmethod def importXml(text, callback=None, encoding="utf-8"): - sMkt = service.Market.getInstance() + sMkt = Market.getInstance() doc = xml.dom.minidom.parseString(text.encode(encoding)) fittings = doc.getElementsByTagName("fittings").item(0) @@ -676,7 +691,7 @@ class Port(object): continue # Recalc to get slot numbers correct for T3 cruisers - service.Fit.getInstance().recalc(f) + Fit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -694,10 +709,10 @@ class Port(object): offineSuffix = " /OFFLINE" export = "[%s, %s]\n" % (fit.ship.item.name, fit.name) stuff = {} - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() for module in fit.modules: slot = module.slot - if not slot in stuff: + if slot not in stuff: stuff[slot] = [] curr = module.item.name if module.item else ("[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "") if module.charge and sFit.serviceFittingOptions["exportCharges"]: @@ -776,12 +791,12 @@ class Port(object): if mod.slot == Slot.SUBSYSTEM: subsystems.append(mod) continue - if not mod.itemID in mods: + if mod.itemID not in mods: mods[mod.itemID] = 0 mods[mod.itemID] += 1 if mod.charge: - if not mod.chargeID in charges: + if mod.chargeID not in charges: charges[mod.chargeID] = 0 # `or 1` because some charges (ie scripts) are without qty charges[mod.chargeID] += mod.numCharges or 1 @@ -792,13 +807,8 @@ class Port(object): for mod in mods: dna += ":{0};{1}".format(mod, mods[mod]) - # drones are known to be in split stacks - groupedDrones = OrderedDict() for drone in fit.drones: - groupedDrones[drone.itemID] = groupedDrones.get(drone.itemID, 0) + drone.amount - - for droneItemID in groupedDrones: - dna += ":{0};{1}".format(droneItemID, groupedDrones[droneItemID]) + dna += ":{0};{1}".format(drone.itemID, drone.amount) for cargo in fit.cargo: # DNA format is a simple/dumb format. As CCP uses the slot information of the item itself @@ -807,7 +817,7 @@ class Port(object): # as being "Fitted" to whatever slot they are for, and it causes an corruption error in the # client when trying to save the fit if cargo.item.category.name == "Charge": - if not cargo.item.ID in charges: + if cargo.item.ID not in charges: charges[cargo.item.ID] = 0 charges[cargo.item.ID] += cargo.amount @@ -821,7 +831,7 @@ class Port(object): doc = xml.dom.minidom.Document() fittings = doc.createElement("fittings") doc.appendChild(fittings) - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() for i, fit in enumerate(fits): try: @@ -847,7 +857,7 @@ class Port(object): # Order of subsystem matters based on this attr. See GH issue #130 slotId = module.getModifiedItemAttr("subSystemSlot") - 125 else: - if not slot in slotNum: + if slot not in slotNum: slotNum[slot] = 0 slotId = slotNum[slot] @@ -861,7 +871,7 @@ class Port(object): fitting.appendChild(hardware) if module.charge and sFit.serviceFittingOptions["exportCharges"]: - if not module.charge.name in charges: + if module.charge.name not in charges: charges[module.charge.name] = 0 # `or 1` because some charges (ie scripts) are without qty charges[module.charge.name] += module.numCharges or 1 @@ -874,7 +884,7 @@ class Port(object): fitting.appendChild(hardware) for cargo in fit.cargo: - if not cargo.item.name in charges: + if cargo.item.name not in charges: charges[cargo.item.name] = 0 charges[cargo.item.name] += cargo.amount @@ -885,7 +895,7 @@ class Port(object): hardware.setAttribute("type", name) fitting.appendChild(hardware) except: - print "Failed on fitID: %d"%fit.ID + print("Failed on fitID: %d" % fit.ID) continue finally: if callback: @@ -897,13 +907,12 @@ class Port(object): def exportMultiBuy(fit): export = "%s\n" % (fit.ship.item.name) stuff = {} - sFit = service.Fit.getInstance() + sFit = Fit.getInstance() for module in fit.modules: slot = module.slot - if not slot in stuff: + if slot not in stuff: stuff[slot] = [] - curr = "%s\n" % module.item.name if module.item else ( - "") + curr = "%s\n" % module.item.name if module.item else "" if module.charge and sFit.serviceFittingOptions["exportCharges"]: curr += "%s x%s\n" % (module.charge.name, module.numCharges) stuff[slot].append(curr) diff --git a/service/prefetch.py b/service/prefetch.py index b16f52d40..f3d09c43c 100644 --- a/service/prefetch.py +++ b/service/prefetch.py @@ -18,10 +18,11 @@ #=============================================================================== import threading -import config import os + +import config import eos.types -import eos.db.migration as migration +from eos.db import migration from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues class PrefetchThread(threading.Thread): @@ -44,27 +45,26 @@ prefetch.start() # @ todo: move this to pyfa.py ######## -#Make sure the saveddata db exists -if not os.path.exists(config.savePath): +# Make sure the saveddata db exists +if config.savePath and not os.path.exists(config.savePath): os.mkdir(config.savePath) -if os.path.isfile(config.saveDB): +if config.saveDB and os.path.isfile(config.saveDB): # If database exists, run migration after init'd database eos.db.saveddata_meta.create_all() migration.update(eos.db.saveddata_engine) # Import default database values # Import values that must exist otherwise Pyfa breaks DefaultDatabaseValues.importRequiredDefaults() -else: +elif config.saveDB: # If database does not exist, do not worry about migration. Simply # create and set version eos.db.saveddata_meta.create_all() eos.db.saveddata_engine.execute('PRAGMA user_version = {}'.format(migration.getAppVersion())) - #Import default database values + # Import default database values # Import values that must exist otherwise Pyfa breaks DefaultDatabaseValues.importRequiredDefaults() # Import default values for damage profiles DefaultDatabaseValues.importDamageProfileDefaults() # Import default values for target resist profiles DefaultDatabaseValues.importResistProfileDefaults() - diff --git a/service/price.py b/service/price.py index 71858cf7c..f2c9c9bd9 100644 --- a/service/price.py +++ b/service/price.py @@ -17,12 +17,13 @@ # along with pyfa. If not, see . #=============================================================================== -import service -import eos.db -import eos.types + import time from xml.dom import minidom +import eos +from service.network import Network, TimeoutError + VALIDITY = 24*60*60 # Price validity period, 24 hours REREQUEST = 4*60*60 # Re-request delay for failed fetches, 4 hours TIMEOUT = 15*60 # Network timeout delay for connection issues, 15 minutes @@ -71,15 +72,15 @@ class Price(): # Attempt to send request and process it try: - network = service.Network.getInstance() + network = Network.getInstance() data = network.request(baseurl, network.PRICES, data) xml = minidom.parse(data) types = xml.getElementsByTagName("marketstat").item(0).getElementsByTagName("type") # Cycle through all types we've got from request - for type in types: + for type_ in types: # Get data out of each typeID details tree - typeID = int(type.getAttribute("id")) - sell = type.getElementsByTagName("sell").item(0) + typeID = int(type_.getAttribute("id")) + sell = type_.getElementsByTagName("sell").item(0) # If price data wasn't there, set price to zero try: percprice = float(sell.getElementsByTagName("percentile").item(0).firstChild.data) @@ -96,7 +97,7 @@ class Price(): del priceMap[typeID] # If getting or processing data returned any errors - except service.network.TimeoutError, e: + except TimeoutError: # Timeout error deserves special treatment for typeID in priceMap.keys(): priceobj = priceMap[typeID] diff --git a/service/pycrest/__init__.py b/service/pycrest/__init__.py index 97244c957..f51a5440e 100644 --- a/service/pycrest/__init__.py +++ b/service/pycrest/__init__.py @@ -9,5 +9,3 @@ logger = logging.getLogger('pycrest') logger.addHandler(NullHandler()) version = "0.0.1" - -from .eve import EVE \ No newline at end of file diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index c0f33becd..221389b79 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -1,15 +1,17 @@ +import logging +import re import os import base64 import time import zlib import requests - -from . import version -from compat import bytes_, text_ -from errors import APIException from requests.adapters import HTTPAdapter +import config +from service.pycrest.compat import bytes_, text_ +from service.pycrest.errors import APIException + try: from urllib.parse import urlparse, urlunparse, parse_qsl except ImportError: # pragma: no cover @@ -20,13 +22,6 @@ try: except ImportError: # pragma: no cover import cPickle as pickle -try: - from urllib.parse import quote -except ImportError: # pragma: no cover - from urllib import quote -import logging -import re -import config logger = logging.getLogger("pycrest.eve") cache_re = re.compile(r'max-age=([0-9]+)') @@ -110,8 +105,7 @@ class APIConnection(object): "Accept": "application/json", }) session.headers.update(additional_headers) - session.mount('https://public-crest.eveonline.com', - HTTPAdapter()) + session.mount('https://public-crest.eveonline.com', HTTPAdapter()) self._session = session if cache: if isinstance(cache, APICache): diff --git a/service/pycrest/weak_ciphers.py b/service/pycrest/weak_ciphers.py index 03de8321a..13a09abba 100644 --- a/service/pycrest/weak_ciphers.py +++ b/service/pycrest/weak_ciphers.py @@ -1,32 +1,23 @@ import datetime import ssl -import sys import warnings from requests.adapters import HTTPAdapter try: - from requests.packages import urllib3 - from requests.packages.urllib3.util import ssl_ - - from requests.packages.urllib3.exceptions import ( - SystemTimeWarning, - SecurityWarning, - ) - from requests.packages.urllib3.packages.ssl_match_hostname import \ - match_hostname + from requests.packages import urllib3 + from requests.packages.urllib3.util import ssl_ + from requests.packages.urllib3.exceptions import ( + SystemTimeWarning, + SecurityWarning, + ) + from requests.packages.urllib3.packages.ssl_match_hostname import \ + match_hostname except: - import urllib3 - from urllib3.util import ssl_ - - from urllib3.exceptions import ( - SystemTimeWarning, - SecurityWarning, - ) - from urllib3.packages.ssl_match_hostname import \ - match_hostname - - + import urllib3 + from urllib3.util import ssl_ + from urllib3.exceptions import SystemTimeWarning, SecurityWarning + from urllib3.packages.ssl_match_hostname import match_hostname class WeakCiphersHTTPSConnection( diff --git a/service/server.py b/service/server.py index ed42d0082..f65729a89 100644 --- a/service/server.py +++ b/service/server.py @@ -2,11 +2,12 @@ import BaseHTTPServer import urlparse import socket import thread -import wx -from service.settings import CRESTSettings - import logging +import wx + +from service.settings import CRESTSettings + logger = logging.getLogger(__name__) HTML = ''' @@ -121,4 +122,3 @@ if __name__ == "__main__": thread.start_new_thread(httpd.serve, ()) raw_input("Press to stop server\n") httpd.stop() - diff --git a/service/settings.py b/service/settings.py index 89f871973..bf70b8705 100644 --- a/service/settings.py +++ b/service/settings.py @@ -19,11 +19,12 @@ import cPickle import os.path -import config import urllib2 +import config + class SettingsProvider(): - BASE_PATH = os.path.join(config.savePath, "settings") + BASE_PATH = os.path.join(config.savePath or ".", "settings") settings = {} _instance = None @classmethod @@ -260,8 +261,8 @@ class HTMLExportSettings(): def setEnabled(self, enabled): self.serviceHTMLExportSettings["enabled"] = enabled - - + + def getMinimalEnabled(self): return self.serviceHTMLExportSettings["minimal"] diff --git a/service/targetResists.py b/service/targetResists.py index 2640551f4..26279ba6b 100644 --- a/service/targetResists.py +++ b/service/targetResists.py @@ -17,14 +17,16 @@ # along with pyfa. If not, see . #=============================================================================== -import eos.db -import eos.types import copy -class ImportError(Exception): - pass +import eos +from eos.saveddata import targetResists as db_targetResists -class TargetResists(): + +class ImportError(Exception): + pass + +class TargetResists(object): instance = None @classmethod def getInstance(cls): @@ -84,5 +86,4 @@ class TargetResists(): def exportPatterns(self): patterns = self.getTargetResistsList() patterns.sort(key=lambda p: p.name) - return eos.types.TargetResists.exportPatterns(*patterns) - + return db_targetResists.TargetResists.exportPatterns(*patterns) diff --git a/service/update.py b/service/update.py index 47a3e35b0..8f498e724 100644 --- a/service/update.py +++ b/service/update.py @@ -18,23 +18,25 @@ #=============================================================================== import threading -import wx -import urllib2 import json -import config -import service -import dateutil.parser import calendar +import wx +import dateutil.parser + +import config +from service.network import Network +from service.settings import UpdateSettings + class CheckUpdateThread(threading.Thread): def __init__(self, callback): threading.Thread.__init__(self) self.callback = callback - self.settings = service.settings.UpdateSettings.getInstance() - self.network = service.Network.getInstance() + self.settings = UpdateSettings.getInstance() + self.network = Network.getInstance() def run(self): - network = service.Network.getInstance() + network = Network.getInstance() try: response = network.request('https://api.github.com/repos/pyfa-org/Pyfa/releases', network.UPDATE) @@ -91,5 +93,3 @@ class Update(): if cls.instance == None: cls.instance = Update() return cls.instance - - diff --git a/tests/test_package.py b/tests/test_package.py new file mode 100644 index 000000000..b63076b47 --- /dev/null +++ b/tests/test_package.py @@ -0,0 +1,34 @@ +"""import tests.""" + +import os +import importlib + +import pytest + +import service +import gui +import eos +import utils + + +def test_packages(): + assert service + assert gui + assert eos + assert utils + + +def service_modules(): + for root, folders, files in os.walk("service"): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + yield mod_name + + +@pytest.mark.parametrize("mod_name", service_modules()) +def test_service_imports(mod_name): + assert importlib.import_module(mod_name) From ea8a4c01cb952773bc92beb6ca8bacb78bbf9820 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Fri, 2 Dec 2016 12:50:49 -0800 Subject: [PATCH 02/53] Eliminate export calling fit.py (no need) Except for 1 (clipboardXML), same number of lines of code in mainFrame, lots of code gone from fit, and no more complicated. Also spotted an import reference that got missed. --- gui/mainFrame.py | 30 +++++++++++++++++------------- service/fit.py | 28 +--------------------------- 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 480d50681..ad7a4a39d 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -68,6 +68,9 @@ from service.update import Update # import this to access override setting from eos.modifiedAttributeDict import ModifiedAttributeDict from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues +from eos import db +from service.port import Port +from service.settings import HTMLExportSettings from time import gmtime, strftime @@ -665,28 +668,29 @@ class MainFrame(wx.Frame): self.marketBrowser.search.Focus() def clipboardEft(self): - sFit = Fit.getInstance() - toClipboard(sFit.exportFit(self.getActiveFit())) + fit = db.getFit(self.getActiveFit()) + toClipboard(Port.exportEft(fit)) def clipboardEftImps(self): - sFit = Fit.getInstance() - toClipboard(sFit.exportEftImps(self.getActiveFit())) + fit = db.getFit(self.getActiveFit()) + toClipboard(Port.exportEftImps(fit)) def clipboardDna(self): - sFit = Fit.getInstance() - toClipboard(sFit.exportDna(self.getActiveFit())) + fit = db.getFit(self.getActiveFit()) + toClipboard(Port.exportDna(fit)) def clipboardCrest(self): - sFit = Fit.getInstance() - toClipboard(sFit.exportCrest(self.getActiveFit())) + fit = db.getFit(self.getActiveFit()) + toClipboard(Port.exportCrest(fit)) def clipboardXml(self): - sFit = Fit.getInstance() - toClipboard(sFit.exportXml(None, self.getActiveFit())) + fitIDs = self.getActiveFit() + fits = map(lambda fitID: db.getFit(fitID), fitIDs) + toClipboard(Port.exportXml(None, *fits)) def clipboardMultiBuy(self): - sFit = Fit.getInstance() - toClipboard(sFit.exportMultiBuy(self.getActiveFit())) + fit = db.getFit(self.getActiveFit()) + toClipboard(Port.exportMultiBuy(fit)) def importFromClipboard(self, event): sFit = Fit.getInstance() @@ -786,7 +790,7 @@ class MainFrame(wx.Frame): def exportHtml(self, event): from gui.utils.exportHtml import exportHtml sFit = Fit.getInstance() - settings = service.settings.HTMLExportSettings.getInstance() + settings = HTMLExportSettings.getInstance() max = sFit.countAllFits() path = settings.getPath() diff --git a/service/fit.py b/service/fit.py index 16e52ab82..57c72ac31 100644 --- a/service/fit.py +++ b/service/fit.py @@ -960,33 +960,7 @@ class Fit(object): fit.damagePattern = dp self.recalc(fit) - # TODO: port this to port.py - ''' - def exportFit(self, fitID): - fit = eos.db.getFit(fitID) - return Port.exportEft(fit) - - def exportEftImps(self, fitID): - fit = eos.db.getFit(fitID) - return Port.exportEftImps(fit) - - def exportDna(self, fitID): - fit = eos.db.getFit(fitID) - return Port.exportDna(fit) - - def exportCrest(self, fitID, callback=None): - fit = eos.db.getFit(fitID) - return Port.exportCrest(fit, callback) - - def exportXml(self, callback=None, *fitIDs): - fits = map(lambda fitID: eos.db.getFit(fitID), fitIDs) - return Port.exportXml(callback, *fits) - - def exportMultiBuy(self, fitID): - fit = eos.db.getFit(fitID) - return Port.exportMultiBuy(fit) - ''' - + # TODO: Should move all the backup/import stuff out of here def backupFits(self, path, callback): thread = FitBackupThread(path, callback) thread.start() From 6ef57e735e626ec690edb7bf11a0d3e3d157677b Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sat, 3 Dec 2016 06:21:52 -0800 Subject: [PATCH 03/53] Missed imports. Fixed now. --- gui/builtinContextMenus/amount.py | 1 + gui/cargoView.py | 1 + 2 files changed, 2 insertions(+) diff --git a/gui/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py index 8b8a2ae26..059111b63 100644 --- a/gui/builtinContextMenus/amount.py +++ b/gui/builtinContextMenus/amount.py @@ -4,6 +4,7 @@ import eos.types import gui.mainFrame import gui.globalEvents as GE import wx +from service.fit import Fit class ChangeAmount(ContextMenu): def __init__(self): diff --git a/gui/cargoView.py b/gui/cargoView.py index ed58e4ba3..a6b93c1ef 100644 --- a/gui/cargoView.py +++ b/gui/cargoView.py @@ -24,6 +24,7 @@ from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import globalEvents as GE from service.fit import Fit +from service.market import Market class CargoViewDrop(wx.PyDropTarget): def __init__(self, dropFn): From 510492e5e9ac881851b1d426e1fb90e5b373170d Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sat, 3 Dec 2016 17:12:16 -0800 Subject: [PATCH 04/53] More refactoring, elimited another recursive import --- eos/db/saveddata/loadDefaultDatabaseValues.py | 10 +- eos/effectHandlerHelpers.py | 8 - eos/saveddata/module.py | 16 +- gui/builtinContextMenus/amount.py | 6 +- gui/commandView.py | 4 +- gui/crestFittings.py | 2 + gui/mainFrame.py | 3 +- gui/projectedView.py | 10 +- service/attribute.py | 2 + service/character.py | 20 +- service/damagePattern.py | 9 +- service/fit.py | 212 +++--------------- service/implantSet.py | 15 +- service/market.py | 23 +- service/port.py | 168 ++++++++++++-- service/prefetch.py | 13 +- service/price.py | 4 +- service/settings.py | 2 +- service/targetResists.py | 28 +-- 19 files changed, 277 insertions(+), 278 deletions(-) diff --git a/eos/db/saveddata/loadDefaultDatabaseValues.py b/eos/db/saveddata/loadDefaultDatabaseValues.py index 3dcccca68..6769ed71c 100644 --- a/eos/db/saveddata/loadDefaultDatabaseValues.py +++ b/eos/db/saveddata/loadDefaultDatabaseValues.py @@ -18,8 +18,8 @@ # =============================================================================== import eos.db -import eos.types - +from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern +from eos.saveddata.targetResists import TargetResists as es_TargetResists class ImportError(Exception): pass @@ -118,7 +118,7 @@ class DefaultDatabaseValues(): name, em, therm, kin, exp = damageProfileRow damageProfile = eos.db.getDamagePattern(name) if damageProfile is None: - damageProfile = eos.types.DamagePattern(em, therm, kin, exp) + damageProfile = es_DamagePattern(em, therm, kin, exp) damageProfile.name = name eos.db.save(damageProfile) @@ -180,7 +180,7 @@ class DefaultDatabaseValues(): name, em, therm, kin, exp = targetResistProfileRow resistsProfile = eos.db.eos.db.getTargetResists(name) if resistsProfile is None: - resistsProfile = eos.types.TargetResists(em, therm, kin, exp) + resistsProfile = es_TargetResists(em, therm, kin, exp) resistsProfile.name = name eos.db.save(resistsProfile) @@ -192,6 +192,6 @@ class DefaultDatabaseValues(): name, em, therm, kin, exp = damageProfileRow damageProfile = eos.db.getDamagePattern(name) if damageProfile is None: - damageProfile = eos.types.DamagePattern(em, therm, kin, exp) + damageProfile = es_DamagePattern(em, therm, kin, exp) damageProfile.name = name eos.db.save(damageProfile) diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py index 2e42ef69f..09ac4500b 100644 --- a/eos/effectHandlerHelpers.py +++ b/eos/effectHandlerHelpers.py @@ -21,7 +21,6 @@ import logging import eos.db -import eos.types logger = logging.getLogger(__name__) @@ -160,13 +159,6 @@ class HandledModuleList(HandledList): for i in xrange(oldPos, len(self)): self[i].position -= 1 - def toDummy(self, index): - mod = self[index] - if not mod.isEmpty: - dummy = eos.types.Module.buildEmpty(mod.slot) - dummy.position = index - self[index] = dummy - def toModule(self, index, mod): mod.position = index self[index] = mod diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 1a1c56de3..998ac595b 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -32,9 +32,6 @@ logger = logging.getLogger(__name__) class State(Enum): - def __init__(self): - pass - OFFLINE = -1 ONLINE = 0 ACTIVE = 1 @@ -42,8 +39,6 @@ class State(Enum): class Slot(Enum): - def __init__(self): - pass # These are self-explanatory LOW = 1 @@ -65,15 +60,13 @@ class Slot(Enum): class Hardpoint(Enum): - def __init__(self): - pass NONE = 0 MISSILE = 1 TURRET = 2 -class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): +class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, list): """An instance of this class represents a module together with its charge and modified attributes""" DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") MINING_ATTRIBUTES = ("miningAmount",) @@ -141,6 +134,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.__chargeModifiedAttributes.original = self.__charge.attributes self.__chargeModifiedAttributes.overrides = self.__charge.overrides + def toDummy(self, index): + mod = self[index] + if not mod.isEmpty: + dummy = Module.buildEmpty(mod.slot) + dummy.position = index + self[index] = dummy + @classmethod def buildEmpty(cls, slot): empty = Module(None) diff --git a/gui/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py index 059111b63..ae9331932 100644 --- a/gui/builtinContextMenus/amount.py +++ b/gui/builtinContextMenus/amount.py @@ -5,6 +5,8 @@ import gui.mainFrame import gui.globalEvents as GE import wx from service.fit import Fit +from eos.saveddata.cargo import Cargo as es_Cargo +from eos.saveddata.fighter import Fighter as es_Fighter class ChangeAmount(ContextMenu): def __init__(self): @@ -50,11 +52,11 @@ class AmountChanger(wx.Dialog): mainFrame = gui.mainFrame.MainFrame.getInstance() fitID = mainFrame.getActiveFit() - if isinstance(self.thing, eos.types.Cargo): + if isinstance(self.thing, es_Cargo): sFit.addCargo(fitID, self.thing.item.ID, int(self.input.GetLineText(0)), replace=True) elif isinstance(self.thing, eos.types.Fit): sFit.changeAmount(fitID, self.thing, int(self.input.GetLineText(0))) - elif isinstance(self.thing, eos.types.Fighter): + elif isinstance(self.thing, es_Fighter): sFit.changeActiveFighters(fitID, self.thing, int(self.input.GetLineText(0))) wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/commandView.py b/gui/commandView.py index ad262b6bf..37caba85f 100644 --- a/gui/commandView.py +++ b/gui/commandView.py @@ -24,9 +24,9 @@ import gui.globalEvents as GE import gui.droneView from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu -import eos.types from service.fit import Fit from service.market import Market +from eos.saveddata.drone import Drone as es_Drone class DummyItem: @@ -108,7 +108,7 @@ class CommandView(d.Display): def startDrag(self, event): row = event.GetIndex() - if row != -1 and isinstance(self.get(row), eos.types.Drone): + if row != -1 and isinstance(self.get(row), es_Drone): data = wx.PyTextDataObject() data.SetText("command:"+str(self.GetItemData(row))) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index d51693a59..585e0f7ec 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -5,6 +5,8 @@ import wx import requests from service.crest import CrestModes +from service.crest import Crest +from service.fit import Fit from eos.types import Cargo from eos.db import getItem diff --git a/gui/mainFrame.py b/gui/mainFrame.py index ad7a4a39d..4bad30a0a 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -784,7 +784,8 @@ class MainFrame(wx.Frame): "Backing up %d fits to: %s"%(max, filePath), maximum=max, parent=self, style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) - sFit.backupFits(filePath, self.backupCallback) + + Port.backupFits(filePath, self.backupCallback) self.progressDialog.ShowModal() def exportHtml(self, event): diff --git a/gui/projectedView.py b/gui/projectedView.py index cccf301af..1e753bc77 100644 --- a/gui/projectedView.py +++ b/gui/projectedView.py @@ -118,7 +118,7 @@ class ProjectedView(d.Display): def startDrag(self, event): row = event.GetIndex() - if row != -1 and isinstance(self.get(row), eos.types.Drone): + if row != -1 and isinstance(self.get(row), es_Drone): data = wx.PyTextDataObject() data.SetText("projected:"+str(self.GetItemData(row))) @@ -134,7 +134,7 @@ class ProjectedView(d.Display): def _merge(self, src, dst): dstDrone = self.get(dst) - if isinstance(dstDrone, eos.types.Drone): + if isinstance(dstDrone, es_Drone): sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() if sFit.mergeDrones(fitID, self.get(src), dstDrone, True): @@ -247,15 +247,15 @@ class ProjectedView(d.Display): item = self.get(sel) if item is None: return sMkt = Market.getInstance() - if isinstance(item, eos.types.Drone): + if isinstance(item, es_Drone): srcContext = "projectedDrone" itemContext = sMkt.getCategoryByItem(item.item).name context = ((srcContext, itemContext),) - elif isinstance(item, eos.types.Fighter): + elif isinstance(item, es_Fighter): srcContext = "projectedFighter" itemContext = sMkt.getCategoryByItem(item.item).name context = ((srcContext, itemContext),) - elif isinstance(item, eos.types.Module): + elif isinstance(item, es_Module): modSrcContext = "projectedModule" modItemContext = sMkt.getCategoryByItem(item.item).name modFullContext = (modSrcContext, modItemContext) diff --git a/service/attribute.py b/service/attribute.py index b6a9ea500..535bb23a2 100644 --- a/service/attribute.py +++ b/service/attribute.py @@ -34,4 +34,6 @@ class Attribute(): elif isinstance(identity, (int, float)): id = int(identity) info = eos.db.getAttributeInfo(id, eager=("icon", "unit")) + else: + info = None return info diff --git a/service/character.py b/service/character.py index c6d3ca98e..f99b79692 100644 --- a/service/character.py +++ b/service/character.py @@ -31,9 +31,13 @@ import wx import config import eos.db -import eos.types from service.eveapi import EVEAPIConnection, ParseXML +from eos.saveddata.implant import Implant as es_Implant +from eos.saveddata.character import Character as es_Character +from eos.saveddata.module import Slot as es_Slot, Module as es_Module +from eos.saveddata.fighter import Fighter as es_Fighter + logger = logging.getLogger(__name__) class CharacterImportThread(threading.Thread): @@ -175,13 +179,13 @@ class Character(object): thread.start() def all0(self): - return eos.types.Character.getAll0() + return es_Character.getAll0() def all0ID(self): return self.all0().ID def all5(self): - return eos.types.Character.getAll5() + return es_Character.getAll5() def all5ID(self): return self.all5().ID @@ -248,7 +252,7 @@ class Character(object): return eos.db.getCharacter(charID).name def new(self, name="New Character"): - char = eos.types.Character(name) + char = es_Character(name) eos.db.save(char) return char @@ -344,7 +348,7 @@ class Character(object): logger.error("Trying to add implant to read-only character") return - implant = eos.types.Implant(eos.db.getItem(itemID)) + implant = es_Implant(eos.db.getItem(itemID)) char.implants.append(implant) eos.db.commit() @@ -361,17 +365,17 @@ class Character(object): toCheck = [] reqs = {} for thing in itertools.chain(fit.modules, fit.drones, fit.fighters, (fit.ship,)): - if isinstance(thing, eos.types.Module) and thing.slot == eos.types.Slot.RIG: + if isinstance(thing, es_Module) and thing.slot == es_Slot.RIG: continue for attr in ("item", "charge"): - if attr == "charge" and isinstance(thing, eos.types.Fighter): + if attr == "charge" and isinstance(thing, es_Fighter): # Fighter Bombers are automatically charged with micro bombs. # These have skill requirements attached, but aren't used in EVE. continue subThing = getattr(thing, attr, None) subReqs = {} if subThing is not None: - if isinstance(thing, eos.types.Fighter) and attr == "charge": + if isinstance(thing, es_Fighter) and attr == "charge": continue self._checkRequirements(fit, fit.character, subThing, subReqs) if subReqs: diff --git a/service/damagePattern.py b/service/damagePattern.py index 226bbf47e..2d1132d5a 100644 --- a/service/damagePattern.py +++ b/service/damagePattern.py @@ -20,8 +20,7 @@ import copy import eos.db -import eos.types - +from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern class ImportError(Exception): pass @@ -41,7 +40,7 @@ class DamagePattern(): return eos.db.getDamagePattern(name) def newPattern(self, name): - p = eos.types.DamagePattern(0, 0, 0, 0) + p = es_DamagePattern(0, 0, 0, 0) p.name = name eos.db.save(p) return p @@ -67,7 +66,7 @@ class DamagePattern(): for pattern in current: lookup[pattern.name] = pattern - imports, num = eos.types.DamagePattern.importPatterns(text) + imports, num = es_DamagePattern.importPatterns(text) for pattern in imports: if pattern.name in lookup: match = lookup[pattern.name] @@ -89,4 +88,4 @@ class DamagePattern(): del patterns[i] patterns.sort(key=lambda p: p.name) - return eos.types.DamagePattern.exportPatterns(*patterns) + return es_DamagePattern.exportPatterns(*patterns) diff --git a/service/fit.py b/service/fit.py index 57c72ac31..aed843cfa 100644 --- a/service/fit.py +++ b/service/fit.py @@ -17,18 +17,23 @@ # along with pyfa. If not, see . # =============================================================================== -import locale + import copy -import threading import logging import wx -from codecs import open - -import xml.parsers.expat import eos.db -from eos.types import State, Slot, Module, Drone, Fighter -from eos.types import Fit as FitType +from eos.types import State, Slot, Module, Drone, Fighter, Fit as FitType + +from eos.saveddata.ship import Ship as es_Ship +from eos.saveddata.citadel import Citadel as es_Citadel +from eos.saveddata.implant import Implant as es_Implant +from eos.saveddata.booster import Booster as es_Booster +from eos.saveddata.module import Module as es_Module +from eos.saveddata.fighter import Fighter as es_Fighter +from eos.saveddata.drone import Drone as es_Drone +from eos.saveddata.cargo import Cargo as es_Cargo +from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern from service.market import Market from service.damagePattern import DamagePattern @@ -37,47 +42,13 @@ from eos.saveddata.character import Character as saveddata_Character from service.fleet import Fleet from service.settings import SettingsProvider + # TODO: port this to port.py #from service.port import Port logger = logging.getLogger(__name__) -class FitBackupThread(threading.Thread): - def __init__(self, path, callback): - threading.Thread.__init__(self) - self.path = path - self.callback = callback - - def run(self): - path = self.path - sFit = Fit.getInstance() - allFits = map(lambda x: x[0], sFit.getAllFits()) - backedUpFits = sFit.exportXml(self.callback, *allFits) - backupFile = open(path, "w", encoding="utf-8") - backupFile.write(backedUpFits) - backupFile.close() - - # Send done signal to GUI - wx.CallAfter(self.callback, -1) - - -class FitImportThread(threading.Thread): - def __init__(self, paths, callback): - threading.Thread.__init__(self) - self.paths = paths - self.callback = callback - - def run(self): - sFit = Fit.getInstance() - success, result = sFit.importFitFromFiles(self.paths, self.callback) - - if not success: # there was an error during processing - logger.error("Error while processing file import: %s", result) - wx.CallAfter(self.callback, -2, result) - else: # Send done signal to GUI - wx.CallAfter(self.callback, -1, result) - class Fit(object): instance = None @@ -163,9 +134,9 @@ class Fit(object): def newFit(self, shipID, name=None): try: - ship = eos.types.Ship(eos.db.getItem(shipID)) + ship = es_Ship(eos.db.getItem(shipID)) except ValueError: - ship = eos.types.Citadel(eos.db.getItem(shipID)) + ship = es_Citadel(eos.db.getItem(shipID)) fit = FitType(ship) fit.name = name if name is not None else "New %s" % fit.ship.item.name fit.damagePattern = self.pattern @@ -291,7 +262,7 @@ class Fit(object): fit = eos.db.getFit(fitID) item = eos.db.getItem(itemID, eager="attributes") try: - implant = eos.types.Implant(item) + implant = es_Implant(item) except ValueError: return False @@ -317,7 +288,7 @@ class Fit(object): fit = eos.db.getFit(fitID) item = eos.db.getItem(itemID, eager="attributes") try: - booster = eos.types.Booster(item) + booster = es_Booster(item) except ValueError: return False @@ -362,19 +333,19 @@ class Fit(object): break if drone is None: - drone = eos.types.Drone(thing) + drone = es_Drone(thing) fit.projectedDrones.append(drone) drone.amount += 1 elif thing.category.name == "Fighter": - fighter = eos.types.Fighter(thing) + fighter = es_Fighter(thing) fit.projectedFighters.append(fighter) elif thing.group.name == "Effect Beacon": - module = eos.types.Module(thing) + module = es_Module(thing) module.state = State.ONLINE fit.projectedModules.append(module) else: - module = eos.types.Module(thing) + module = es_Module(thing) module.state = State.ACTIVE if not module.canHaveState(module.state, fit): module.state = State.OFFLINE @@ -405,14 +376,14 @@ class Fit(object): def toggleProjected(self, fitID, thing, click): fit = eos.db.getFit(fitID) - if isinstance(thing, eos.types.Drone): + if isinstance(thing, es_Drone): if thing.amountActive == 0 and thing.canBeApplied(fit): thing.amountActive = thing.amount else: thing.amountActive = 0 - elif isinstance(thing, eos.types.Fighter): + elif isinstance(thing, es_Fighter): thing.active = not thing.active - elif isinstance(thing, eos.types.Module): + elif isinstance(thing, es_Module): thing.state = self.__getProposedState(thing, click) if not thing.canHaveState(thing.state, fit): thing.state = State.OFFLINE @@ -453,11 +424,11 @@ class Fit(object): def removeProjected(self, fitID, thing): fit = eos.db.getFit(fitID) - if isinstance(thing, eos.types.Drone): + if isinstance(thing, es_Drone): fit.projectedDrones.remove(thing) - elif isinstance(thing, eos.types.Module): + elif isinstance(thing, es_Module): fit.projectedModules.remove(thing) - elif isinstance(thing, eos.types.Fighter): + elif isinstance(thing, es_Fighter): fit.projectedFighters.remove(thing) else: del fit.__projectedFits[thing.ID] @@ -477,7 +448,7 @@ class Fit(object): fit = eos.db.getFit(fitID) item = eos.db.getItem(itemID, eager=("attributes", "group.category")) try: - m = eos.types.Module(item) + m = es_Module(item) except ValueError: return False @@ -524,7 +495,7 @@ class Fit(object): item = eos.db.getItem(newItemID, eager=("attributes", "group.category")) try: - m = eos.types.Module(item) + m = es_Module(item) except ValueError: return False @@ -591,7 +562,7 @@ class Fit(object): x.amount += 1 break else: - moduleP = eos.types.Cargo(module.item) + moduleP = es_Cargo(module.item) moduleP.amount = 1 fit.cargo.insert(cargoIdx, moduleP) @@ -656,7 +627,7 @@ class Fit(object): if cargo is None: # if we don't have the item already in cargo, use default values - cargo = eos.types.Cargo(item) + cargo = es_Cargo(item) fit.cargo.append(cargo) if replace: @@ -748,7 +719,7 @@ class Fit(object): break if drone is None: - drone = eos.types.Drone(item) + drone = es_Drone(item) if drone.fits(fit) is True: fit.drones.append(drone) else: @@ -950,7 +921,7 @@ class Fit(object): sDP = DamagePattern.getInstance() dp = sDP.getDamagePattern("Selected Ammo") if dp is None: - dp = eos.types.DamagePattern() + dp = es_DamagePattern() dp.name = "Selected Ammo" fit = eos.db.getFit(fitID) @@ -960,123 +931,6 @@ class Fit(object): fit.damagePattern = dp self.recalc(fit) - # TODO: Should move all the backup/import stuff out of here - def backupFits(self, path, callback): - thread = FitBackupThread(path, callback) - thread.start() - - def importFitsThreaded(self, paths, callback): - thread = FitImportThread(paths, callback) - thread.start() - - def importFitFromFiles(self, paths, callback=None): - """ - Imports fits from file(s). First processes all provided paths and stores - assembled fits into a list. This allows us to call back to the GUI as - fits are processed as well as when fits are being saved. - returns - """ - defcodepage = locale.getpreferredencoding() - - fits = [] - for path in paths: - if callback: # Pulse - wx.CallAfter(callback, 1, "Processing file:\n%s" % path) - - file = open(path, "r") - srcString = file.read() - - if len(srcString) == 0: # ignore blank files - continue - - codec_found = None - # If file had ANSI encoding, decode it to unicode using detection - # of BOM header or if there is no header try default - # codepage then fallback to utf-16, cp1252 - - if isinstance(srcString, str): - encoding_map = ( - ('\xef\xbb\xbf', 'utf-8'), - ('\xff\xfe\0\0', 'utf-32'), - ('\0\0\xfe\xff', 'UTF-32BE'), - ('\xff\xfe', 'utf-16'), - ('\xfe\xff', 'UTF-16BE')) - - for bom, encoding in encoding_map: - if srcString.startswith(bom): - codec_found = encoding - savebom = bom - - if codec_found is None: - logger.info("Unicode BOM not found in file %s.", path) - attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252") - - for page in attempt_codecs: - try: - logger.info("Attempting to decode file %s using %s page.", path, page) - srcString = unicode(srcString, page) - codec_found = page - logger.info("File %s decoded using %s page.", path, page) - except UnicodeDecodeError: - logger.info("Error unicode decoding %s from page %s, trying next codec", path, page) - else: - break - else: - logger.info("Unicode BOM detected in %s, using %s page.", path, codec_found) - srcString = unicode(srcString[len(savebom):], codec_found) - - else: - # nasty hack to detect other transparent utf-16 loading - if srcString[0] == '<' and 'utf-16' in srcString[:128].lower(): - codec_found = "utf-16" - else: - codec_found = "utf-8" - - if codec_found is None: - return False, "Proper codec could not be established for %s" % path - - # TODO: port this to port.py - ''' - try: - _, fitsImport = Port.importAuto(srcString, path, callback=callback, encoding=codec_found) - fits += fitsImport - except xml.parsers.expat.ExpatError: - return False, "Malformed XML in %s" % path - except Exception: - logger.exception("Unknown exception processing: %s", path) - return False, "Unknown Error while processing %s" % path - ''' - - IDs = [] - numFits = len(fits) - for i, fit in enumerate(fits): - # Set some more fit attributes and save - fit.character = self.character - fit.damagePattern = self.pattern - fit.targetResists = self.targetResists - eos.db.save(fit) - IDs.append(fit.ID) - if callback: # Pulse - wx.CallAfter( - callback, 1, - "Processing complete, saving fits to database\n(%d/%d)" % - (i + 1, numFits) - ) - - return True, fits - - # TODO: port this to port.py - ''' - def importFitFromBuffer(self, bufferStr, activeFit=None): - _, fits = Port.importAuto(bufferStr, activeFit=activeFit) - for fit in fits: - fit.character = self.character - fit.damagePattern = self.pattern - fit.targetResists = self.targetResists - eos.db.save(fit) - return fits - ''' - def checkStates(self, fit, base): changed = False for mod in fit.modules: diff --git a/service/implantSet.py b/service/implantSet.py index e65b2e708..dcf954be2 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -20,8 +20,9 @@ import copy import eos.db -import eos.types from service.market import Market +from eos.saveddata.implant import Implant as es_Implant +from eos.saveddata.implantSet import ImplantSet as es_ImplantSet class ImportError(Exception): pass @@ -47,7 +48,7 @@ class ImplantSets(): def addImplant(self, setID, itemID): set = eos.db.getImplantSet(setID) - implant = eos.types.Implant(eos.db.getItem(itemID)) + implant = es_Implant(eos.db.getItem(itemID)) set.implants.append(implant) eos.db.commit() @@ -57,7 +58,7 @@ class ImplantSets(): eos.db.commit() def newSet(self, name): - s = eos.types.ImplantSet() + s = es_ImplantSet() s.name = name eos.db.save(s) return s @@ -91,11 +92,11 @@ class ImplantSets(): if line == '' or line[0] == "#": # comments / empty string continue if line[:1] == "[" and line[-1:] == "]": - current = eos.types.ImplantSet(line[1:-1]) + current = es_ImplantSet(line[1:-1]) newSets.append(current) else: item = sMkt.getItem(line) - current.implants.append(eos.types.Implant(item)) + current.implants.append(es_Implant(item)) except: errors += 1 continue @@ -107,7 +108,7 @@ class ImplantSets(): if set.name in lookup: match = lookup[set.name] for implant in set.implants: - match.implants.append(eos.types.Implant(implant.item)) + match.implants.append(es_Implant(implant.item)) else: eos.db.save(set) @@ -122,4 +123,4 @@ class ImplantSets(): def exportSets(self): patterns = self.getImplantSetList() patterns.sort(key=lambda p: p.name) - return eos.types.ImplantSet.exportSets(*patterns) + return es_ImplantSet.exportSets(*patterns) diff --git a/service/market.py b/service/market.py index 4a297f00d..4ea0ee5ca 100644 --- a/service/market.py +++ b/service/market.py @@ -27,11 +27,12 @@ from sqlalchemy.sql import or_ import config import eos.db -import eos.types from service import conversions from service.settings import SettingsProvider from service.price import Price +from eos.gamedata import Category as e_Category, Group as e_Group, Item as e_Item + try: from collections import OrderedDict except ImportError: @@ -126,14 +127,14 @@ class SearchWorkerThread(threading.Thread): sMkt = Market.getInstance() if filterOn is True: # Rely on category data provided by eos as we don't hardcode them much in service - filter = or_(eos.types.Category.name.in_(sMkt.SEARCH_CATEGORIES), eos.types.Group.name.in_(sMkt.SEARCH_GROUPS)) + filter = or_(e_Category.name.in_(sMkt.SEARCH_CATEGORIES), e_Group.name.in_(sMkt.SEARCH_GROUPS)) elif filterOn: # filter by selected categories - filter = eos.types.Category.name.in_(filterOn) + filter = e_Category.name.in_(filterOn) else: filter=None results = eos.db.searchItems(request, where=filter, - join=(eos.types.Item.group, eos.types.Group.category), + join=(e_Item.group, e_Group.category), eager=("icon", "group.category", "metaGroup", "metaGroup.parent")) items = set() @@ -177,7 +178,7 @@ class Market(): # Items' group overrides self.customGroups = set() # Limited edition ships - self.les_grp = eos.types.Group() + self.les_grp = e_Group() self.les_grp.ID = -1 self.les_grp.name = "Limited Issue Ships" self.les_grp.published = True @@ -364,7 +365,7 @@ class Market(): def getItem(self, identity, *args, **kwargs): """Get item by its ID or name""" try: - if isinstance(identity, eos.types.Item): + if isinstance(identity, e_Item): item = identity elif isinstance(identity, int): item = eos.db.getItem(identity, *args, **kwargs) @@ -386,7 +387,7 @@ class Market(): def getGroup(self, identity, *args, **kwargs): """Get group by its ID or name""" - if isinstance(identity, eos.types.Group): + if isinstance(identity, e_Group): return identity elif isinstance(identity, (int, float, basestring)): if isinstance(identity, float): @@ -404,7 +405,7 @@ class Market(): def getCategory(self, identity, *args, **kwargs): """Get category by its ID or name""" - if isinstance(identity, eos.types.Category): + if isinstance(identity, e_Category): category = identity elif isinstance(identity, (int, basestring)): category = eos.db.getCategory(identity, *args, **kwargs) @@ -689,9 +690,9 @@ class Market(): def searchShips(self, name): """Find ships according to given text pattern""" - filter = eos.types.Category.name.in_(["Ship", "Structure"]) + filter = e_Category.name.in_(["Ship", "Structure"]) results = eos.db.searchItems(name, where=filter, - join=(eos.types.Item.group, eos.types.Group.category), + join=(e_Item.group, e_Group.category), eager=("icon", "group.category", "metaGroup", "metaGroup.parent")) ships = set() for item in results: @@ -766,7 +767,7 @@ class Market(): def cb(): try: callback(requests) - except Exception: + except Exception, e: pass eos.db.commit() diff --git a/service/port.py b/service/port.py index 9c662f706..38257fda8 100644 --- a/service/port.py +++ b/service/port.py @@ -23,23 +23,18 @@ import xml.dom import logging import collections import json +import threading +import locale + +from codecs import open + +import xml.parsers.expat + +from eos import db import wx -from eos.types import State, Slot, Module, Cargo, Fit, Ship, Drone, Implant, Booster, Citadel -from service.crest import Crest -from service.market import Market - -import wx - -from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel -from service.crest import Crest -from service.market import Market -from service.fit import Fit - -import wx - -from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel +from eos.types import Fit, State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel from service.crest import Crest from service.market import Market from service.fit import Fit @@ -63,6 +58,116 @@ INV_FLAG_CARGOBAY = 5 INV_FLAG_DRONEBAY = 87 class Port(object): + def backupFits(self, path, callback): + thread = FitBackupThread(path, callback) + thread.start() + + def importFitsThreaded(self, paths, callback): + thread = FitImportThread(paths, callback) + thread.start() + + def importFitFromFiles(self, paths, callback=None): + """ + Imports fits from file(s). First processes all provided paths and stores + assembled fits into a list. This allows us to call back to the GUI as + fits are processed as well as when fits are being saved. + returns + """ + defcodepage = locale.getpreferredencoding() + + fits = [] + for path in paths: + if callback: # Pulse + wx.CallAfter(callback, 1, "Processing file:\n%s" % path) + + file = open(path, "r") + srcString = file.read() + + if len(srcString) == 0: # ignore blank files + continue + + codec_found = None + # If file had ANSI encoding, decode it to unicode using detection + # of BOM header or if there is no header try default + # codepage then fallback to utf-16, cp1252 + + if isinstance(srcString, str): + encoding_map = ( + ('\xef\xbb\xbf', 'utf-8'), + ('\xff\xfe\0\0', 'utf-32'), + ('\0\0\xfe\xff', 'UTF-32BE'), + ('\xff\xfe', 'utf-16'), + ('\xfe\xff', 'UTF-16BE')) + + for bom, encoding in encoding_map: + if srcString.startswith(bom): + codec_found = encoding + savebom = bom + + if codec_found is None: + logger.info("Unicode BOM not found in file %s.", path) + attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252") + + for page in attempt_codecs: + try: + logger.info("Attempting to decode file %s using %s page.", path, page) + srcString = unicode(srcString, page) + codec_found = page + logger.info("File %s decoded using %s page.", path, page) + except UnicodeDecodeError: + logger.info("Error unicode decoding %s from page %s, trying next codec", path, page) + else: + break + else: + logger.info("Unicode BOM detected in %s, using %s page.", path, codec_found) + srcString = unicode(srcString[len(savebom):], codec_found) + + else: + # nasty hack to detect other transparent utf-16 loading + if srcString[0] == '<' and 'utf-16' in srcString[:128].lower(): + codec_found = "utf-16" + else: + codec_found = "utf-8" + + if codec_found is None: + return False, "Proper codec could not be established for %s" % path + + try: + _, fitsImport = Port.importAuto(srcString, path, callback=callback, encoding=codec_found) + fits += fitsImport + except xml.parsers.expat.ExpatError: + return False, "Malformed XML in %s" % path + except Exception: + logger.exception("Unknown exception processing: %s", path) + return False, "Unknown Error while processing %s" % path + + IDs = [] + numFits = len(fits) + for i, fit in enumerate(fits): + # Set some more fit attributes and save + fit.character = self.character + fit.damagePattern = self.pattern + fit.targetResists = self.targetResists + db.save(fit) + IDs.append(fit.ID) + if callback: # Pulse + wx.CallAfter( + callback, 1, + "Processing complete, saving fits to database\n(%d/%d)" % + (i + 1, numFits) + ) + + return True, fits + + def importFitFromBuffer(self, bufferStr, activeFit=None): + _, fits = Port.importAuto(bufferStr, activeFit=activeFit) + for fit in fits: + fit.character = self.character + fit.damagePattern = self.pattern + fit.targetResists = self.targetResists + db.save(fit) + return fits + """Service which houses all import/export format functions""" @classmethod def exportCrest(cls, ofit, callback=None): @@ -948,3 +1053,38 @@ class Port(object): export = export[:-1] return export + + +class FitBackupThread(threading.Thread): + def __init__(self, path, callback): + threading.Thread.__init__(self) + self.path = path + self.callback = callback + + def run(self): + path = self.path + sFit = Fit.getInstance() + allFits = map(lambda x: x[0], sFit.getAllFits()) + backedUpFits = sFit.exportXml(self.callback, *allFits) + backupFile = open(path, "w", encoding="utf-8") + backupFile.write(backedUpFits) + backupFile.close() + + # Send done signal to GUI + wx.CallAfter(self.callback, -1) + +class FitImportThread(threading.Thread): + def __init__(self, paths, callback): + threading.Thread.__init__(self) + self.paths = paths + self.callback = callback + + def run(self): + sFit = Fit.getInstance() + success, result = sFit.importFitFromFiles(self.paths, self.callback) + + if not success: # there was an error during processing + logger.error("Error while processing file import: %s", result) + wx.CallAfter(self.callback, -2, result) + else: # Send done signal to GUI + wx.CallAfter(self.callback, -1, result) diff --git a/service/prefetch.py b/service/prefetch.py index f3d09c43c..30cec80a1 100644 --- a/service/prefetch.py +++ b/service/prefetch.py @@ -21,16 +21,17 @@ import threading import os import config -import eos.types +from eos import db from eos.db import migration from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues +from eos.saveddata.character import Character as es_Character class PrefetchThread(threading.Thread): def run(self): # We're a daemon thread, as such, interpreter might get shut down while we do stuff # Make sure we don't throw tracebacks to console try: - eos.types.Character.setSkillList(eos.db.getItemsByCategory("Skill", eager=("effects", "attributes", "attributes.info.icon", "attributes.info.unit", "icon"))) + es_Character.setSkillList(db.getItemsByCategory("Skill", eager=("effects", "attributes", "attributes.info.icon", "attributes.info.unit", "icon"))) except: pass @@ -51,16 +52,16 @@ if config.savePath and not os.path.exists(config.savePath): if config.saveDB and os.path.isfile(config.saveDB): # If database exists, run migration after init'd database - eos.db.saveddata_meta.create_all() - migration.update(eos.db.saveddata_engine) + db.saveddata_meta.create_all() + migration.update(db.saveddata_engine) # Import default database values # Import values that must exist otherwise Pyfa breaks DefaultDatabaseValues.importRequiredDefaults() elif config.saveDB: # If database does not exist, do not worry about migration. Simply # create and set version - eos.db.saveddata_meta.create_all() - eos.db.saveddata_engine.execute('PRAGMA user_version = {}'.format(migration.getAppVersion())) + db.saveddata_meta.create_all() + db.saveddata_engine.execute('PRAGMA user_version = {}'.format(migration.getAppVersion())) # Import default database values # Import values that must exist otherwise Pyfa breaks DefaultDatabaseValues.importRequiredDefaults() diff --git a/service/price.py b/service/price.py index f2c9c9bd9..fc2bf9bbf 100644 --- a/service/price.py +++ b/service/price.py @@ -21,7 +21,7 @@ import time from xml.dom import minidom -import eos +from eos import db from service.network import Network, TimeoutError VALIDITY = 24*60*60 # Price validity period, 24 hours @@ -50,7 +50,7 @@ class Price(): # Compose list of items we're going to request for typeID in priceMap: # Get item object - item = eos.db.getItem(typeID) + item = db.getItem(typeID) # We're not going to request items only with market group, as eve-central # doesn't provide any data for items not on the market if item is not None and item.marketGroupID: diff --git a/service/settings.py b/service/settings.py index bf70b8705..186193033 100644 --- a/service/settings.py +++ b/service/settings.py @@ -36,7 +36,7 @@ class SettingsProvider(): def __init__(self): if not os.path.exists(self.BASE_PATH): - os.mkdir(self.BASE_PATH); + os.mkdir(self.BASE_PATH) def getSettings(self, area, defaults=None): diff --git a/service/targetResists.py b/service/targetResists.py index 26279ba6b..b98ec8b16 100644 --- a/service/targetResists.py +++ b/service/targetResists.py @@ -19,8 +19,8 @@ import copy -import eos -from eos.saveddata import targetResists as db_targetResists +from eos import db +from eos.saveddata.targetResists import TargetResists as es_TargetResists class ImportError(Exception): @@ -36,31 +36,31 @@ class TargetResists(object): return cls.instance def getTargetResistsList(self): - return eos.db.getTargetResistsList() + return db.getTargetResistsList() def getTargetResists(self, name): - return eos.db.getTargetResists(name) + return db.getTargetResists(name) def newPattern(self, name): - p = eos.types.TargetResists(0.0, 0.0, 0.0, 0.0) + p = es_TargetResists(0.0, 0.0, 0.0, 0.0) p.name = name - eos.db.save(p) + db.save(p) return p def renamePattern(self, p, newName): p.name = newName - eos.db.save(p) + db.save(p) def deletePattern(self, p): - eos.db.remove(p) + db.remove(p) def copyPattern(self, p): newP = copy.deepcopy(p) - eos.db.save(newP) + db.save(newP) return newP def saveChanges(self, p): - eos.db.save(p) + db.save(p) def importPatterns(self, text): lookup = {} @@ -68,14 +68,14 @@ class TargetResists(object): for pattern in current: lookup[pattern.name] = pattern - imports, num = eos.types.TargetResists.importPatterns(text) + imports, num = es_TargetResists.importPatterns(text) for pattern in imports: if pattern.name in lookup: match = lookup[pattern.name] match.__dict__.update(pattern.__dict__) else: - eos.db.save(pattern) - eos.db.commit() + db.save(pattern) + db.commit() lenImports = len(imports) if lenImports == 0: @@ -86,4 +86,4 @@ class TargetResists(object): def exportPatterns(self): patterns = self.getTargetResistsList() patterns.sort(key=lambda p: p.name) - return db_targetResists.TargetResists.exportPatterns(*patterns) + return es_TargetResists.exportPatterns(*patterns) From d3b6bc1c93ae4a7454295eb18342abb47db2b616 Mon Sep 17 00:00:00 2001 From: a-tal Date: Sat, 3 Dec 2016 17:04:12 -0800 Subject: [PATCH 05/53] so many pep8 fixes (cherry picked from commit bee125d) --- config.py | 40 +- gui/PFListPane.py | 38 +- gui/PFSearchBox.py | 49 +- gui/aboutData.py | 8 +- gui/additionsPane.py | 29 +- gui/bitmapLoader.py | 13 +- gui/boosterView.py | 37 +- gui/builtinContextMenus/ammoPattern.py | 71 +-- gui/builtinContextMenus/amount.py | 1 + gui/builtinContextMenus/cargo.py | 1 + .../changeAffectingSkills.py | 1 + gui/builtinContextMenus/damagePattern.py | 1 + gui/builtinContextMenus/droneRemoveStack.py | 1 + gui/builtinContextMenus/droneSplit.py | 119 ++-- gui/builtinContextMenus/fighterAbilities.py | 1 + gui/builtinContextMenus/itemStats.py | 126 ++--- gui/builtinContextMenus/marketJump.py | 97 ++-- gui/builtinContextMenus/metaSwap.py | 15 +- gui/builtinContextMenus/moduleAmmoPicker.py | 450 ++++++++-------- gui/builtinContextMenus/openFit.py | 1 + gui/builtinContextMenus/priceClear.py | 1 + gui/builtinContextMenus/project.py | 75 +-- gui/builtinContextMenus/shipJump.py | 1 + gui/builtinContextMenus/tacticalMode.py | 1 + gui/builtinContextMenus/targetResists.py | 1 + gui/builtinContextMenus/whProjector.py | 1 + gui/builtinGraphs/fitDps.py | 8 +- gui/builtinPreferenceViews/__init__.py | 6 +- gui/builtinPreferenceViews/dummyView.py | 75 +-- .../pyfaCrestPreferences.py | 87 +-- .../pyfaGaugePreferences.py | 265 +++++---- .../pyfaGeneralPreferences.py | 86 +-- .../pyfaHTMLExportPreferences.py | 74 ++- .../pyfaNetworkPreferences.py | 125 +++-- .../pyfaUpdatePreferences.py | 87 ++- gui/builtinStatsViews/capacitorViewFull.py | 22 +- gui/builtinStatsViews/firepowerViewFull.py | 5 +- gui/builtinStatsViews/miningyieldViewFull.py | 5 +- gui/builtinStatsViews/priceViewFull.py | 16 +- gui/builtinStatsViews/rechargeViewFull.py | 25 +- gui/builtinStatsViews/resistancesViewFull.py | 5 +- gui/builtinStatsViews/resourcesViewFull.py | 119 ++-- .../targetingMiscViewFull.py | 50 +- gui/builtinViewColumns/abilities.py | 11 +- gui/builtinViewColumns/ammo.py | 14 +- gui/builtinViewColumns/ammoIcon.py | 9 +- gui/builtinViewColumns/attributeDisplay.py | 19 +- gui/builtinViewColumns/baseIcon.py | 15 +- gui/builtinViewColumns/baseName.py | 20 +- gui/builtinViewColumns/capacitorUse.py | 14 +- gui/builtinViewColumns/maxRange.py | 25 +- gui/builtinViewColumns/misc.py | 42 +- gui/builtinViewColumns/price.py | 21 +- gui/builtinViewColumns/propertyDisplay.py | 142 ++--- gui/builtinViewColumns/state.py | 14 +- gui/builtinViews/emptyView.py | 7 +- gui/builtinViews/fittingView.py | 93 ++-- gui/builtinViews/fleetView.py | 281 +++++----- gui/builtinViews/implantEditor.py | 34 +- gui/cachingImageList.py | 14 +- gui/cargoView.py | 32 +- gui/characterEditor.py | 4 +- gui/characterSelection.py | 61 ++- gui/chromeTabs.py | 107 ++-- gui/commandView.py | 51 +- gui/contextMenu.py | 10 +- gui/copySelectDialog.py | 16 +- gui/display.py | 81 ++- gui/droneView.py | 61 +-- gui/fighterView.py | 4 +- gui/fleetBrowser.py | 124 ++--- gui/gangView.py | 246 ++++----- gui/globalEvents.py | 1 + gui/graph.py | 8 +- gui/graphFrame.py | 55 +- gui/implantView.py | 22 +- gui/itemStats.py | 412 ++++++-------- gui/mainFrame.py | 268 ++++----- gui/mainMenuBar.py | 29 +- gui/marketBrowser.py | 41 +- gui/multiSwitch.py | 6 +- gui/notesView.py | 5 +- gui/patternEditor.py | 68 ++- gui/preferenceDialog.py | 32 +- gui/preferenceView.py | 9 +- gui/projectedView.py | 52 +- gui/propertyEditor.py | 59 +- gui/pyfatogglepanel.py | 133 +++-- gui/pygauge.py | 123 ++--- gui/resistsEditor.py | 55 +- gui/setEditor.py | 48 +- gui/sfBrowserItem.py | 60 +-- gui/shipBrowser.py | 509 +++++++++--------- gui/statsPane.py | 32 +- gui/statsView.py | 9 +- gui/updateDialog.py | 4 +- gui/utils/animEffects.py | 157 +++--- gui/utils/animUtils.py | 26 +- gui/utils/clipboard.py | 4 +- gui/utils/colorUtils.py | 63 +-- gui/utils/compat.py | 2 +- gui/utils/drawUtils.py | 84 +-- gui/utils/exportHtml.py | 51 +- gui/utils/fonts.py | 2 + gui/utils/numberFormatter.py | 10 +- gui/viewColumn.py | 9 +- service/attribute.py | 4 +- service/character.py | 21 +- service/conversions/releaseApril2016.py | 2 +- service/conversions/releaseDecember15.py | 2 +- service/conversions/releaseFeb2016.py | 1 - service/conversions/releaseJan2016.py | 2 +- service/conversions/releaseMar2016.py | 2 +- service/damagePattern.py | 8 +- service/eveapi.py | 103 ++-- service/fit.py | 16 +- service/fleet.py | 4 +- service/implantSet.py | 56 +- service/network.py | 9 +- service/port.py | 34 +- service/prefetch.py | 7 +- service/price.py | 14 +- service/pycrest/__init__.py | 1 + service/pycrest/compat.py | 2 +- service/pycrest/errors.py | 2 +- service/pycrest/eve.py | 8 +- service/pycrest/weak_ciphers.py | 42 +- service/server.py | 5 +- service/settings.py | 51 +- service/targetResists.py | 6 +- service/update.py | 20 +- utils/compat.py | 2 +- utils/timer.py | 7 +- 133 files changed, 3371 insertions(+), 3319 deletions(-) diff --git a/config.py b/config.py index 9e3dc3d36..fdf6a18a9 100644 --- a/config.py +++ b/config.py @@ -31,18 +31,19 @@ gameDB = None class StreamToLogger(object): - """ - Fake file-like stream object that redirects writes to a logger instance. - From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ - """ - def __init__(self, logger, log_level=logging.INFO): - self.logger = logger - self.log_level = log_level - self.linebuf = '' + """ + Fake file-like stream object that redirects writes to a logger instance. + From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ + """ + def __init__(self, logger, log_level=logging.INFO): + self.logger = logger + self.log_level = log_level + self.linebuf = '' + + def write(self, buf): + for line in buf.rstrip().splitlines(): + self.logger.log(self.log_level, line.rstrip()) - def write(self, buf): - for line in buf.rstrip().splitlines(): - self.logger.log(self.log_level, line.rstrip()) def isFrozen(): if hasattr(sys, 'frozen'): @@ -50,16 +51,19 @@ def isFrozen(): else: return False + def getPyfaRoot(): base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] root = os.path.dirname(os.path.realpath(os.path.abspath(base))) root = unicode(root, sys.getfilesystemencoding()) return root + def __createDirs(path): if not os.path.exists(path): os.makedirs(path) + def defPaths(customSavePath): global debug global pyfaPath @@ -87,9 +91,9 @@ def defPaths(customSavePath): else: savePath = getattr(configforced, "savePath", None) if savePath is None: - if customSavePath is None: # customSavePath is not overriden + if customSavePath is None: # customSavePath is not overriden savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")), - sys.getfilesystemencoding()) + sys.getfilesystemencoding()) else: savePath = customSavePath @@ -114,9 +118,9 @@ def defPaths(customSavePath): sys.stdout = sl # This interferes with cx_Freeze's own handling of exceptions. Find a way to fix this. - #stderr_logger = logging.getLogger('STDERR') - #sl = StreamToLogger(stderr_logger, logging.ERROR) - #sys.stderr = sl + # stderr_logger = logging.getLogger('STDERR') + # sl = StreamToLogger(stderr_logger, logging.ERROR) + # sys.stderr = sl # The database where we store all the fits etc saveDB = os.path.join(savePath, "saveddata.db") @@ -126,10 +130,10 @@ def defPaths(customSavePath): # maintenance script gameDB = os.path.join(pyfaPath, "eve.db") - ## DON'T MODIFY ANYTHING BELOW ## + # DON'T MODIFY ANYTHING BELOW! import eos.config - #Caching modifiers, disable all gamedata caching, its unneeded. + # Caching modifiers, disable all gamedata caching, its unneeded. eos.config.gamedataCache = False # saveddata db location modifier, shouldn't ever need to touch this eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False" diff --git a/gui/PFListPane.py b/gui/PFListPane.py index 9385dbc75..9803f6873 100644 --- a/gui/PFListPane.py +++ b/gui/PFListPane.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,13 +15,14 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx + class PFListPane(wx.ScrolledWindow): def __init__(self, parent): - wx.ScrolledWindow.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(1, 1), style=wx.TAB_TRAVERSAL) + wx.ScrolledWindow.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(1, 1), style=wx.TAB_TRAVERSAL) self._wList = [] self._wCount = 0 @@ -29,19 +30,18 @@ class PFListPane(wx.ScrolledWindow): self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - self.SetVirtualSize((1, 1)) self.SetScrollRate(0, 1) self.Bind(wx.EVT_SCROLLWIN_LINEUP, self.MScrollUp) self.Bind(wx.EVT_SCROLLWIN_LINEDOWN, self.MScrollDown) -# self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus) -# self.Bind(wx.EVT_LEFT_DOWN, self.ForceFocus) + # self.Bind(wx.EVT_CHILD_FOCUS, self.OnChildFocus) + # self.Bind(wx.EVT_LEFT_DOWN, self.ForceFocus) self.SetFocus() -# self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self.ForceFocus) + # self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self.ForceFocus) self.Bind(wx.EVT_SCROLLWIN_THUMBRELEASE, self.ForceFocus) - def ForceFocus(self,event): + def ForceFocus(self, event): if self.FindFocus() and self.FindFocus().Parent != self: self.SetFocus() event.Skip() @@ -67,13 +67,12 @@ class PFListPane(wx.ScrolledWindow): event.Skip() - def ScrollChildIntoView(self, child): """ Scrolls the panel such that the specified child window is in view. """ sppu_x, sppu_y = self.GetScrollPixelsPerUnit() - vs_x, vs_y = self.GetViewStart() + vs_x, vs_y = self.GetViewStart() cr = child.GetRect() clntsz = self.GetSize() new_vs_x, new_vs_y = -1, -1 @@ -110,8 +109,6 @@ class PFListPane(wx.ScrolledWindow): if new_vs_x != -1 or new_vs_y != -1: self.Scroll(new_vs_x, new_vs_y) - - def AddWidget(self, widget): widget.Reparent(self) self._wList.append(widget) @@ -124,13 +121,11 @@ class PFListPane(wx.ScrolledWindow): def IsWidgetSelectedByContext(self, widget): return False - def RefreshList(self, doRefresh = False, doFocus = False): - ypos = 0 + def RefreshList(self, doRefresh=False, doFocus=False): maxy = 0 - scrollTo = 0 selected = None - for i in xrange( len(self._wList) ): + for i in xrange(len(self._wList)): iwidth, iheight = self._wList[i].GetSize() xa, ya = self.CalcScrolledPosition((0, maxy)) self._wList[i].SetPosition((xa, ya)) @@ -143,16 +138,16 @@ class PFListPane(wx.ScrolledWindow): if selected: self.ScrollChildIntoView(selected) - #selected.SetFocus() + # selected.SetFocus() elif doFocus: self.SetFocus() - clientW,clientH = self.GetSize() - for i in xrange( len(self._wList) ): + clientW, clientH = self.GetSize() + for i in xrange(len(self._wList)): iwidth, iheight = self._wList[i].GetSize() - itemX,itemY = self._wList[i].GetPosition() + itemX, itemY = self._wList[i].GetPosition() self._wList[i].SetSize((cwidth, iheight)) - if doRefresh == True: + if doRefresh is True: self._wList[i].Refresh() self.itemsHeight = max(self.itemsHeight, iheight - 1) @@ -160,7 +155,6 @@ class PFListPane(wx.ScrolledWindow): child.Destroy() self._wList.remove(child) - def RemoveAllChildren(self): for widget in self._wList: widget.Destroy() diff --git a/gui/PFSearchBox.py b/gui/PFSearchBox.py index 7fff91e9b..452bdbf09 100644 --- a/gui/PFSearchBox.py +++ b/gui/PFSearchBox.py @@ -1,7 +1,6 @@ import wx import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils -from gui.bitmapLoader import BitmapLoader SearchButton, EVT_SEARCH_BTN = wx.lib.newevent.NewEvent() @@ -9,9 +8,10 @@ CancelButton, EVT_CANCEL_BTN = wx.lib.newevent.NewEvent() TextEnter, EVT_TEXT_ENTER = wx.lib.newevent.NewEvent() TextTyped, EVT_TEXT = wx.lib.newevent.NewEvent() + class PFSearchBox(wx.Window): - def __init__(self, parent, id = wx.ID_ANY, value = "", pos = wx.DefaultPosition, size = wx.Size(-1,24), style = 0): - wx.Window.__init__(self, parent, id, pos, size, style = style) + def __init__(self, parent, id=wx.ID_ANY, value="", pos=wx.DefaultPosition, size=wx.Size(-1, 24), style=0): + wx.Window.__init__(self, parent, id, pos, size, style=style) self.isSearchButtonVisible = False self.isCancelButtonVisible = False @@ -35,13 +35,12 @@ class PFSearchBox(wx.Window): self.editX = 0 self.editY = 0 - self.padding = 4 self._hl = False - w,h = size - self.EditBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1 ), wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) + w, h = size + self.EditBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1), wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk) @@ -50,7 +49,7 @@ class PFSearchBox(wx.Window): self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) -# self.EditBox.ChangeValue(self.descriptiveText) + # self.EditBox.ChangeValue(self.descriptiveText) self.EditBox.Bind(wx.EVT_SET_FOCUS, self.OnEditSetFocus) self.EditBox.Bind(wx.EVT_KILL_FOCUS, self.OnEditKillFocus) @@ -68,11 +67,10 @@ class PFSearchBox(wx.Window): wx.PostEvent(self, TextEnter()) event.Skip() - def OnEditSetFocus(self, event): -# value = self.EditBox.GetValue() -# if value == self.descriptiveText: -# self.EditBox.ChangeValue("") + # value = self.EditBox.GetValue() + # if value == self.descriptiveText: + # self.EditBox.ChangeValue("") event.Skip() def OnEditKillFocus(self, event): @@ -80,10 +78,9 @@ class PFSearchBox(wx.Window): self.Clear() event.Skip() - def Clear(self): self.EditBox.Clear() -# self.EditBox.ChangeValue(self.descriptiveText) + # self.EditBox.ChangeValue(self.descriptiveText) def Focus(self): self.EditBox.SetFocus() @@ -110,8 +107,8 @@ class PFSearchBox(wx.Window): def GetButtonsPos(self): btnpos = [] - btnpos.append( (self.searchButtonX, self.searchButtonY) ) - btnpos.append( (self.cancelButtonX, self.cancelButtonY) ) + btnpos.append((self.searchButtonX, self.searchButtonY)) + btnpos.append((self.cancelButtonX, self.cancelButtonY)) return btnpos def GetButtonsSize(self): @@ -131,8 +128,8 @@ class PFSearchBox(wx.Window): cw = 0 ch = 0 - btnsize.append( (sw,sh)) - btnsize.append( (cw,ch)) + btnsize.append((sw, sh)) + btnsize.append((cw, ch)) return btnsize def OnLeftDown(self, event): @@ -140,7 +137,7 @@ class PFSearchBox(wx.Window): btnsize = self.GetButtonsSize() self.CaptureMouse() - for btn in xrange(2): + for btn in range(2): if self.HitTest(btnpos[btn], event.GetPosition(), btnsize[btn]): if btn == 0: if not self.searchButtonPressed: @@ -158,7 +155,7 @@ class PFSearchBox(wx.Window): if self.HasCapture(): self.ReleaseMouse() - for btn in xrange(2): + for btn in range(2): if self.HitTest(btnpos[btn], event.GetPosition(), btnsize[btn]): if btn == 0: if self.searchButtonPressed: @@ -218,9 +215,9 @@ class PFSearchBox(wx.Window): editWidth, editHeight = self.EditBox.GetSize() - self.editY = (cheight - editHeight)/2 + self.editY = (cheight - editHeight) / 2 self.EditBox.SetPosition((self.editX, self.editY)) - self.EditBox.SetSize( (self.cancelButtonX - self.padding - self.editX, -1)) + self.EditBox.SetSize((self.cancelButtonX - self.padding - self.editX, -1)) def OnPaint(self, event): dc = wx.BufferedPaintDC(self) @@ -246,7 +243,6 @@ class PFSearchBox(wx.Window): dc.DrawBitmap(self.searchBitmapShadow, self.searchButtonX + 1, self.searchButtonY + 1) dc.DrawBitmap(self.searchBitmap, self.searchButtonX + spad, self.searchButtonY + spad) - if self.isCancelButtonVisible: if self.cancelBitmap: if self.cancelButtonPressed: @@ -256,8 +252,8 @@ class PFSearchBox(wx.Window): dc.DrawBitmap(self.cancelBitmapShadow, self.cancelButtonX + 1, self.cancelButtonY + 1) dc.DrawBitmap(self.cancelBitmap, self.cancelButtonX + cpad, self.cancelButtonY + cpad) - dc.SetPen(wx.Pen(sepColor,1)) - dc.DrawLine(0,rect.height - 1, rect.width, rect.height - 1) + dc.SetPen(wx.Pen(sepColor, 1)) + dc.DrawLine(0, rect.height - 1, rect.width, rect.height - 1) def SetSearchBitmap(self, bitmap): self.searchBitmap = bitmap @@ -273,10 +269,10 @@ class PFSearchBox(wx.Window): def IsCancelButtonVisible(self): return self.isCancelButtonVisible - def ShowSearchButton(self, show = True): + def ShowSearchButton(self, show=True): self.isSearchButtonVisible = show - def ShowCancelButton(self, show = True): + def ShowCancelButton(self, show=True): self.isCancelButtonVisible = show def SetDescriptiveText(self, text): @@ -284,4 +280,3 @@ class PFSearchBox(wx.Window): def GetDescriptiveText(self): return self.descriptiveText - diff --git a/gui/aboutData.py b/gui/aboutData.py index be29b6298..98e4c12f7 100644 --- a/gui/aboutData.py +++ b/gui/aboutData.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,9 +15,11 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + import config + versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion) licenses = ( "pyfa is released under GNU GPLv3 - see included LICENSE file", @@ -27,7 +29,7 @@ licenses = ( ) developers = ( "blitzmann \tSable Blitzmann (maintainer)", - "cncfanatics \tSakari Orisi (retired)" , + "cncfanatics \tSakari Orisi (retired)", "DarkPhoenix \tKadesh Priestess (retired)", "Darriele \t\tDarriele (retired)", "Ebag333 \t\tEbag Trescientas" diff --git a/gui/additionsPane.py b/gui/additionsPane.py index c7b28b48b..2e7e459e3 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import gui.mainFrame @@ -29,16 +29,16 @@ from gui.projectedView import ProjectedView from gui.commandView import CommandView from gui.notesView import NotesView from gui.pyfatogglepanel import TogglePanel -from gui.gangView import GangView from gui.bitmapLoader import BitmapLoader import gui.chromeTabs + class AdditionsPane(TogglePanel): def __init__(self, parent): - TogglePanel.__init__(self, parent, forceLayout = 1) + TogglePanel.__init__(self, parent, forceLayout=1) self.SetLabel("Additions") pane = self.GetContentPane() @@ -63,28 +63,28 @@ class AdditionsPane(TogglePanel): notesImg = BitmapLoader.getImage("skill_small", "gui") self.drone = DroneView(self.notebook) - self.notebook.AddPage(self.drone, "Drones", tabImage = droneImg, showClose = False) + self.notebook.AddPage(self.drone, "Drones", tabImage=droneImg, showClose=False) self.fighter = FighterView(self.notebook) - self.notebook.AddPage(self.fighter, "Fighters", tabImage = fighterImg, showClose = False) + self.notebook.AddPage(self.fighter, "Fighters", tabImage=fighterImg, showClose=False) self.cargo = CargoView(self.notebook) - self.notebook.AddPage(self.cargo, "Cargo", tabImage = cargoImg, showClose = False) + self.notebook.AddPage(self.cargo, "Cargo", tabImage=cargoImg, showClose=False) self.implant = ImplantView(self.notebook) - self.notebook.AddPage(self.implant, "Implants", tabImage = implantImg, showClose = False) + self.notebook.AddPage(self.implant, "Implants", tabImage=implantImg, showClose=False) self.booster = BoosterView(self.notebook) - self.notebook.AddPage(self.booster, "Boosters", tabImage = boosterImg, showClose = False) + self.notebook.AddPage(self.booster, "Boosters", tabImage=boosterImg, showClose=False) self.projectedPage = ProjectedView(self.notebook) - self.notebook.AddPage(self.projectedPage, "Projected", tabImage = projectedImg, showClose = False) + self.notebook.AddPage(self.projectedPage, "Projected", tabImage=projectedImg, showClose=False) self.gangPage = CommandView(self.notebook) - self.notebook.AddPage(self.gangPage, "Command", tabImage = gangImg, showClose = False) + self.notebook.AddPage(self.gangPage, "Command", tabImage=gangImg, showClose=False) self.notes = NotesView(self.notebook) - self.notebook.AddPage(self.notes, "Notes", tabImage = notesImg, showClose = False) + self.notebook.AddPage(self.notes, "Notes", tabImage=notesImg, showClose=False) self.notebook.SetSelection(0) @@ -101,12 +101,12 @@ class AdditionsPane(TogglePanel): def toggleContent(self, event): TogglePanel.toggleContent(self, event) - h = self.headerPanel.GetSize()[1]+4 + h = self.headerPanel.GetSize()[1] + 4 if self.IsCollapsed(): self.old_pos = self.parent.GetSashPosition() self.parent.SetMinimumPaneSize(h) - self.parent.SetSashPosition(h*-1, True) + self.parent.SetSashPosition(h * -1, True) # only available in >= wx2.9 if getattr(self.parent, "SetSashInvisible", None): self.parent.SetSashInvisible(True) @@ -115,4 +115,3 @@ class AdditionsPane(TogglePanel): self.parent.SetSashInvisible(False) self.parent.SetMinimumPaneSize(200) self.parent.SetSashPosition(self.old_pos, True) - diff --git a/gui/bitmapLoader.py b/gui/bitmapLoader.py index 45026bedb..fb8be8a02 100644 --- a/gui/bitmapLoader.py +++ b/gui/bitmapLoader.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import os.path import config @@ -28,7 +28,8 @@ try: except ImportError: from utils.compat import OrderedDict -class BitmapLoader(): + +class BitmapLoader(object): try: archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') @@ -42,7 +43,7 @@ class BitmapLoader(): @classmethod def getStaticBitmap(cls, name, parent, location): static = wx.StaticBitmap(parent) - static.SetBitmap(cls.getBitmap(name,location)) + static.SetBitmap(cls.getBitmap(name, location)) return static @classmethod @@ -83,11 +84,11 @@ class BitmapLoader(): sbuf = cStringIO.StringIO(img_data) return wx.ImageFromStream(sbuf) except KeyError: - print "Missing icon file from zip: {0}".format(path) + print("Missing icon file from zip: {0}".format(path)) else: path = os.path.join(config.pyfaPath, 'imgs', location, filename) if os.path.exists(path): return wx.Image(path) else: - print "Missing icon file: {0}".format(path) + print("Missing icon file: {0}".format(path)) diff --git a/gui/boosterView.py b/gui/boosterView.py index d7e52a4ac..d4d631942 100644 --- a/gui/boosterView.py +++ b/gui/boosterView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import gui.display as d @@ -25,19 +25,21 @@ from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu from service.fit import Fit -class BoosterViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t +class BoosterViewDrop(wx.PyDropTarget): + def __init__(self, dropFn): + wx.PyDropTarget.__init__(self) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t + class BoosterView(d.Display): DEFAULT_COLS = ["State", @@ -58,7 +60,7 @@ class BoosterView(d.Display): self.SetDropTarget(BoosterViewDrop(self.handleListDrag)) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) @@ -75,7 +77,7 @@ class BoosterView(d.Display): if data[0] == "market": wx.PostEvent(self.mainFrame, mb.ItemSelected(itemID=int(data[1]))) - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() @@ -90,7 +92,7 @@ class BoosterView(d.Display): self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -154,7 +156,6 @@ class BoosterView(d.Display): sFit.toggleBooster(fitID, row) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - def scheduleMenu(self, event): event.Skip() if self.getColumn(event.Position) != self.getColIndex(State): diff --git a/gui/builtinContextMenus/ammoPattern.py b/gui/builtinContextMenus/ammoPattern.py index 5e38ec66c..be2833d9e 100644 --- a/gui/builtinContextMenus/ammoPattern.py +++ b/gui/builtinContextMenus/ammoPattern.py @@ -1,34 +1,37 @@ -from gui.contextMenu import ContextMenu -import gui.mainFrame -import wx -import gui.globalEvents as GE - -class AmmoPattern(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: - return False - - item = selection[0] - for attr in ("emDamage", "thermalDamage", "explosiveDamage", "kineticDamage"): - if item.getAttribute(attr) is not None: - return True - - return False - - def getText(self, itmContext, selection): - return "Set {0} as Damage Pattern".format(itmContext if itmContext is not None else "Item") - - def activate(self, fullContext, selection, i): - item = selection[0] - fit = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - sFit.setAsPattern(fit, item) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit)) - - def getBitmap(self, context, selection): - return None - -AmmoPattern.register() +from gui.contextMenu import ContextMenu +import gui.mainFrame +import wx +import gui.globalEvents as GE +from service.fit import Fit + + +class AmmoPattern(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: + return False + + item = selection[0] + for attr in ("emDamage", "thermalDamage", "explosiveDamage", "kineticDamage"): + if item.getAttribute(attr) is not None: + return True + + return False + + def getText(self, itmContext, selection): + return "Set {0} as Damage Pattern".format(itmContext if itmContext is not None else "Item") + + def activate(self, fullContext, selection, i): + item = selection[0] + fit = self.mainFrame.getActiveFit() + sFit = Fit.getInstance() + sFit.setAsPattern(fit, item) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fit)) + + def getBitmap(self, context, selection): + return None + + +AmmoPattern.register() diff --git a/gui/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py index ae9331932..a3186b723 100644 --- a/gui/builtinContextMenus/amount.py +++ b/gui/builtinContextMenus/amount.py @@ -23,6 +23,7 @@ class ChangeAmount(ContextMenu): dlg = AmountChanger(self.mainFrame, selection[0], srcContext) dlg.ShowModal() + ChangeAmount.register() class AmountChanger(wx.Dialog): diff --git a/gui/builtinContextMenus/cargo.py b/gui/builtinContextMenus/cargo.py index 3de85804d..8d1705ee6 100644 --- a/gui/builtinContextMenus/cargo.py +++ b/gui/builtinContextMenus/cargo.py @@ -32,4 +32,5 @@ class Cargo(ContextMenu): self.mainFrame.additionsPane.select("Cargo") wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + Cargo.register() diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index df7d896b0..d679a8791 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -100,4 +100,5 @@ class ChangeAffectingSkills(ContextMenu): wx.PostEvent(self.mainFrame, GE.CharListUpdated()) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + ChangeAffectingSkills.register() diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index df14e0c9b..553c02e04 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -105,4 +105,5 @@ class DamagePattern(ContextMenu): setattr(self.mainFrame,"_activeDmgPattern", pattern) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + DamagePattern.register() diff --git a/gui/builtinContextMenus/droneRemoveStack.py b/gui/builtinContextMenus/droneRemoveStack.py index bc608ea1e..af2ecd9ea 100644 --- a/gui/builtinContextMenus/droneRemoveStack.py +++ b/gui/builtinContextMenus/droneRemoveStack.py @@ -23,4 +23,5 @@ class ItemRemove(ContextMenu): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + ItemRemove.register() diff --git a/gui/builtinContextMenus/droneSplit.py b/gui/builtinContextMenus/droneSplit.py index 20cdf0caa..476e40eee 100644 --- a/gui/builtinContextMenus/droneSplit.py +++ b/gui/builtinContextMenus/droneSplit.py @@ -1,58 +1,61 @@ -from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import gui.mainFrame -import gui.globalEvents as GE -import wx - -class DroneSplit(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - return srcContext in ("droneItem", "projectedDrone") and selection[0].amount > 1 - - def getText(self, itmContext, selection): - return "Split {0} Stack".format(itmContext) - - def activate(self, fullContext, selection, i): - srcContext = fullContext[0] - dlg = DroneSpinner(self.mainFrame, selection[0], srcContext) - dlg.ShowModal() - dlg.Destroy() - -DroneSplit.register() - - -class DroneSpinner(wx.Dialog): - - def __init__(self, parent, drone, context): - wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60)) - self.drone = drone - self.context = context - - bSizer1 = wx.BoxSizer(wx.HORIZONTAL) - - self.spinner = wx.SpinCtrl(self) - self.spinner.SetRange(1, drone.amount - 1) - self.spinner.SetValue(1) - - bSizer1.Add(self.spinner, 0, wx.ALL, 5) - - self.button = wx.Button(self, wx.ID_OK, u"Split") - bSizer1.Add(self.button, 0, wx.ALL, 5) - - self.SetSizer(bSizer1) - self.Layout() - self.Centre(wx.BOTH) - self.button.Bind(wx.EVT_BUTTON, self.split) - - def split(self, event): - sFit = Fit.getInstance() - mainFrame = gui.mainFrame.MainFrame.getInstance() - fitID = mainFrame.getActiveFit() - if self.context == "droneItem": - sFit.splitDroneStack(fitID, self.drone, self.spinner.GetValue()) - else: - sFit.splitProjectedDroneStack(fitID, self.drone, self.spinner.GetValue()) - wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) - event.Skip() +from gui.contextMenu import ContextMenu +from gui.itemStats import ItemStatsDialog +import gui.mainFrame +import gui.globalEvents as GE +from service.fit import Fit +import wx + + +class DroneSplit(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + return srcContext in ("droneItem", "projectedDrone") and selection[0].amount > 1 + + def getText(self, itmContext, selection): + return "Split {0} Stack".format(itmContext) + + def activate(self, fullContext, selection, i): + srcContext = fullContext[0] + dlg = DroneSpinner(self.mainFrame, selection[0], srcContext) + dlg.ShowModal() + dlg.Destroy() + + +DroneSplit.register() + + +class DroneSpinner(wx.Dialog): + + def __init__(self, parent, drone, context): + wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60)) + self.drone = drone + self.context = context + + bSizer1 = wx.BoxSizer(wx.HORIZONTAL) + + self.spinner = wx.SpinCtrl(self) + self.spinner.SetRange(1, drone.amount - 1) + self.spinner.SetValue(1) + + bSizer1.Add(self.spinner, 0, wx.ALL, 5) + + self.button = wx.Button(self, wx.ID_OK, u"Split") + bSizer1.Add(self.button, 0, wx.ALL, 5) + + self.SetSizer(bSizer1) + self.Layout() + self.Centre(wx.BOTH) + self.button.Bind(wx.EVT_BUTTON, self.split) + + def split(self, event): + sFit = Fit.getInstance() + mainFrame = gui.mainFrame.MainFrame.getInstance() + fitID = mainFrame.getActiveFit() + if self.context == "droneItem": + sFit.splitDroneStack(fitID, self.drone, self.spinner.GetValue()) + else: + sFit.splitProjectedDroneStack(fitID, self.drone, self.spinner.GetValue()) + wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID)) + event.Skip() diff --git a/gui/builtinContextMenus/fighterAbilities.py b/gui/builtinContextMenus/fighterAbilities.py index 3a659c905..e422070cc 100644 --- a/gui/builtinContextMenus/fighterAbilities.py +++ b/gui/builtinContextMenus/fighterAbilities.py @@ -52,4 +52,5 @@ class FighterAbility(ContextMenu): sFit.toggleFighterAbility(fitID, ability) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + FighterAbility.register() diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index 8c5bc0cd6..2f822aec3 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -1,62 +1,64 @@ -from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import gui.mainFrame -import wx -from service.fit import Fit - -class ItemStats(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - - return srcContext in ("marketItemGroup", "marketItemMisc", - "fittingModule", "fittingCharge", - "fittingShip", "baseShip", - "cargoItem", "droneItem", - "implantItem", "boosterItem", - "skillItem", "projectedModule", - "projectedDrone", "projectedCharge", - "itemStats", "fighterItem", - "implantItemChar", "projectedFighter") - - def getText(self, itmContext, selection): - return "{0} Stats".format(itmContext if itmContext is not None else "Item") - - def activate(self, fullContext, selection, i): - srcContext = fullContext[0] - if srcContext == "fittingShip": - fitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - stuff = sFit.getFit(fitID).ship - else: - stuff = selection[0] - - if srcContext == "fittingModule" and stuff.isEmpty: - return - - mstate = wx.GetMouseState() - reuse = False - - if mstate.CmdDown(): - reuse = True - - if self.mainFrame.GetActiveStatsWindow() is None and reuse: - ItemStatsDialog(stuff, fullContext) - - elif reuse: - lastWnd = self.mainFrame.GetActiveStatsWindow() - pos = lastWnd.GetPosition() - maximized = lastWnd.IsMaximized() - if not maximized: - size = lastWnd.GetSize() - else: - size = wx.DefaultSize - pos = wx.DefaultPosition - ItemStatsDialog(stuff, fullContext, pos, size, maximized) - lastWnd.closeEvent(None) - - else: - ItemStatsDialog(stuff, fullContext) - -ItemStats.register() +from gui.contextMenu import ContextMenu +from gui.itemStats import ItemStatsDialog +import gui.mainFrame +import wx +from service.fit import Fit + + +class ItemStats(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + + return srcContext in ("marketItemGroup", "marketItemMisc", + "fittingModule", "fittingCharge", + "fittingShip", "baseShip", + "cargoItem", "droneItem", + "implantItem", "boosterItem", + "skillItem", "projectedModule", + "projectedDrone", "projectedCharge", + "itemStats", "fighterItem", + "implantItemChar", "projectedFighter") + + def getText(self, itmContext, selection): + return "{0} Stats".format(itmContext if itmContext is not None else "Item") + + def activate(self, fullContext, selection, i): + srcContext = fullContext[0] + if srcContext == "fittingShip": + fitID = self.mainFrame.getActiveFit() + sFit = Fit.getInstance() + stuff = sFit.getFit(fitID).ship + else: + stuff = selection[0] + + if srcContext == "fittingModule" and stuff.isEmpty: + return + + mstate = wx.GetMouseState() + reuse = False + + if mstate.CmdDown(): + reuse = True + + if self.mainFrame.GetActiveStatsWindow() is None and reuse: + ItemStatsDialog(stuff, fullContext) + + elif reuse: + lastWnd = self.mainFrame.GetActiveStatsWindow() + pos = lastWnd.GetPosition() + maximized = lastWnd.IsMaximized() + if not maximized: + size = lastWnd.GetSize() + else: + size = wx.DefaultSize + pos = wx.DefaultPosition + ItemStatsDialog(stuff, fullContext, pos, size, maximized) + lastWnd.closeEvent(None) + + else: + ItemStatsDialog(stuff, fullContext) + + +ItemStats.register() diff --git a/gui/builtinContextMenus/marketJump.py b/gui/builtinContextMenus/marketJump.py index d5804eada..eea2a9a2b 100644 --- a/gui/builtinContextMenus/marketJump.py +++ b/gui/builtinContextMenus/marketJump.py @@ -1,48 +1,49 @@ -from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import gui.mainFrame -from service.market import Market - -class MarketJump(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - validContexts = ("marketItemMisc", "fittingModule", - "fittingCharge", "droneItem", - "implantItem", "boosterItem", - "projectedModule", "projectedDrone", - "projectedCharge", "cargoItem", - "implantItemChar", "fighterItem", - "projectedDrone") - - if not srcContext in validContexts or selection is None or len(selection) < 1: - return False - - sMkt = Market.getInstance() - item = getattr(selection[0], "item", selection[0]) - mktGrp = sMkt.getMarketGroupByItem(item) - - # 1663 is Special Edition Festival Assets, we don't have root group for it - if mktGrp is None or mktGrp.ID == 1663: - return False - - doit = not selection[0].isEmpty if srcContext == "fittingModule" else True - return doit - - def getText(self, itmContext, selection): - return "{0} Market Group".format(itmContext if itmContext is not None else "Item") - - def activate(self, fullContext, selection, i): - srcContext = fullContext[0] - if srcContext in ("fittingCharge", "projectedCharge"): - item = selection[0].charge - elif hasattr(selection[0], "item"): - item = selection[0].item - else: - item = selection[0] - - self.mainFrame.notebookBrowsers.SetSelection(0) - self.mainFrame.marketBrowser.jump(item) - -MarketJump.register() +from gui.contextMenu import ContextMenu +from gui.itemStats import ItemStatsDialog +import gui.mainFrame +from service.market import Market + +class MarketJump(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + validContexts = ("marketItemMisc", "fittingModule", + "fittingCharge", "droneItem", + "implantItem", "boosterItem", + "projectedModule", "projectedDrone", + "projectedCharge", "cargoItem", + "implantItemChar", "fighterItem", + "projectedDrone") + + if srcContext not in validContexts or selection is None or len(selection) < 1: + return False + + sMkt = Market.getInstance() + item = getattr(selection[0], "item", selection[0]) + mktGrp = sMkt.getMarketGroupByItem(item) + + # 1663 is Special Edition Festival Assets, we don't have root group for it + if mktGrp is None or mktGrp.ID == 1663: + return False + + doit = not selection[0].isEmpty if srcContext == "fittingModule" else True + return doit + + def getText(self, itmContext, selection): + return "{0} Market Group".format(itmContext if itmContext is not None else "Item") + + def activate(self, fullContext, selection, i): + srcContext = fullContext[0] + if srcContext in ("fittingCharge", "projectedCharge"): + item = selection[0].charge + elif hasattr(selection[0], "item"): + item = selection[0].item + else: + item = selection[0] + + self.mainFrame.notebookBrowsers.SetSelection(0) + self.mainFrame.marketBrowser.jump(item) + + +MarketJump.register() diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py index 38e339074..43d722346 100644 --- a/gui/builtinContextMenus/metaSwap.py +++ b/gui/builtinContextMenus/metaSwap.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- -from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import gui.mainFrame import wx -import gui.globalEvents as GE -from service.market import Market + from service.fit import Fit +from service.market import Market +import gui.mainFrame +import gui.globalEvents as GE +from gui.contextMenu import ContextMenu class MetaSwap(ContextMenu): def __init__(self): @@ -42,7 +41,8 @@ class MetaSwap(ContextMenu): self.moduleLookup = {} def get_metalevel(x): - if "metaLevel" not in x.attributes: return 0 + if "metaLevel" not in x.attributes: + return 0 return x.attributes["metaLevel"].value def get_metagroup(x): @@ -99,4 +99,5 @@ class MetaSwap(ContextMenu): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + MetaSwap.register() diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 6edcf6deb..03ace7f52 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -1,224 +1,226 @@ -# -*- coding: utf-8 -*- -from gui.contextMenu import ContextMenu -import gui.mainFrame -import wx -from gui.bitmapLoader import BitmapLoader -from eos.types import Hardpoint -import gui.globalEvents as GE -from service.market import Market -from service.fit import Fit - -class ModuleAmmoPicker(ContextMenu): - DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal") - MISSILE_ORDER = ("em", "thermal", "kinetic", "explosive", "mixed") - - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "projectedModule"): - return False - - modules = selection if srcContext == "fittingModule" else (selection[0],) - - validCharges = None - checkedTypes = set() - - for mod in modules: - # loop through modules and gather list of valid charges - if mod.item.ID in checkedTypes: - continue - checkedTypes.add(mod.item.ID) - currCharges = mod.getValidCharges() - if len(currCharges) > 0: - if validCharges is not None and validCharges != currCharges: - return False - - validCharges = currCharges - self.module = mod - - if validCharges is None: - return False - - self.modules = modules - self.charges = list(filter(lambda charge: Market.getInstance().getPublicityByItem(charge), validCharges)) - return len(self.charges) > 0 - - def getText(self, itmContext, selection): - return "Charge" - - def turretSorter(self, charge): - damage = 0 - range = (self.module.getModifiedItemAttr("maxRange") or 0) * (charge.getAttribute("weaponRangeMultiplier") or 1) - falloff = (self.module.getModifiedItemAttr("falloff") or 0) * (charge.getAttribute("fallofMultiplier") or 1) - for type in self.DAMAGE_TYPES: - d = charge.getAttribute("%sDamage" % type) - if d > 0: - damage += d - - # Take optimal and half falloff as range factor - rangeFactor = range + falloff / 2 - - return - rangeFactor, charge.name.rsplit()[-2:], damage, charge.name - - def missileSorter(self, charge): - # Get charge damage type and total damage - chargeDamageType, totalDamage = self.damageInfo(charge) - # Find its position in sort list - position = self.MISSILE_ORDER.index(chargeDamageType) - return position, totalDamage, charge.name - - def damageInfo(self, charge): - # Set up data storage for missile damage stuff - damageMap = {} - totalDamage = 0 - # Fill them with the data about charge - for damageType in self.DAMAGE_TYPES: - currentDamage = charge.getAttribute("{0}Damage".format(damageType)) or 0 - damageMap[damageType] = currentDamage - totalDamage += currentDamage - # Detect type of ammo - chargeDamageType = None - for damageType in damageMap: - # If all damage belongs to certain type purely, set appropriate - # ammoType - if damageMap[damageType] == totalDamage: - chargeDamageType = damageType - break - # Else consider ammo as mixed damage - if chargeDamageType is None: - chargeDamageType = "mixed" - - return chargeDamageType, totalDamage - - def numericConverter(self, string): - return int(string) if string.isdigit() else string - - def nameSorter(self, charge): - parts = charge.name.split(" ") - return map(self.numericConverter, parts) - - def addCharge(self, menu, charge): - id = ContextMenu.nextID() - name = charge.name if charge is not None else "Empty" - self.chargeIds[id] = charge - item = wx.MenuItem(menu, id, name) - menu.Bind(wx.EVT_MENU, self.handleAmmoSwitch, item) - item.charge = charge - if charge is not None and charge.icon is not None: - bitmap = BitmapLoader.getBitmap(charge.icon.iconFile, "icons") - if bitmap is not None: - item.SetBitmap(bitmap) - - return item - - def addSeperator(self, m, text): - id = ContextMenu.nextID() - m.Append(id, u'─ %s ─' % text) - m.Enable(id, False) - - def getSubMenu(self, context, selection, rootMenu, i, pitem): - msw = True if "wxMSW" in wx.PlatformInfo else False - m = wx.Menu() - self.chargeIds = {} - hardpoint = self.module.hardpoint - moduleName = self.module.item.name - # Make sure we do not consider mining turrets as combat turrets - if hardpoint == Hardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount") is None: - self.addSeperator(m, "Long Range") - items = [] - range = None - nameBase = None - sub = None - self.charges.sort(key=self.turretSorter) - for charge in self.charges: - # fix issue 71 - will probably have to change if CCP adds more Orbital ammo - if "Orbital" in charge.name: - # uncomment if we ever want to include Oribital ammo in ammo picker - see issue #71 - # This allows us to hide the ammo, but it's still loadable from the market - #item = self.addCharge(m, charge) - #items.append(item) - continue - currBase = charge.name.rsplit()[-2:] - currRange = charge.getAttribute("weaponRangeMultiplier") - if nameBase is None or range != currRange or nameBase != currBase: - if sub is not None: - self.addSeperator(sub, "More Damage") - - sub = None - base = charge - nameBase = currBase - range = currRange - item = self.addCharge(rootMenu if msw else m, charge) - items.append(item) - else: - if sub is None: - sub = wx.Menu() - sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) - self.addSeperator(sub, "Less Damage") - item.SetSubMenu(sub) - sub.AppendItem(self.addCharge(rootMenu if msw else sub, base)) - - sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) - - if sub is not None: - self.addSeperator(sub, "More Damage") - - for item in items: - m.AppendItem(item) - - self.addSeperator(m, "Short Range") - elif hardpoint == Hardpoint.MISSILE and moduleName != 'Festival Launcher': - self.charges.sort(key=self.missileSorter) - type = None - sub = None - defender = None - for charge in self.charges: - currType = self.damageInfo(charge)[0] - - if currType != type or type is None: - if sub is not None: - self.addSeperator(sub, "More Damage") - - type = currType - item = wx.MenuItem(m, wx.ID_ANY, type.capitalize()) - bitmap = BitmapLoader.getBitmap("%s_small" % type, "gui") - if bitmap is not None: - item.SetBitmap(bitmap) - - sub = wx.Menu() - sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) - self.addSeperator(sub, "Less Damage") - item.SetSubMenu(sub) - m.AppendItem(item) - - if charge.name not in ("Light Defender Missile I", "Heavy Defender Missile I"): - sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) - else: - defender = charge - - if defender is not None: - m.AppendItem(self.addCharge(rootMenu if msw else m, defender)) - if sub is not None: - self.addSeperator(sub, "More Damage") - else: - self.charges.sort(key=self.nameSorter) - for charge in self.charges: - m.AppendItem(self.addCharge(rootMenu if msw else m, charge)) - - m.AppendItem(self.addCharge(rootMenu if msw else m, None)) - return m - - def handleAmmoSwitch(self, event): - charge = self.chargeIds.get(event.Id, False) - if charge is False: - event.Skip() - return - - sFit = Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - - sFit.setAmmo(fitID, charge.ID if charge is not None else None, self.modules) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - -ModuleAmmoPicker.register() +# -*- coding: utf-8 -*- +from gui.contextMenu import ContextMenu +import gui.mainFrame +import wx +from gui.bitmapLoader import BitmapLoader +from eos.types import Hardpoint +import gui.globalEvents as GE +from service.market import Market +from service.fit import Fit + + +class ModuleAmmoPicker(ContextMenu): + DAMAGE_TYPES = ("em", "explosive", "kinetic", "thermal") + MISSILE_ORDER = ("em", "thermal", "kinetic", "explosive", "mixed") + + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "projectedModule"): + return False + + modules = selection if srcContext == "fittingModule" else (selection[0],) + + validCharges = None + checkedTypes = set() + + for mod in modules: + # loop through modules and gather list of valid charges + if mod.item.ID in checkedTypes: + continue + checkedTypes.add(mod.item.ID) + currCharges = mod.getValidCharges() + if len(currCharges) > 0: + if validCharges is not None and validCharges != currCharges: + return False + + validCharges = currCharges + self.module = mod + + if validCharges is None: + return False + + self.modules = modules + self.charges = list(filter(lambda charge: Market.getInstance().getPublicityByItem(charge), validCharges)) + return len(self.charges) > 0 + + def getText(self, itmContext, selection): + return "Charge" + + def turretSorter(self, charge): + damage = 0 + range_ = (self.module.getModifiedItemAttr("maxRange") or 0) * (charge.getAttribute("weaponRangeMultiplier") or 1) + falloff = (self.module.getModifiedItemAttr("falloff") or 0) * (charge.getAttribute("fallofMultiplier") or 1) + for type_ in self.DAMAGE_TYPES: + d = charge.getAttribute("%sDamage" % type_) + if d > 0: + damage += d + + # Take optimal and half falloff as range factor + rangeFactor = range_ + falloff / 2 + + return - rangeFactor, charge.name.rsplit()[-2:], damage, charge.name + + def missileSorter(self, charge): + # Get charge damage type and total damage + chargeDamageType, totalDamage = self.damageInfo(charge) + # Find its position in sort list + position = self.MISSILE_ORDER.index(chargeDamageType) + return position, totalDamage, charge.name + + def damageInfo(self, charge): + # Set up data storage for missile damage stuff + damageMap = {} + totalDamage = 0 + # Fill them with the data about charge + for damageType in self.DAMAGE_TYPES: + currentDamage = charge.getAttribute("{0}Damage".format(damageType)) or 0 + damageMap[damageType] = currentDamage + totalDamage += currentDamage + # Detect type of ammo + chargeDamageType = None + for damageType in damageMap: + # If all damage belongs to certain type purely, set appropriate + # ammoType + if damageMap[damageType] == totalDamage: + chargeDamageType = damageType + break + # Else consider ammo as mixed damage + if chargeDamageType is None: + chargeDamageType = "mixed" + + return chargeDamageType, totalDamage + + def numericConverter(self, string): + return int(string) if string.isdigit() else string + + def nameSorter(self, charge): + parts = charge.name.split(" ") + return map(self.numericConverter, parts) + + def addCharge(self, menu, charge): + id_ = ContextMenu.nextID() + name = charge.name if charge is not None else "Empty" + self.chargeIds[id_] = charge + item = wx.MenuItem(menu, id_, name) + menu.Bind(wx.EVT_MENU, self.handleAmmoSwitch, item) + item.charge = charge + if charge is not None and charge.icon is not None: + bitmap = BitmapLoader.getBitmap(charge.icon.iconFile, "icons") + if bitmap is not None: + item.SetBitmap(bitmap) + + return item + + def addSeperator(self, m, text): + id_ = ContextMenu.nextID() + m.Append(id_, u'─ %s ─' % text) + m.Enable(id_, False) + + def getSubMenu(self, context, selection, rootMenu, i, pitem): + msw = True if "wxMSW" in wx.PlatformInfo else False + m = wx.Menu() + self.chargeIds = {} + hardpoint = self.module.hardpoint + moduleName = self.module.item.name + # Make sure we do not consider mining turrets as combat turrets + if hardpoint == Hardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount") is None: + self.addSeperator(m, "Long Range") + items = [] + range_ = None + nameBase = None + sub = None + self.charges.sort(key=self.turretSorter) + for charge in self.charges: + # fix issue 71 - will probably have to change if CCP adds more Orbital ammo + if "Orbital" in charge.name: + # uncomment if we ever want to include Oribital ammo in ammo picker - see issue #71 + # This allows us to hide the ammo, but it's still loadable from the market + # item = self.addCharge(m, charge) + # items.append(item) + continue + currBase = charge.name.rsplit()[-2:] + currRange = charge.getAttribute("weaponRangeMultiplier") + if nameBase is None or range_ != currRange or nameBase != currBase: + if sub is not None: + self.addSeperator(sub, "More Damage") + + sub = None + base = charge + nameBase = currBase + range_ = currRange + item = self.addCharge(rootMenu if msw else m, charge) + items.append(item) + else: + if sub is None: + sub = wx.Menu() + sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) + self.addSeperator(sub, "Less Damage") + item.SetSubMenu(sub) + sub.AppendItem(self.addCharge(rootMenu if msw else sub, base)) + + sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) + + if sub is not None: + self.addSeperator(sub, "More Damage") + + for item in items: + m.AppendItem(item) + + self.addSeperator(m, "Short Range") + elif hardpoint == Hardpoint.MISSILE and moduleName != 'Festival Launcher': + self.charges.sort(key=self.missileSorter) + type_ = None + sub = None + defender = None + for charge in self.charges: + currType = self.damageInfo(charge)[0] + + if currType != type_ or type_ is None: + if sub is not None: + self.addSeperator(sub, "More Damage") + + type_ = currType + item = wx.MenuItem(m, wx.ID_ANY, type_.capitalize()) + bitmap = BitmapLoader.getBitmap("%s_small" % type, "gui") + if bitmap is not None: + item.SetBitmap(bitmap) + + sub = wx.Menu() + sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) + self.addSeperator(sub, "Less Damage") + item.SetSubMenu(sub) + m.AppendItem(item) + + if charge.name not in ("Light Defender Missile I", "Heavy Defender Missile I"): + sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) + else: + defender = charge + + if defender is not None: + m.AppendItem(self.addCharge(rootMenu if msw else m, defender)) + if sub is not None: + self.addSeperator(sub, "More Damage") + else: + self.charges.sort(key=self.nameSorter) + for charge in self.charges: + m.AppendItem(self.addCharge(rootMenu if msw else m, charge)) + + m.AppendItem(self.addCharge(rootMenu if msw else m, None)) + return m + + def handleAmmoSwitch(self, event): + charge = self.chargeIds.get(event.Id, False) + if charge is False: + event.Skip() + return + + sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + + sFit.setAmmo(fitID, charge.ID if charge is not None else None, self.modules) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + +ModuleAmmoPicker.register() diff --git a/gui/builtinContextMenus/openFit.py b/gui/builtinContextMenus/openFit.py index eaf38832e..621885f67 100644 --- a/gui/builtinContextMenus/openFit.py +++ b/gui/builtinContextMenus/openFit.py @@ -17,4 +17,5 @@ class OpenFit(ContextMenu): fit = selection[0] wx.PostEvent(self.mainFrame, FitSelected(fitID=fit.ID, startup=2)) + OpenFit.register() diff --git a/gui/builtinContextMenus/priceClear.py b/gui/builtinContextMenus/priceClear.py index 14e4d23b4..be7986536 100644 --- a/gui/builtinContextMenus/priceClear.py +++ b/gui/builtinContextMenus/priceClear.py @@ -19,4 +19,5 @@ class PriceClear(ContextMenu): sMkt.clearPriceCache() wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) + PriceClear.register() diff --git a/gui/builtinContextMenus/project.py b/gui/builtinContextMenus/project.py index b40058a79..0fcae7864 100644 --- a/gui/builtinContextMenus/project.py +++ b/gui/builtinContextMenus/project.py @@ -1,37 +1,38 @@ -from gui.contextMenu import ContextMenu -import gui.mainFrame -import gui.globalEvents as GE -import wx -import eos.db -from service.fit import Fit - -class Project(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: - return False - - sFit = Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(fitID) - - if fit.isStructure: - return False - - item = selection[0] - return item.isType("projected") - - def getText(self, itmContext, selection): - return "Project {0} onto Fit".format(itmContext) - - def activate(self, fullContext, selection, i): - sFit = Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - trigger = sFit.project(fitID, selection[0]) - if trigger: - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - self.mainFrame.additionsPane.select("Projected") - -Project.register() +from gui.contextMenu import ContextMenu +import gui.mainFrame +import gui.globalEvents as GE +import wx +from service.fit import Fit + + +class Project(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None: + return False + + sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + fit = sFit.getFit(fitID) + + if fit.isStructure: + return False + + item = selection[0] + return item.isType("projected") + + def getText(self, itmContext, selection): + return "Project {0} onto Fit".format(itmContext) + + def activate(self, fullContext, selection, i): + sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + trigger = sFit.project(fitID, selection[0]) + if trigger: + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + self.mainFrame.additionsPane.select("Projected") + + +Project.register() diff --git a/gui/builtinContextMenus/shipJump.py b/gui/builtinContextMenus/shipJump.py index 3cf6b1f12..478619cf7 100644 --- a/gui/builtinContextMenus/shipJump.py +++ b/gui/builtinContextMenus/shipJump.py @@ -23,4 +23,5 @@ class ShipJump(ContextMenu): self.mainFrame.notebookBrowsers.SetSelection(1) wx.PostEvent(self.mainFrame.shipBrowser,Stage3Selected(shipID=stuff.item.ID, back=groupID)) + ShipJump.register() diff --git a/gui/builtinContextMenus/tacticalMode.py b/gui/builtinContextMenus/tacticalMode.py index 07dda7bc4..3909ef693 100644 --- a/gui/builtinContextMenus/tacticalMode.py +++ b/gui/builtinContextMenus/tacticalMode.py @@ -58,4 +58,5 @@ class TacticalMode(ContextMenu): sFit.setMode(fitID, self.modeIds[event.Id]) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + TacticalMode.register() diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index 557a7f276..7f3ba2131 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -106,4 +106,5 @@ class TargetResists(ContextMenu): return sub + TargetResists.register() diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py index b47ded3da..96fe2c767 100644 --- a/gui/builtinContextMenus/whProjector.py +++ b/gui/builtinContextMenus/whProjector.py @@ -54,4 +54,5 @@ class WhProjector(ContextMenu): sFit.project(fitID, swObj) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + WhProjector.register() diff --git a/gui/builtinGraphs/fitDps.py b/gui/builtinGraphs/fitDps.py index 0e0c4f12e..fcbfa68fa 100644 --- a/gui/builtinGraphs/fitDps.py +++ b/gui/builtinGraphs/fitDps.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= from gui.graph import Graph from gui.bitmapLoader import BitmapLoader @@ -24,6 +24,7 @@ from eos.graph import Data import gui.mainFrame from service.attribute import Attribute + class FitDpsGraph(Graph): propertyAttributeMap = {"angle": "maxVelocity", "distance": "maxRange", @@ -74,7 +75,7 @@ class FitDpsGraph(Graph): if variable is None: variable = fieldName else: - #We can't handle more then one variable atm, OOPS FUCK OUT + # We can't handle more then one variable atm, OOPS FUCK OUT return False, "Can only handle 1 variable" fitDps.setData(d) @@ -90,4 +91,5 @@ class FitDpsGraph(Graph): return x, y + FitDpsGraph.register() diff --git a/gui/builtinPreferenceViews/__init__.py b/gui/builtinPreferenceViews/__init__.py index 9d29c75ff..48e631eb1 100644 --- a/gui/builtinPreferenceViews/__init__.py +++ b/gui/builtinPreferenceViews/__init__.py @@ -1,6 +1,6 @@ -__all__ = ["pyfaGeneralPreferences","pyfaHTMLExportPreferences","pyfaUpdatePreferences","pyfaNetworkPreferences"] - import wx -if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): +__all__ = ["pyfaGeneralPreferences", "pyfaHTMLExportPreferences", "pyfaUpdatePreferences", "pyfaNetworkPreferences"] # noqa + +if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): __all__.append("pyfaCrestPreferences") diff --git a/gui/builtinPreferenceViews/dummyView.py b/gui/builtinPreferenceViews/dummyView.py index 5b8822d1c..c6c93f582 100644 --- a/gui/builtinPreferenceViews/dummyView.py +++ b/gui/builtinPreferenceViews/dummyView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,80 +15,81 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader + + class DummyView(PreferenceView): title = "Dummy" def populatePanel(self, panel): - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) headerSizer = self.initHeader(panel) - mainSizer.Add( headerSizer, 0, wx.EXPAND, 5 ) + mainSizer.Add(headerSizer, 0, wx.EXPAND, 5) - self.stline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.stline1, 0, wx.EXPAND, 5 ) + self.stline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.stline1, 0, wx.EXPAND, 5) contentSizer = self.initContent(panel) - mainSizer.Add( contentSizer, 1, wx.EXPAND|wx.TOP|wx.BOTTOM|wx.LEFT, 10 ) - - self.stline2 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.stline2, 0, wx.EXPAND, 5 ) + mainSizer.Add(contentSizer, 1, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.LEFT, 10) + self.stline2 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.stline2, 0, wx.EXPAND, 5) footerSizer = self.initFooter(panel) - mainSizer.Add( footerSizer, 0, wx.EXPAND, 5 ) - panel.SetSizer( mainSizer ) + mainSizer.Add(footerSizer, 0, wx.EXPAND, 5) + panel.SetSizer(mainSizer) panel.Layout() def refreshPanel(self, fit): pass def initHeader(self, panel): - headerSizer = wx.BoxSizer( wx.VERTICAL ) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, u"Dummy", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 14, 70, 90, 90, False, wx.EmptyString ) ) - headerSizer.Add( self.stTitle, 0, wx.ALL, 5) + headerSizer = wx.BoxSizer(wx.VERTICAL) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, u"Dummy", wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(14, 70, 90, 90, False, wx.EmptyString)) + headerSizer.Add(self.stTitle, 0, wx.ALL, 5) return headerSizer def initContent(self, panel): - contentSizer = wx.BoxSizer( wx.VERTICAL ) + contentSizer = wx.BoxSizer(wx.VERTICAL) - self.m_checkBox2 = wx.CheckBox( panel, wx.ID_ANY, u"Check Me!", wx.DefaultPosition, wx.DefaultSize, 0 ) - contentSizer.Add( self.m_checkBox2, 0, wx.ALL, 5 ) + self.m_checkBox2 = wx.CheckBox(panel, wx.ID_ANY, u"Check Me!", wx.DefaultPosition, wx.DefaultSize, 0) + contentSizer.Add(self.m_checkBox2, 0, wx.ALL, 5) - self.m_radioBtn2 = wx.RadioButton( panel, wx.ID_ANY, u"RadioBtn", wx.DefaultPosition, wx.DefaultSize, 0 ) - contentSizer.Add( self.m_radioBtn2, 0, wx.ALL, 5 ) + self.m_radioBtn2 = wx.RadioButton(panel, wx.ID_ANY, u"RadioBtn", wx.DefaultPosition, wx.DefaultSize, 0) + contentSizer.Add(self.m_radioBtn2, 0, wx.ALL, 5) - self.m_slider2 = wx.Slider( panel, wx.ID_ANY, 50, 0, 100, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL ) - contentSizer.Add( self.m_slider2, 0, wx.ALL, 5 ) + self.m_slider2 = wx.Slider(panel, wx.ID_ANY, 50, 0, 100, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL) + contentSizer.Add(self.m_slider2, 0, wx.ALL, 5) - self.m_gauge1 = wx.Gauge( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL ) - contentSizer.Add( self.m_gauge1, 0, wx.ALL, 5 ) + self.m_gauge1 = wx.Gauge(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) + contentSizer.Add(self.m_gauge1, 0, wx.ALL, 5) - self.m_textCtrl2 = wx.TextCtrl( panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) - contentSizer.Add( self.m_textCtrl2, 0, wx.ALL, 5 ) + self.m_textCtrl2 = wx.TextCtrl(panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) + contentSizer.Add(self.m_textCtrl2, 0, wx.ALL, 5) return contentSizer def initFooter(self, panel): - footerSizer = wx.BoxSizer( wx.HORIZONTAL ) + footerSizer = wx.BoxSizer(wx.HORIZONTAL) + footerSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - footerSizer.AddSpacer( ( 0, 0), 1, wx.EXPAND, 5 ) + self.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnRestore.Enable(False) - self.btnRestore = wx.Button( panel, wx.ID_ANY, u"Restore", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.btnRestore.Enable( False ) + footerSizer.Add(self.btnRestore, 0, wx.ALL, 5) - footerSizer.Add( self.btnRestore, 0, wx.ALL, 5 ) - - self.btnApply = wx.Button( panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0 ) - footerSizer.Add( self.btnApply, 0, wx.ALL, 5 ) + self.btnApply = wx.Button(panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0) + footerSizer.Add(self.btnApply, 0, wx.ALL, 5) return footerSizer + + DummyView.register() diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py index 56f559068..964596eb6 100644 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ b/gui/builtinPreferenceViews/pyfaCrestPreferences.py @@ -4,35 +4,35 @@ from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -from service.crest import CrestModes from service.crest import Crest from service.settings import CRESTSettings from wx.lib.intctrl import IntCtrl -class PFCrestPref ( PreferenceView): + +class PFCrestPref(PreferenceView): title = "CREST" - def populatePanel( self, panel ): + def populatePanel(self, panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.settings = CRESTSettings.getInstance() self.dirtySettings = False dlgWidth = panel.GetParent().GetParent().ClientSize.width - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.stInfo = wx.StaticText( panel, wx.ID_ANY, u"Please see the pyfa wiki on GitHub for information regarding these options.", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stInfo = wx.StaticText(panel, wx.ID_ANY, u"Please see the pyfa wiki on GitHub for information regarding these options.", wx.DefaultPosition, wx.DefaultSize, 0) self.stInfo.Wrap(dlgWidth - 50) - mainSizer.Add( self.stInfo, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) rbSizer = wx.BoxSizer(wx.HORIZONTAL) self.rbMode = wx.RadioBox(panel, -1, "Mode", wx.DefaultPosition, wx.DefaultSize, ['Implicit', 'User-supplied details'], 1, wx.RA_SPECIFY_COLS) @@ -41,65 +41,65 @@ class PFCrestPref ( PreferenceView): self.rbMode.SetSelection(self.settings.get('mode')) self.rbServer.SetSelection(self.settings.get('server')) - rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5 ) - rbSizer.Add(self.rbServer, 1, wx.ALL, 5 ) + rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5) + rbSizer.Add(self.rbServer, 1, wx.ALL, 5) self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) self.rbServer.Bind(wx.EVT_RADIOBOX, self.OnServerChange) - mainSizer.Add(rbSizer, 1, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) timeoutSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stTimout = wx.StaticText( panel, wx.ID_ANY, u"Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTimout.Wrap( -1 ) + self.stTimout = wx.StaticText(panel, wx.ID_ANY, u"Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0) + self.stTimout.Wrap(-1) - timeoutSizer.Add( self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout')) - timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5 ) + timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5) self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange) - mainSizer.Add(timeoutSizer, 0, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(timeoutSizer, 0, wx.ALL | wx.EXPAND, 0) - detailsTitle = wx.StaticText( panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0 ) - detailsTitle.Wrap( -1 ) - detailsTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + detailsTitle = wx.StaticText(panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0) + detailsTitle.Wrap(-1) + detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( detailsTitle, 0, wx.ALL, 5 ) - mainSizer.Add( wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND, 5 ) + mainSizer.Add(detailsTitle, 0, wx.ALL, 5) + mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 5) - fgAddrSizer = wx.FlexGridSizer( 2, 2, 0, 0 ) - fgAddrSizer.AddGrowableCol( 1 ) - fgAddrSizer.SetFlexibleDirection( wx.BOTH ) - fgAddrSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) + fgAddrSizer.AddGrowableCol(1) + fgAddrSizer.SetFlexibleDirection(wx.BOTH) + fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - self.stSetID = wx.StaticText( panel, wx.ID_ANY, u"Client ID:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stSetID.Wrap( -1 ) - fgAddrSizer.Add( self.stSetID, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.stSetID = wx.StaticText(panel, wx.ID_ANY, u"Client ID:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stSetID.Wrap(-1) + fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.inputClientID = wx.TextCtrl( panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, wx.DefaultSize, 0) - fgAddrSizer.Add( self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5 ) + fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.stSetSecret = wx.StaticText( panel, wx.ID_ANY, u"Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stSetSecret.Wrap( -1 ) + self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, u"Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stSetSecret.Wrap(-1) - fgAddrSizer.Add( self.stSetSecret, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.inputClientSecret = wx.TextCtrl( panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, wx.DefaultSize, 0 ) + self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, wx.DefaultSize, 0) - fgAddrSizer.Add( self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5 ) + fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.btnApply = wx.Button( panel, wx.ID_ANY, u"Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.btnApply = wx.Button(panel, wx.ID_ANY, u"Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0) self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) - mainSizer.Add( fgAddrSizer, 0, wx.EXPAND, 5) - mainSizer.Add( self.btnApply, 0, wx.ALIGN_RIGHT, 5) + mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) + mainSizer.Add(self.btnApply, 0, wx.ALIGN_RIGHT, 5) self.ToggleProxySettings(self.settings.get('mode')) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def OnTimeoutChange(self, event): @@ -135,4 +135,5 @@ class PFCrestPref ( PreferenceView): def getImage(self): return BitmapLoader.getBitmap("eve", "gui") + PFCrestPref.register() diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index 4163eeaf4..c20f0d878 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -11,12 +11,13 @@ from gui.utils import colorUtils import gui.utils.drawUtils as drawUtils ########################################################################### -## Class PFGaugePref +# Class PFGaugePref ########################################################################### + class PFGaugePreview(wx.Window): - def __init__ (self, parent, id = wx.ID_ANY, value = 0, pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0): - wx.Window.__init__(self, parent, id, pos = pos, size = size, style = style) + def __init__(self, parent, id=wx.ID_ANY, value=0, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): + wx.Window.__init__(self, parent, id, pos=pos, size=size, style=style) self.value = float(value) self.oldValue = self.value @@ -28,14 +29,14 @@ class PFGaugePreview(wx.Window): self.animDir = 1 self._fractionDigits = 2 - self.colorS = wx.Colour(0,0,0,255) - self.colorE = wx.Colour(0,0,0,255) + self.colorS = wx.Colour(0, 0, 0, 255) + self.colorE = wx.Colour(0, 0, 0, 255) self.gradientStart = 0 - self.bkColor = wx.Colour(0,0,0,255) - self.SetMinSize((100,-1)) + self.bkColor = wx.Colour(0, 0, 0, 255) + self.SetMinSize((100, -1)) - self.font = wx.FontFromPixelSize((0,13),wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.font = wx.FontFromPixelSize((0, 13), wx.SWISS, wx.NORMAL, wx.NORMAL, False) self.timerID = wx.NewId() self.timer = wx.Timer(self, self.timerID) @@ -56,7 +57,7 @@ class PFGaugePreview(wx.Window): if self.value > 100: self.value = 100 self.animDir = -1 - if self.value <0: + if self.value < 0: self.value = 0 self.animDir = 1 self.Refresh() @@ -79,7 +80,7 @@ class PFGaugePreview(wx.Window): self.Refresh() event.Skip() - def CanAnimate(self, anim = True): + def CanAnimate(self, anim=True): self.animate = anim if self.timer.IsRunning(): self.timer.Stop() @@ -97,7 +98,7 @@ class PFGaugePreview(wx.Window): self.Refresh() def SetValue(self, value): - self.value = min(max(value,0),100) + self.value = min(max(value, 0), 100) self.Refresh() def SetPercentages(self, start, end): @@ -119,198 +120,198 @@ class PFGaugePreview(wx.Window): r = copy.copy(rect) r.width = w - color = colorUtils.CalculateTransitionColor(self.colorS, self.colorE, float(value)/100) + color = colorUtils.CalculateTransitionColor(self.colorS, self.colorE, float(value) / 100) if self.gradientStart > 0: - gcolor = colorUtils.BrightenColor(color, float(self.gradientStart) / 100) - gMid = colorUtils.BrightenColor(color, float(self.gradientStart/2) / 100) + gcolor = colorUtils.BrightenColor(color, float(self.gradientStart) / 100) + gMid = colorUtils.BrightenColor(color, float(self.gradientStart / 2) / 100) else: - gcolor = colorUtils.DarkenColor(color, float(-self.gradientStart) / 100) - gMid = colorUtils.DarkenColor(color, float(-self.gradientStart/2) / 100) + gcolor = colorUtils.DarkenColor(color, float(-self.gradientStart) / 100) + gMid = colorUtils.DarkenColor(color, float(-self.gradientStart / 2) / 100) gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) - dc.DrawBitmap(gBmp,0,0) + dc.DrawBitmap(gBmp, 0, 0) dc.SetFont(self.font) r = copy.copy(rect) - r.left +=1 - r.top +=1 - + r.left += 1 + r.top += 1 formatStr = "{0:." + str(self._fractionDigits) + "f}%" value = (self.percE - self.percS) * value / (self.percE - self.percS) value = self.percS + (self.percE - self.percS) * value / 100 - dc.SetTextForeground(wx.Colour(80,80,80)) + dc.SetTextForeground(wx.Colour(80, 80, 80)) dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) - dc.SetTextForeground(wx.Colour(255,255,255)) + dc.SetTextForeground(wx.Colour(255, 255, 255)) dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) -class PFGaugePref ( PreferenceView): +class PFGaugePref(PreferenceView): title = "Pyfa Gauge Theme" - def populatePanel( self, panel ): + + def populatePanel(self, panel): self.InitDefaultColours() - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - gSizer1 = wx.BoxSizer( wx.HORIZONTAL ) + gSizer1 = wx.BoxSizer(wx.HORIZONTAL) - self.st0100 = wx.StaticText( panel, wx.ID_ANY, u"0 - 100", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.st0100.Wrap( -1 ) - gSizer1.Add( self.st0100, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.st0100 = wx.StaticText(panel, wx.ID_ANY, u"0 - 100", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) + self.st0100.Wrap(-1) + gSizer1.Add(self.st0100, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp0100S = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer1.Add( self.cp0100S, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp0100S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer1.Add(self.cp0100S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp0100E = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer1.Add( self.cp0100E, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp0100E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer1.Add(self.cp0100E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.gauge0100S = PFGaugePreview( panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer1.Add( self.gauge0100S, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge0100S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer1.Add(self.gauge0100S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge0100M = PFGaugePreview( panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer1.Add( self.gauge0100M, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge0100M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer1.Add(self.gauge0100M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge0100E = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer1.Add( self.gauge0100E, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, 5 ) + self.gauge0100E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer1.Add(self.gauge0100E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - mainSizer.Add( gSizer1, 0, wx.EXPAND, 5 ) + mainSizer.Add(gSizer1, 0, wx.EXPAND, 5) - gSizer2 = wx.BoxSizer( wx.HORIZONTAL ) + gSizer2 = wx.BoxSizer(wx.HORIZONTAL) - self.st100101 = wx.StaticText( panel, wx.ID_ANY, u"100 - 101", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.st100101.Wrap( -1 ) - gSizer2.Add( self.st100101, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.st100101 = wx.StaticText(panel, wx.ID_ANY, u"100 - 101", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) + self.st100101.Wrap(-1) + gSizer2.Add(self.st100101, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp100101S = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer2.Add( self.cp100101S, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp100101S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer2.Add(self.cp100101S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp100101E = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer2.Add( self.cp100101E, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp100101E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer2.Add(self.cp100101E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.gauge100101S = PFGaugePreview( panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer2.Add( self.gauge100101S, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge100101S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer2.Add(self.gauge100101S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge100101M = PFGaugePreview( panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer2.Add( self.gauge100101M, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge100101M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer2.Add(self.gauge100101M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge100101E = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer2.Add( self.gauge100101E, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, 5 ) + self.gauge100101E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer2.Add(self.gauge100101E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - mainSizer.Add( gSizer2, 0, wx.EXPAND, 5 ) + mainSizer.Add(gSizer2, 0, wx.EXPAND, 5) - gSizer3 = wx.BoxSizer( wx.HORIZONTAL ) + gSizer3 = wx.BoxSizer(wx.HORIZONTAL) - self.st101103 = wx.StaticText( panel, wx.ID_ANY, u"101 - 103", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.st101103.Wrap( -1 ) - gSizer3.Add( self.st101103, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.st101103 = wx.StaticText(panel, wx.ID_ANY, u"101 - 103", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) + self.st101103.Wrap(-1) + gSizer3.Add(self.st101103, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp101103S = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer3.Add( self.cp101103S, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp101103S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer3.Add(self.cp101103S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp101103E = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer3.Add( self.cp101103E, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp101103E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer3.Add(self.cp101103E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.gauge101103S = PFGaugePreview( panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer3.Add( self.gauge101103S, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge101103S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer3.Add(self.gauge101103S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge101103M = PFGaugePreview( panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer3.Add( self.gauge101103M, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge101103M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer3.Add(self.gauge101103M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge101103E = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer3.Add( self.gauge101103E, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, 5 ) + self.gauge101103E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer3.Add(self.gauge101103E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - mainSizer.Add( gSizer3, 0, wx.EXPAND, 5 ) + mainSizer.Add(gSizer3, 0, wx.EXPAND, 5) - gSizer4 = wx.BoxSizer( wx.HORIZONTAL ) + gSizer4 = wx.BoxSizer(wx.HORIZONTAL) - self.st103105 = wx.StaticText( panel, wx.ID_ANY, u"103 - 105", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.st103105.Wrap( -1 ) - gSizer4.Add( self.st103105, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.st103105 = wx.StaticText(panel, wx.ID_ANY, u"103 - 105", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) + self.st103105.Wrap(-1) + gSizer4.Add(self.st103105, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp103105S = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer4.Add( self.cp103105S, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp103105S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer4.Add(self.cp103105S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp103105E = wx.ColourPickerCtrl( panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL ) - gSizer4.Add( self.cp103105E, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.cp103105E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + gSizer4.Add(self.cp103105E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.gauge103105S = PFGaugePreview( panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer4.Add( self.gauge103105S, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge103105S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer4.Add(self.gauge103105S, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge103105M = PFGaugePreview( panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer4.Add( self.gauge103105M, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5 ) + self.gauge103105M = PFGaugePreview(panel, wx.ID_ANY, 66, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer4.Add(self.gauge103105M, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5) - self.gauge103105E = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER ) - gSizer4.Add( self.gauge103105E, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.LEFT, 5 ) + self.gauge103105E = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) + gSizer4.Add(self.gauge103105E, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - mainSizer.Add( gSizer4, 0, wx.EXPAND, 5 ) + mainSizer.Add(gSizer4, 0, wx.EXPAND, 5) - footerSizer = wx.BoxSizer( wx.VERTICAL ) + footerSizer = wx.BoxSizer(wx.VERTICAL) - self.sl1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - footerSizer.Add( self.sl1, 0, wx.EXPAND |wx.ALL, 5 ) + self.sl1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + footerSizer.Add(self.sl1, 0, wx.EXPAND | wx.ALL, 5) - previewSizer = wx.BoxSizer( wx.HORIZONTAL ) + previewSizer = wx.BoxSizer(wx.HORIZONTAL) - self.wndPreview0100 = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) - previewSizer.Add( self.wndPreview0100, 1, wx.ALIGN_CENTER_VERTICAL, 5 ) + self.wndPreview0100 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) + previewSizer.Add(self.wndPreview0100, 1, wx.ALIGN_CENTER_VERTICAL, 5) - self.wndPreview100101 = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) - previewSizer.Add( self.wndPreview100101, 1, wx.ALIGN_CENTER_VERTICAL, 5 ) + self.wndPreview100101 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) + previewSizer.Add(self.wndPreview100101, 1, wx.ALIGN_CENTER_VERTICAL, 5) - self.wndPreview101103 = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) - previewSizer.Add( self.wndPreview101103, 1, wx.ALIGN_CENTER_VERTICAL, 5 ) + self.wndPreview101103 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) + previewSizer.Add(self.wndPreview101103, 1, wx.ALIGN_CENTER_VERTICAL, 5) - self.wndPreview103105 = PFGaugePreview( panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) - previewSizer.Add( self.wndPreview103105, 1, wx.ALIGN_CENTER_VERTICAL, 5 ) + self.wndPreview103105 = PFGaugePreview(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, 0) + previewSizer.Add(self.wndPreview103105, 1, wx.ALIGN_CENTER_VERTICAL, 5) - footerSizer.Add( previewSizer, 1, wx.EXPAND | wx.ALL, 5 ) + footerSizer.Add(previewSizer, 1, wx.EXPAND | wx.ALL, 5) - buttonsSizer = wx.BoxSizer( wx.HORIZONTAL ) + buttonsSizer = wx.BoxSizer(wx.HORIZONTAL) - self.cbLink = wx.CheckBox( panel, wx.ID_ANY, u"Link Colors", wx.DefaultPosition, wx.DefaultSize, 0 ) - buttonsSizer.Add( self.cbLink, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5 ) + self.cbLink = wx.CheckBox(panel, wx.ID_ANY, u"Link Colors", wx.DefaultPosition, wx.DefaultSize, 0) + buttonsSizer.Add(self.cbLink, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - self.sliderGradientStart = wx.Slider( panel, wx.ID_ANY, self.gradientStart, -100, 100, wx.DefaultPosition, (127,-1), wx.SL_HORIZONTAL|wx.SL_LABELS ) - buttonsSizer.Add( self.sliderGradientStart, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.sliderGradientStart = wx.Slider(panel, wx.ID_ANY, self.gradientStart, -100, 100, wx.DefaultPosition, (127, -1), wx.SL_HORIZONTAL | wx.SL_LABELS) + buttonsSizer.Add(self.sliderGradientStart, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnRestore = wx.Button( panel, wx.ID_ANY, u"Restore Defaults", wx.DefaultPosition, wx.DefaultSize, 0 ) - buttonsSizer.Add( self.btnRestore, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore Defaults", wx.DefaultPosition, wx.DefaultSize, 0) + buttonsSizer.Add(self.btnRestore, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnDump = wx.Button( panel, wx.ID_ANY, u"Dump Colors", wx.DefaultPosition, wx.DefaultSize, 0 ) - buttonsSizer.Add( self.btnDump, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.btnDump = wx.Button(panel, wx.ID_ANY, u"Dump Colors", wx.DefaultPosition, wx.DefaultSize, 0) + buttonsSizer.Add(self.btnDump, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnOk = wx.Button( panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0 ) - buttonsSizer.Add( self.btnOk, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.btnOk = wx.Button(panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0) + buttonsSizer.Add(self.btnOk, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - footerSizer.Add( buttonsSizer, 1, wx.ALIGN_RIGHT, 5 ) - mainSizer.Add( footerSizer, 0, wx.EXPAND, 5 ) + footerSizer.Add(buttonsSizer, 1, wx.ALIGN_RIGHT, 5) + mainSizer.Add(footerSizer, 0, wx.EXPAND, 5) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) self.SetColours() -# self.Fit() -# self.Layout() + # self.Fit() + # self.Layout() self.sliderGradientStart.Bind(wx.EVT_SCROLL, self.OnGradientStartScroll) self.btnRestore.Bind(wx.EVT_BUTTON, self.RestoreDefaults) self.btnDump.Bind(wx.EVT_BUTTON, self.DumpColours) self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk) - self.cp0100S.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) - self.cp0100E.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) + self.cp0100S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) + self.cp0100E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) - self.cp100101S.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) - self.cp100101E.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) + self.cp100101S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) + self.cp100101E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) - self.cp101103S.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) - self.cp101103E.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) + self.cp101103S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) + self.cp101103E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) - self.cp103105S.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) - self.cp103105E.Bind( wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged ) + self.cp103105S.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) + self.cp103105E.Bind(wx.EVT_COLOURPICKER_CHANGED, self.OnColourChanged) def getImage(self): return BitmapLoader.getBitmap("pref-gauges_big", "gui") @@ -344,7 +345,6 @@ class PFGaugePref ( PreferenceView): self.gauge0100M.SetGradientStart(self.sliderGradientStart.GetValue()) self.gauge0100E.SetGradientStart(self.sliderGradientStart.GetValue()) - self.cp100101S.SetColour(self.c100101S) self.cp100101E.SetColour(self.c100101E) self.gauge100101S.SetColour(self.c100101S, self.c100101E) @@ -363,7 +363,6 @@ class PFGaugePref ( PreferenceView): self.gauge100101M.SetGradientStart(self.sliderGradientStart.GetValue()) self.gauge100101E.SetGradientStart(self.sliderGradientStart.GetValue()) - self.cp101103S.SetColour(self.c101103S) self.cp101103E.SetColour(self.c101103E) self.gauge101103S.SetColour(self.c101103S, self.c101103E) @@ -378,7 +377,6 @@ class PFGaugePref ( PreferenceView): self.gauge101103M.SetGradientStart(self.sliderGradientStart.GetValue()) self.gauge101103E.SetGradientStart(self.sliderGradientStart.GetValue()) - self.cp103105S.SetColour(self.c103105S) self.cp103105E.SetColour(self.c103105E) self.gauge103105S.SetColour(self.c103105S, self.c103105E) @@ -406,7 +404,7 @@ class PFGaugePref ( PreferenceView): self.wndPreview101103.SetGradientStart(self.sliderGradientStart.GetValue()) self.wndPreview103105.SetColour(self.c103105S, self.c103105E) - self.wndPreview103105.SetPercentages(103,104.99) + self.wndPreview103105.SetPercentages(103, 104.99) self.wndPreview103105.SetGradientStart(self.sliderGradientStart.GetValue()) def OnGradientStartScroll(self, event): @@ -415,15 +413,15 @@ class PFGaugePref ( PreferenceView): event.Skip() def OnOk(self, event): - #Apply New Settings + # Apply New Settings event.Skip() def DumpColours(self, event): - print "Gradient start: %d" % self.sliderGradientStart.GetValue() - print " 0 <-> 100 Start: ", self.c0100S, " End: ", self.c0100E - print "100 <-> 101 Start: ", self.c100101S, " End: ", self.c100101E - print "101 <-> 103 Start: ", self.c101103S, " End: ", self.c101103E - print "103 <-> 105 Start: ", self.c103105S, " End: ", self.c103105E + print("Gradient start: %d" % self.sliderGradientStart.GetValue()) + print(" 0 <-> 100 Start: ", self.c0100S, " End: ", self.c0100E) + print("100 <-> 101 Start: ", self.c100101S, " End: ", self.c100101E) + print("101 <-> 103 Start: ", self.c101103S, " End: ", self.c101103E) + print("103 <-> 105 Start: ", self.c103105S, " End: ", self.c103105E) event.Skip() @@ -478,7 +476,8 @@ class PFGaugePref ( PreferenceView): self.SetColours() event.Skip() - def __del__( self ): + def __del__(self): pass + PFGaugePref.register() diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index 4ea5924bc..35ea9cb1d 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -8,67 +8,68 @@ import gui.globalEvents as GE from service.settings import SettingsProvider from service.fit import Fit -class PFGeneralPref ( PreferenceView): + +class PFGeneralPref(PreferenceView): title = "General" - def populatePanel( self, panel ): + def populatePanel(self, panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.dirtySettings = False self.openFitsSettings = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.cbGlobalChar = wx.CheckBox( panel, wx.ID_ANY, u"Use global character", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbGlobalChar, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, u"Use global character", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbGlobalChar, 0, wx.ALL | wx.EXPAND, 5) - self.cbGlobalDmgPattern = wx.CheckBox( panel, wx.ID_ANY, u"Use global damage pattern", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbGlobalDmgPattern, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbGlobalDmgPattern = wx.CheckBox(panel, wx.ID_ANY, u"Use global damage pattern", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbGlobalDmgPattern, 0, wx.ALL | wx.EXPAND, 5) - self.cbGlobalForceReload = wx.CheckBox( panel, wx.ID_ANY, u"Factor in reload time", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbGlobalForceReload, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbGlobalForceReload = wx.CheckBox(panel, wx.ID_ANY, u"Factor in reload time", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbGlobalForceReload, 0, wx.ALL | wx.EXPAND, 5) - self.cbCompactSkills = wx.CheckBox( panel, wx.ID_ANY, u"Compact skills needed tooltip", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbCompactSkills, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbCompactSkills = wx.CheckBox(panel, wx.ID_ANY, u"Compact skills needed tooltip", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbCompactSkills, 0, wx.ALL | wx.EXPAND, 5) - self.cbFitColorSlots = wx.CheckBox( panel, wx.ID_ANY, u"Color fitting view by slot", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbFitColorSlots, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbFitColorSlots = wx.CheckBox(panel, wx.ID_ANY, u"Color fitting view by slot", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbFitColorSlots, 0, wx.ALL | wx.EXPAND, 5) - self.cbReopenFits = wx.CheckBox( panel, wx.ID_ANY, u"Reopen previous fits on startup", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbReopenFits, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbReopenFits = wx.CheckBox(panel, wx.ID_ANY, u"Reopen previous fits on startup", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbReopenFits, 0, wx.ALL | wx.EXPAND, 5) - self.cbRackSlots = wx.CheckBox( panel, wx.ID_ANY, u"Separate Racks", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbRackSlots, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbRackSlots = wx.CheckBox(panel, wx.ID_ANY, u"Separate Racks", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbRackSlots, 0, wx.ALL | wx.EXPAND, 5) - labelSizer = wx.BoxSizer( wx.VERTICAL ) - self.cbRackLabels = wx.CheckBox( panel, wx.ID_ANY, u"Show Rack Labels", wx.DefaultPosition, wx.DefaultSize, 0 ) - labelSizer.Add( self.cbRackLabels, 0, wx.ALL|wx.EXPAND, 5 ) - mainSizer.Add( labelSizer, 0, wx.LEFT|wx.EXPAND, 30 ) + labelSizer = wx.BoxSizer(wx.VERTICAL) + self.cbRackLabels = wx.CheckBox(panel, wx.ID_ANY, u"Show Rack Labels", wx.DefaultPosition, wx.DefaultSize, 0) + labelSizer.Add(self.cbRackLabels, 0, wx.ALL | wx.EXPAND, 5) + mainSizer.Add(labelSizer, 0, wx.LEFT | wx.EXPAND, 30) - self.cbShowTooltip = wx.CheckBox( panel, wx.ID_ANY, u"Show tab tooltips", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbShowTooltip, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbShowTooltip = wx.CheckBox(panel, wx.ID_ANY, u"Show tab tooltips", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbShowTooltip, 0, wx.ALL | wx.EXPAND, 5) - self.cbMarketShortcuts = wx.CheckBox( panel, wx.ID_ANY, u"Show market shortcuts", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbMarketShortcuts, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbMarketShortcuts = wx.CheckBox(panel, wx.ID_ANY, u"Show market shortcuts", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbMarketShortcuts, 0, wx.ALL | wx.EXPAND, 5) - self.cbGaugeAnimation = wx.CheckBox( panel, wx.ID_ANY, u"Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbGaugeAnimation, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbGaugeAnimation = wx.CheckBox(panel, wx.ID_ANY, u"Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbGaugeAnimation, 0, wx.ALL | wx.EXPAND, 5) - self.cbExportCharges = wx.CheckBox( panel, wx.ID_ANY, u"Export loaded charges", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbExportCharges, 0, wx.ALL|wx.EXPAND, 5 ) - - self.cbOpenFitInNew = wx.CheckBox( panel, wx.ID_ANY, u"Open fittings in a new page by default", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbOpenFitInNew, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbExportCharges = wx.CheckBox(panel, wx.ID_ANY, u"Export loaded charges", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbExportCharges, 0, wx.ALL | wx.EXPAND, 5) - defCharSizer = wx.BoxSizer( wx.HORIZONTAL ) + self.cbOpenFitInNew = wx.CheckBox(panel, wx.ID_ANY, u"Open fittings in a new page by default", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbOpenFitInNew, 0, wx.ALL | wx.EXPAND, 5) + + wx.BoxSizer(wx.HORIZONTAL) self.sFit = Fit.getInstance() @@ -102,7 +103,7 @@ class PFGeneralPref ( PreferenceView): self.cbRackLabels.Enable(self.sFit.serviceFittingOptions["rackSlots"] or False) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def onCBGlobalColorBySlot(self, event): @@ -163,11 +164,12 @@ class PFGeneralPref ( PreferenceView): def onCBExportCharges(self, event): self.sFit.serviceFittingOptions["exportCharges"] = self.cbExportCharges.GetValue() - + def onCBOpenFitInNew(self, event): self.sFit.serviceFittingOptions["openFitInNew"] = self.cbOpenFitInNew.GetValue() def getImage(self): return BitmapLoader.getBitmap("prefs_settings", "gui") -PFGeneralPref.register() \ No newline at end of file + +PFGeneralPref.register() diff --git a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py index f018efe8f..588c9eead 100644 --- a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py +++ b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py @@ -5,79 +5,76 @@ from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader import gui.mainFrame -import gui.globalEvents as GE from service.settings import HTMLExportSettings -class PFHTMLExportPref ( PreferenceView): +class PFHTMLExportPref(PreferenceView): title = "HTML Export" - desc = "HTML Export (File > Export HTML) allows you to export your entire fitting "+\ - "database into an HTML file at the specified location. This file can be "+\ - "used in the in-game browser to easily open and import your fits, or used "+\ - "in a regular web browser to open them at NULL-SEC.com or Osmium." - desc2 = "Enabling automatic exporting will update the HTML file after any change "+\ - "to a fit is made. Under certain circumstance, this may cause performance issues." - desc4 = "Export Fittings in a minmal HTML Version, just containing the Fittingslinks " +\ - "without any visual styling or javscript features" + desc = ("HTML Export (File > Export HTML) allows you to export your entire fitting " + "database into an HTML file at the specified location. This file can be " + "used in the in-game browser to easily open and import your fits, or used " + "in a regular web browser to open them at NULL-SEC.com or Osmium.") + desc2 = ("Enabling automatic exporting will update the HTML file after any change " + "to a fit is made. Under certain circumstance, this may cause performance issues.") + desc4 = ("Export Fittings in a minmal HTML Version, just containing the Fittingslinks " + "without any visual styling or javscript features") - def populatePanel( self, panel ): + def populatePanel(self, panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.HTMLExportSettings = HTMLExportSettings.getInstance() self.dirtySettings = False dlgWidth = panel.GetParent().GetParent().ClientSize.width - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.stDesc = wx.StaticText( panel, wx.ID_ANY, self.desc, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stDesc = wx.StaticText(panel, wx.ID_ANY, self.desc, wx.DefaultPosition, wx.DefaultSize, 0) self.stDesc.Wrap(dlgWidth - 50) - mainSizer.Add( self.stDesc, 0, wx.ALL, 5 ) + mainSizer.Add(self.stDesc, 0, wx.ALL, 5) - self.PathLinkCtrl = wx.HyperlinkCtrl( panel, wx.ID_ANY, self.HTMLExportSettings.getPath(), u'file:///{}'.format(self.HTMLExportSettings.getPath()), wx.DefaultPosition, wx.DefaultSize, wx.HL_ALIGN_LEFT|wx.NO_BORDER|wx.HL_CONTEXTMENU ) - mainSizer.Add( self.PathLinkCtrl, 0, wx.ALL|wx.EXPAND, 5) + self.PathLinkCtrl = wx.HyperlinkCtrl(panel, wx.ID_ANY, self.HTMLExportSettings.getPath(), u'file:///{}'.format(self.HTMLExportSettings.getPath()), wx.DefaultPosition, wx.DefaultSize, wx.HL_ALIGN_LEFT | wx.NO_BORDER | wx.HL_CONTEXTMENU) + mainSizer.Add(self.PathLinkCtrl, 0, wx.ALL | wx.EXPAND, 5) - self.fileSelectDialog = wx.FileDialog(None, "Save Fitting As...", wildcard = "EVE IGB HTML fitting file (*.html)|*.html", style = wx.FD_SAVE) + self.fileSelectDialog = wx.FileDialog(None, "Save Fitting As...", wildcard="EVE IGB HTML fitting file (*.html)|*.html", style=wx.FD_SAVE) self.fileSelectDialog.SetPath(self.HTMLExportSettings.getPath()) - self.fileSelectDialog.SetFilename(os.path.basename(self.HTMLExportSettings.getPath())); + self.fileSelectDialog.SetFilename(os.path.basename(self.HTMLExportSettings.getPath())) - self.fileSelectButton = wx.Button(panel, -1, "Set export destination", pos=(0,0)) + self.fileSelectButton = wx.Button(panel, -1, "Set export destination", pos=(0, 0)) self.fileSelectButton.Bind(wx.EVT_BUTTON, self.selectHTMLExportFilePath) - mainSizer.Add( self.fileSelectButton, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) + mainSizer.Add(self.fileSelectButton, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.stDesc2 = wx.StaticText( panel, wx.ID_ANY, self.desc2, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stDesc2 = wx.StaticText(panel, wx.ID_ANY, self.desc2, wx.DefaultPosition, wx.DefaultSize, 0) self.stDesc2.Wrap(dlgWidth - 50) - mainSizer.Add( self.stDesc2, 0, wx.ALL, 5 ) + mainSizer.Add(self.stDesc2, 0, wx.ALL, 5) - self.exportEnabled = wx.CheckBox( panel, wx.ID_ANY, u"Enable automatic HTML export", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.exportEnabled = wx.CheckBox(panel, wx.ID_ANY, u"Enable automatic HTML export", wx.DefaultPosition, wx.DefaultSize, 0) self.exportEnabled.SetValue(self.HTMLExportSettings.getEnabled()) self.exportEnabled.Bind(wx.EVT_CHECKBOX, self.OnExportEnabledChange) - mainSizer.Add( self.exportEnabled, 0, wx.ALL|wx.EXPAND, 5 ) + mainSizer.Add(self.exportEnabled, 0, wx.ALL | wx.EXPAND, 5) - - - self.stDesc4 = wx.StaticText( panel, wx.ID_ANY, self.desc4, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stDesc4 = wx.StaticText(panel, wx.ID_ANY, self.desc4, wx.DefaultPosition, wx.DefaultSize, 0) self.stDesc4.Wrap(dlgWidth - 50) - mainSizer.Add( self.stDesc4, 0, wx.ALL, 5 ) + mainSizer.Add(self.stDesc4, 0, wx.ALL, 5) - self.exportMinimal = wx.CheckBox( panel, wx.ID_ANY, u"Enable minimal export Format", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.exportMinimal = wx.CheckBox(panel, wx.ID_ANY, u"Enable minimal export Format", wx.DefaultPosition, wx.DefaultSize, 0) self.exportMinimal.SetValue(self.HTMLExportSettings.getMinimalEnabled()) self.exportMinimal.Bind(wx.EVT_CHECKBOX, self.OnMinimalEnabledChange) - mainSizer.Add( self.exportMinimal, 0, wx.ALL|wx.EXPAND, 5 ) + mainSizer.Add(self.exportMinimal, 0, wx.ALL | wx.EXPAND, 5) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def setPathLinkCtrlValues(self, path): self.PathLinkCtrl.SetLabel(self.HTMLExportSettings.getPath()) self.PathLinkCtrl.SetURL(u'file:///{}'.format(self.HTMLExportSettings.getPath())) - self.PathLinkCtrl.SetSize(wx.DefaultSize); + self.PathLinkCtrl.SetSize(wx.DefaultSize) self.PathLinkCtrl.Refresh() def selectHTMLExportFilePath(self, event): @@ -95,4 +92,5 @@ class PFHTMLExportPref ( PreferenceView): def getImage(self): return BitmapLoader.getBitmap("prefs_html", "gui") + PFHTMLExportPref.register() diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index cda14dd3b..c8da61bc3 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -7,48 +7,49 @@ import gui.mainFrame from service.settings import NetworkSettings from service.network import Network -class PFNetworkPref ( PreferenceView): + +class PFNetworkPref(PreferenceView): title = "Network" - def populatePanel( self, panel ): + def populatePanel(self, panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.settings = NetworkSettings.getInstance() self.network = Network.getInstance() self.dirtySettings = False - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.cbEnableNetwork = wx.CheckBox( panel, wx.ID_ANY, u"Enable Network", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.cbEnableNetwork, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbEnableNetwork = wx.CheckBox(panel, wx.ID_ANY, u"Enable Network", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.cbEnableNetwork, 0, wx.ALL | wx.EXPAND, 5) - subSizer = wx.BoxSizer( wx.VERTICAL ) - self.cbEve = wx.CheckBox( panel, wx.ID_ANY, u"EVE Servers (API && CREST import)", wx.DefaultPosition, wx.DefaultSize, 0 ) - subSizer.Add( self.cbEve, 0, wx.ALL|wx.EXPAND, 5 ) + subSizer = wx.BoxSizer(wx.VERTICAL) + self.cbEve = wx.CheckBox(panel, wx.ID_ANY, u"EVE Servers (API && CREST import)", wx.DefaultPosition, wx.DefaultSize, 0) + subSizer.Add(self.cbEve, 0, wx.ALL | wx.EXPAND, 5) - self.cbPricing = wx.CheckBox( panel, wx.ID_ANY, u"Pricing updates", wx.DefaultPosition, wx.DefaultSize, 0 ) - subSizer.Add( self.cbPricing, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbPricing = wx.CheckBox(panel, wx.ID_ANY, u"Pricing updates", wx.DefaultPosition, wx.DefaultSize, 0) + subSizer.Add(self.cbPricing, 0, wx.ALL | wx.EXPAND, 5) - self.cbPyfaUpdate = wx.CheckBox( panel, wx.ID_ANY, u"Pyfa Update checks", wx.DefaultPosition, wx.DefaultSize, 0 ) - subSizer.Add( self.cbPyfaUpdate, 0, wx.ALL|wx.EXPAND, 5 ) + self.cbPyfaUpdate = wx.CheckBox(panel, wx.ID_ANY, u"Pyfa Update checks", wx.DefaultPosition, wx.DefaultSize, 0) + subSizer.Add(self.cbPyfaUpdate, 0, wx.ALL | wx.EXPAND, 5) - mainSizer.Add( subSizer, 0, wx.LEFT|wx.EXPAND, 30 ) + mainSizer.Add(subSizer, 0, wx.LEFT | wx.EXPAND, 30) - proxyTitle = wx.StaticText( panel, wx.ID_ANY, "Proxy settings", wx.DefaultPosition, wx.DefaultSize, 0 ) - proxyTitle.Wrap( -1 ) - proxyTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + proxyTitle = wx.StaticText(panel, wx.ID_ANY, "Proxy settings", wx.DefaultPosition, wx.DefaultSize, 0) + proxyTitle.Wrap(-1) + proxyTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - mainSizer.Add( proxyTitle, 0, wx.ALL, 5 ) - mainSizer.Add( wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND, 5 ) + mainSizer.Add(proxyTitle, 0, wx.ALL, 5) + mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 5) self.cbEnableNetwork.SetValue(self.settings.isEnabled(self.network.ENABLED)) self.cbEve.SetValue(self.settings.isEnabled(self.network.EVE)) @@ -62,9 +63,9 @@ class PFNetworkPref ( PreferenceView): self.toggleNetworks(self.cbEnableNetwork.GetValue()) - #--------------- + # --------------- # Proxy - #--------------- + # --------------- self.nMode = self.settings.getMode() self.nAddr = self.settings.getAddress() @@ -74,46 +75,44 @@ class PFNetworkPref ( PreferenceView): if self.nAuth is None: self.nAuth = ("", "") # we don't want None here, it should be a tuple - ptypeSizer = wx.BoxSizer( wx.HORIZONTAL ) + ptypeSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stPType = wx.StaticText( panel, wx.ID_ANY, u"Mode:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stPType.Wrap( -1 ) - ptypeSizer.Add( self.stPType, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.stPType = wx.StaticText(panel, wx.ID_ANY, u"Mode:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPType.Wrap(-1) + ptypeSizer.Add(self.stPType, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.chProxyTypeChoices = [ u"No proxy", u"Auto-detected proxy settings", u"Manual proxy settings" ] - self.chProxyType = wx.Choice( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, self.chProxyTypeChoices, 0 ) + self.chProxyTypeChoices = [u"No proxy", u"Auto-detected proxy settings", u"Manual proxy settings"] + self.chProxyType = wx.Choice(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, self.chProxyTypeChoices, 0) + self.chProxyType.SetSelection(self.nMode) - self.chProxyType.SetSelection( self.nMode ) + ptypeSizer.Add(self.chProxyType, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - ptypeSizer.Add( self.chProxyType, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + mainSizer.Add(ptypeSizer, 0, wx.EXPAND, 5) - mainSizer.Add( ptypeSizer, 0, wx.EXPAND, 5 ) + fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) + fgAddrSizer.AddGrowableCol(1) + fgAddrSizer.SetFlexibleDirection(wx.BOTH) + fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - fgAddrSizer = wx.FlexGridSizer( 2, 2, 0, 0 ) - fgAddrSizer.AddGrowableCol( 1 ) - fgAddrSizer.SetFlexibleDirection( wx.BOTH ) - fgAddrSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + self.stPSetAddr = wx.StaticText(panel, wx.ID_ANY, u"Addr:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetAddr.Wrap(-1) + fgAddrSizer.Add(self.stPSetAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + self.editProxySettingsAddr = wx.TextCtrl(panel, wx.ID_ANY, self.nAddr, wx.DefaultPosition, wx.DefaultSize, 0) - self.stPSetAddr = wx.StaticText( panel, wx.ID_ANY, u"Addr:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stPSetAddr.Wrap( -1 ) - fgAddrSizer.Add( self.stPSetAddr, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + fgAddrSizer.Add(self.editProxySettingsAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.editProxySettingsAddr = wx.TextCtrl( panel, wx.ID_ANY, self.nAddr, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stPSetPort = wx.StaticText(panel, wx.ID_ANY, u"Port:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetPort.Wrap(-1) - fgAddrSizer.Add( self.editProxySettingsAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5 ) + fgAddrSizer.Add(self.stPSetPort, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.stPSetPort = wx.StaticText( panel, wx.ID_ANY, u"Port:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stPSetPort.Wrap( -1 ) + self.editProxySettingsPort = wx.TextCtrl(panel, wx.ID_ANY, self.nPort, wx.DefaultPosition, wx.DefaultSize, 0) - fgAddrSizer.Add( self.stPSetPort, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + fgAddrSizer.Add(self.editProxySettingsPort, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.editProxySettingsPort = wx.TextCtrl( panel, wx.ID_ANY, self.nPort, wx.DefaultPosition, wx.DefaultSize, 0 ) - - fgAddrSizer.Add( self.editProxySettingsPort, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5 ) - - mainSizer.Add( fgAddrSizer, 0, wx.EXPAND, 5) + mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) # proxy auth information: login and pass self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, u"Username:", wx.DefaultPosition, wx.DefaultSize, 0) @@ -130,23 +129,23 @@ class PFNetworkPref ( PreferenceView): pAuthSizer.Add(self.editProxySettingsPassword, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) mainSizer.Add(pAuthSizer, 0, wx.EXPAND, 5) - self.stPSAutoDetected = wx.StaticText( panel, wx.ID_ANY, u"Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stPSAutoDetected.Wrap( -1 ) - mainSizer.Add( self.stPSAutoDetected, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, u"Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSAutoDetected.Wrap(-1) + mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - btnSizer = wx.BoxSizer( wx.HORIZONTAL ) - btnSizer.AddSpacer( ( 0, 0), 1, wx.EXPAND, 5 ) + btnSizer = wx.BoxSizer(wx.HORIZONTAL) + btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - self.btnApply = wx.Button( panel, wx.ID_ANY, u"Apply Proxy Settings", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.btnApply = wx.Button(panel, wx.ID_ANY, u"Apply Proxy Settings", wx.DefaultPosition, wx.DefaultSize, 0) - btnSizer.Add( self.btnApply, 0, wx.ALL, 5 ) + btnSizer.Add(self.btnApply, 0, wx.ALL, 5) - mainSizer.Add(btnSizer, 0, wx.EXPAND,5) + mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) proxy = self.settings.autodetect() if proxy is not None: - addr,port = proxy + addr, port = proxy txt = addr + ":" + str(port) else: txt = "None" @@ -160,7 +159,6 @@ class PFNetworkPref ( PreferenceView): self.editProxySettingsLogin.Bind(wx.EVT_TEXT, self.OnEditPSLoginText) self.editProxySettingsPassword.Bind(wx.EVT_TEXT, self.OnEditPSPasswordText) - self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) self.UpdateApplyButtonState() @@ -170,7 +168,7 @@ class PFNetworkPref ( PreferenceView): else: self.ToggleProxySettings(True) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def toggleNetworks(self, toggle): @@ -265,4 +263,5 @@ class PFNetworkPref ( PreferenceView): def getImage(self): return BitmapLoader.getBitmap("prefs_proxy", "gui") + PFNetworkPref.register() diff --git a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py index c4ec70039..88022be61 100644 --- a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py +++ b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py @@ -1,83 +1,79 @@ import wx -import os from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader from service.settings import UpdateSettings -import gui.globalEvents as GE - -class PFUpdatePref (PreferenceView): +class PFUpdatePref(PreferenceView): title = "Updates" - desc = "Pyfa can automatically check and notify you of new releases. "+\ - "This feature is toggled in the Network settings. "+\ - "Here, you may allow pre-release notifications and view "+\ - "suppressed release notifications, if any." + desc = ("Pyfa can automatically check and notify you of new releases. " + "This feature is toggled in the Network settings. " + "Here, you may allow pre-release notifications and view " + "suppressed release notifications, if any.") - def populatePanel( self, panel ): + def populatePanel(self, panel): self.UpdateSettings = UpdateSettings.getInstance() self.dirtySettings = False dlgWidth = panel.GetParent().GetParent().ClientSize.width - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText( panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stTitle.Wrap( -1 ) - self.stTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) - mainSizer.Add( self.stTitle, 0, wx.ALL, 5 ) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.m_staticline1 = wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline1, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5 ) + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.stDesc = wx.StaticText( panel, wx.ID_ANY, self.desc, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stDesc = wx.StaticText(panel, wx.ID_ANY, self.desc, wx.DefaultPosition, wx.DefaultSize, 0) self.stDesc.Wrap(dlgWidth - 50) - mainSizer.Add( self.stDesc, 0, wx.ALL, 5 ) + mainSizer.Add(self.stDesc, 0, wx.ALL, 5) - self.suppressPrerelease = wx.CheckBox( panel, wx.ID_ANY, u"Allow pre-release notifications", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.suppressPrerelease = wx.CheckBox(panel, wx.ID_ANY, u"Allow pre-release notifications", wx.DefaultPosition, wx.DefaultSize, 0) self.suppressPrerelease.Bind(wx.EVT_CHECKBOX, self.OnPrereleaseStateChange) self.suppressPrerelease.SetValue(not self.UpdateSettings.get('prerelease')) - mainSizer.Add( self.suppressPrerelease, 0, wx.ALL|wx.EXPAND, 5 ) + mainSizer.Add(self.suppressPrerelease, 0, wx.ALL | wx.EXPAND, 5) if (self.UpdateSettings.get('version')): - self.versionSizer = wx.BoxSizer( wx.VERTICAL ) + self.versionSizer = wx.BoxSizer(wx.VERTICAL) + self.versionTitle = wx.StaticText(panel, wx.ID_ANY, "Suppressing {0} Notifications".format(self.UpdateSettings.get('version')), wx.DefaultPosition, wx.DefaultSize, 0) + self.versionTitle.Wrap(-1) + self.versionTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - self.versionTitle = wx.StaticText( panel, wx.ID_ANY, "Suppressing "+self.UpdateSettings.get('version')+" Notifications", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.versionTitle.Wrap( -1 ) - self.versionTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) ) + self.versionInfo = ("There is a release available which you have chosen to suppress. " + "You can choose to reset notification suppression for this release, " + "or download the new release from GitHub.") - self.versionInfo = "There is a release available which you have chosen to suppress. "+\ - "You can choose to reset notification suppression for this release, "+\ - "or download the new release from GitHub." + self.versionSizer.AddSpacer((5, 5), 0, wx.EXPAND, 5) - self.versionSizer.AddSpacer( ( 5, 5), 0, wx.EXPAND, 5 ) + self.versionSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 5) + self.versionSizer.AddSpacer((5, 5), 0, wx.EXPAND, 5) - self.versionSizer.Add( wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND, 5 ) - self.versionSizer.AddSpacer( ( 5, 5), 0, wx.EXPAND, 5 ) - - self.versionSizer.Add( self.versionTitle, 0, wx.EXPAND, 5 ) - self.versionDesc = wx.StaticText( panel, wx.ID_ANY, self.versionInfo, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.versionSizer.Add(self.versionTitle, 0, wx.EXPAND, 5) + self.versionDesc = wx.StaticText(panel, wx.ID_ANY, self.versionInfo, wx.DefaultPosition, wx.DefaultSize, 0) self.versionDesc.Wrap(dlgWidth - 50) - self.versionSizer.Add( self.versionDesc, 0, wx.ALL, 5 ) + self.versionSizer.Add(self.versionDesc, 0, wx.ALL, 5) - actionSizer = wx.BoxSizer( wx.HORIZONTAL ) - resetSizer = wx.BoxSizer( wx.VERTICAL ) + actionSizer = wx.BoxSizer(wx.HORIZONTAL) + resetSizer = wx.BoxSizer(wx.VERTICAL) - self.downloadButton = wx.Button( panel, wx.ID_ANY, "Download", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.downloadButton = wx.Button(panel, wx.ID_ANY, "Download", wx.DefaultPosition, wx.DefaultSize, 0) self.downloadButton.Bind(wx.EVT_BUTTON, self.OnDownload) - resetSizer.Add( self.downloadButton, 0, wx.ALL, 5 ) - actionSizer.Add( resetSizer, 1, wx.EXPAND, 5 ) + resetSizer.Add(self.downloadButton, 0, wx.ALL, 5) + actionSizer.Add(resetSizer, 1, wx.EXPAND, 5) - self.resetButton = wx.Button( panel, wx.ID_ANY, "Reset Suppression", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.resetButton = wx.Button(panel, wx.ID_ANY, "Reset Suppression", wx.DefaultPosition, wx.DefaultSize, 0) self.resetButton.Bind(wx.EVT_BUTTON, self.ResetSuppression) - actionSizer.Add( self.resetButton, 0, wx.ALL, 5 ) - self.versionSizer.Add( actionSizer, 0, wx.EXPAND, 5 ) - mainSizer.Add( self.versionSizer, 0, wx.EXPAND, 5 ) + actionSizer.Add(self.resetButton, 0, wx.ALL, 5) + self.versionSizer.Add(actionSizer, 0, wx.EXPAND, 5) + mainSizer.Add(self.versionSizer, 0, wx.EXPAND, 5) - panel.SetSizer( mainSizer ) + panel.SetSizer(mainSizer) panel.Layout() def OnPrereleaseStateChange(self, event): @@ -95,9 +91,10 @@ class PFUpdatePref (PreferenceView): self.resetButton.Hide() def OnDownload(self, event): - wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/'+self.UpdateSettings.get('version')) + wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/' + self.UpdateSettings.get('version')) def getImage(self): return BitmapLoader.getBitmap("prefs_update", "gui") + PFUpdatePref.register() diff --git a/gui/builtinStatsViews/capacitorViewFull.py b/gui/builtinStatsViews/capacitorViewFull.py index c07045199..a09b2ff91 100644 --- a/gui/builtinStatsViews/capacitorViewFull.py +++ b/gui/builtinStatsViews/capacitorViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,24 +15,26 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount + class CapacitorViewFull(StatsView): name = "capacitorViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent + def getHeaderText(self, fit): return "Capacitor" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -104,12 +106,13 @@ class CapacitorViewFull(StatsView): chargeSizer.Add(lbl, 0, wx.ALIGN_CENTER) chargeSizer.Add(wx.StaticText(parent, wx.ID_ANY, " GJ/s"), 0, wx.ALIGN_CENTER) - def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats= (("label%sCapacitorCapacity", lambda: fit.ship.getModifiedItemAttr("capacitorCapacity"), 3, 0, 9), - ("label%sCapacitorRecharge", lambda: fit.capRecharge, 3, 0, 0), - ("label%sCapacitorDischarge", lambda: fit.capUsed, 3, 0, 0)) + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here + stats = ( + ("label%sCapacitorCapacity", lambda: fit.ship.getModifiedItemAttr("capacitorCapacity"), 3, 0, 9), + ("label%sCapacitorRecharge", lambda: fit.capRecharge, 3, 0, 0), + ("label%sCapacitorDischarge", lambda: fit.capUsed, 3, 0, 0), + ) panel = "Full" for labelName, value, prec, lowest, highest in stats: @@ -147,4 +150,5 @@ class CapacitorViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() + CapacitorViewFull.register() diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 26789a0df..39f7bdbd1 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import gui.mainFrame @@ -170,4 +170,5 @@ class FirepowerViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() + FirepowerViewFull.register() diff --git a/gui/builtinStatsViews/miningyieldViewFull.py b/gui/builtinStatsViews/miningyieldViewFull.py index f96def013..5a3a10212 100644 --- a/gui/builtinStatsViews/miningyieldViewFull.py +++ b/gui/builtinStatsViews/miningyieldViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Alexandros Kosiaris # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import gui.mainFrame @@ -142,4 +142,5 @@ class MiningYieldViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() + MiningYieldViewFull.register() diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index 2829a3fa2..c02f723c1 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,17 +15,18 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.market import Market + class PriceViewFull(StatsView): name = "priceViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent @@ -81,15 +82,15 @@ class PriceViewFull(StatsView): typeIDs.append(mod.itemID) for drone in fit.drones: - for _ in xrange(drone.amount): + for _ in range(drone.amount): typeIDs.append(drone.itemID) for fighter in fit.fighters: - for _ in xrange(fighter.amountActive): + for _ in range(fighter.amountActive): typeIDs.append(fighter.itemID) for cargo in fit.cargo: - for _ in xrange(cargo.amount): + for _ in range(cargo.amount): typeIDs.append(cargo.itemID) sMkt = Market.getInstance() @@ -117,10 +118,11 @@ class PriceViewFull(StatsView): self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(modPrice, 3, 3, 9, currency=True)) self.labelPriceFittings.SetToolTip(wx.ToolTip('{:,.2f}'.format(modPrice))) self._cachedFittings = modPrice - if self._cachedTotal != (shipPrice+modPrice): + if self._cachedTotal != (shipPrice + modPrice): self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(shipPrice + modPrice, 3, 3, 9, currency=True)) self.labelPriceTotal.SetToolTip(wx.ToolTip('{:,.2f}'.format(shipPrice + modPrice))) self._cachedTotal = shipPrice + modPrice self.panel.Layout() + PriceViewFull.register() diff --git a/gui/builtinStatsViews/rechargeViewFull.py b/gui/builtinStatsViews/rechargeViewFull.py index 5d8c1424a..b8055de97 100644 --- a/gui/builtinStatsViews/rechargeViewFull.py +++ b/gui/builtinStatsViews/rechargeViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.statsView import StatsView @@ -25,8 +25,10 @@ import gui.mainFrame import gui.builtinStatsViews.resistancesViewFull as rvf from service.fit import Fit + class RechargeViewFull(StatsView): name = "rechargeViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent @@ -38,7 +40,7 @@ class RechargeViewFull(StatsView): return "Recharge rates" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def toggleEffective(self, event): @@ -53,21 +55,21 @@ class RechargeViewFull(StatsView): self.panel = contentPanel self.headerPanel = headerPanel sizerTankStats = wx.FlexGridSizer(3, 5) - for i in xrange(4): + for i in range(4): sizerTankStats.AddGrowableCol(i + 1) contentSizer.Add(sizerTankStats, 0, wx.EXPAND, 0) - #Add an empty label first for correct alignment. + # Add an empty label first for correct alignment. sizerTankStats.Add(wx.StaticText(contentPanel, wx.ID_ANY, ""), 0) - toolTipText = {"shieldPassive" : "Passive shield recharge", "shieldActive" : "Active shield boost", "armorActive" : "Armor repair amount", "hullActive" : "Hull repair amount"} + toolTipText = {"shieldPassive": "Passive shield recharge", "shieldActive": "Active shield boost", "armorActive": "Armor repair amount", "hullActive": "Hull repair amount"} for tankType in ("shieldPassive", "shieldActive", "armorActive", "hullActive"): bitmap = BitmapLoader.getStaticBitmap("%s_big" % tankType, contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[tankType]) bitmap.SetToolTip(tooltip) sizerTankStats.Add(bitmap, 0, wx.ALIGN_CENTER) - toolTipText = {"reinforced" : "Reinforced", "sustained" : "Sustained"} + toolTipText = {"reinforced": "Reinforced", "sustained": "Sustained"} for stability in ("reinforced", "sustained"): bitmap = BitmapLoader.getStaticBitmap("regen%s_big" % stability.capitalize(), contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[stability]) @@ -79,7 +81,7 @@ class RechargeViewFull(StatsView): continue tankTypeCap = tankType[0].capitalize() + tankType[1:] - lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0.0", style = wx.ALIGN_RIGHT) + lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0.0", style=wx.ALIGN_RIGHT) setattr(self, "labelTank%s%s" % (stability.capitalize(), tankTypeCap), lbl) box = wx.BoxSizer(wx.HORIZONTAL) @@ -91,12 +93,12 @@ class RechargeViewFull(StatsView): contentPanel.Layout() def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here for stability in ("reinforced", "sustained"): - if stability == "reinforced" and fit != None: + if stability == "reinforced" and fit is not None: tank = fit.effectiveTank if self.effective else fit.tank - elif stability == "sustained" and fit != None: + elif stability == "sustained" and fit is not None: tank = fit.effectiveSustainableTank if self.effective else fit.sustainableTank else: tank = None @@ -122,4 +124,5 @@ class RechargeViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() + RechargeViewFull.register() diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index f12860a85..3f56c3727 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.statsView import StatsView @@ -222,5 +222,6 @@ class ResistancesViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() + ResistancesViewFull.register() diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index e0ac0da52..4f3f63b3b 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,11 +15,10 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader from gui import pygauge as PG import gui.mainFrame @@ -29,6 +28,7 @@ from eos.types import Hardpoint from gui.utils.numberFormatter import formatAmount + class ResourcesViewFull(StatsView): name = "resourcesViewFull" contexts = ["drone", "fighter", "cargo"] @@ -79,7 +79,7 @@ class ResourcesViewFull(StatsView): return "Resources" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -99,54 +99,52 @@ class ResourcesViewFull(StatsView): self.headerPanel = headerPanel panel = "full" - base = sizerResources sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - #Turrets & launcher hardslots display - tooltipText = {"turret":"Turret hardpoints", "launcher":"Launcher hardpoints", "drones":"Drones active", "fighter": "Fighter squadrons active", "calibration":"Calibration"} - for type in ("turret", "launcher", "drones", "fighter", "calibration"): + # Turrets & launcher hardslots display + tooltipText = {"turret": "Turret hardpoints", "launcher": "Launcher hardpoints", "drones": "Drones active", "fighter": "Fighter squadrons active", "calibration": "Calibration"} + for type_ in ("turret", "launcher", "drones", "fighter", "calibration"): box = wx.BoxSizer(wx.HORIZONTAL) bitmap = BitmapLoader.getStaticBitmap("%s_big" % type, parent, "gui") - tooltip = wx.ToolTip(tooltipText[type]) + tooltip = wx.ToolTip(tooltipText[type_]) bitmap.SetToolTip(tooltip) box.Add(bitmap, 0, wx.ALIGN_CENTER) sizer.Add(box, 0, wx.ALIGN_CENTER) - suffix = {'turret':'Hardpoints', 'launcher':'Hardpoints', 'drones':'Active', 'fighter':'Tubes', 'calibration':'Points'} + suffix = {'turret': 'Hardpoints', 'launcher': 'Hardpoints', 'drones': 'Active', 'fighter': 'Tubes', 'calibration': 'Points'} lbl = wx.StaticText(parent, wx.ID_ANY, "0") - setattr(self, "label%sUsed%s%s" % (panel.capitalize(), type.capitalize(), suffix[type].capitalize()), lbl) + setattr(self, "label%sUsed%s%s" % (panel.capitalize(), type_.capitalize(), suffix[type_].capitalize()), lbl) box.Add(lbl, 0, wx.ALIGN_CENTER | wx.LEFT, 5) box.Add(wx.StaticText(parent, wx.ID_ANY, "/"), 0, wx.ALIGN_CENTER) lbl = wx.StaticText(parent, wx.ID_ANY, "0") - setattr(self, "label%sTotal%s%s" % (panel.capitalize(), type.capitalize(), suffix[type].capitalize()), lbl) + setattr(self, "label%sTotal%s%s" % (panel.capitalize(), type_.capitalize(), suffix[type_].capitalize()), lbl) box.Add(lbl, 0, wx.ALIGN_CENTER) - setattr(self, "boxSizer{}".format(type.capitalize()), box) + setattr(self, "boxSizer{}".format(type_.capitalize()), box) # Hack - We add a spacer after each thing, but we are always hiding something. The spacer is stil there. # This way, we only have one space after the drones/fighters - if type != "drones": + if type_ != "drones": sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - - #PG, Cpu & drone stuff - tooltipText = {"cpu":"CPU", "pg":"PowerGrid", "droneBay":"Drone bay", "fighterBay": "Fighter bay", "droneBandwidth":"Drone bandwidth", "cargoBay":"Cargo bay"} + # PG, Cpu & drone stuff + tooltipText = {"cpu": "CPU", "pg": "PowerGrid", "droneBay": "Drone bay", "fighterBay": "Fighter bay", "droneBandwidth": "Drone bandwidth", "cargoBay": "Cargo bay"} for i, group in enumerate((("cpu", "pg"), ("cargoBay", "droneBay", "fighterBay", "droneBandwidth"))): main = wx.BoxSizer(wx.VERTICAL) - base.Add(main, 1 , wx.ALIGN_CENTER) + base.Add(main, 1, wx.ALIGN_CENTER) - for type in group: - capitalizedType = type[0].capitalize() + type[1:] - bitmap = BitmapLoader.getStaticBitmap(type + "_big", parent, "gui") - tooltip = wx.ToolTip(tooltipText[type]) + for type_ in group: + capitalizedType = type_[0].capitalize() + type_[1:] + bitmap = BitmapLoader.getStaticBitmap(type_ + "_big", parent, "gui") + tooltip = wx.ToolTip(tooltipText[type_]) bitmap.SetToolTip(tooltip) stats = wx.BoxSizer(wx.VERTICAL) - absolute = wx.BoxSizer(wx.HORIZONTAL) + absolute = wx.BoxSizer(wx.HORIZONTAL) stats.Add(absolute, 0, wx.EXPAND) b = wx.BoxSizer(wx.HORIZONTAL) @@ -166,8 +164,8 @@ class ResourcesViewFull(StatsView): setattr(self, "label%sTotal%s" % (panel.capitalize(), capitalizedType), lbl) absolute.Add(lbl, 0, wx.ALIGN_LEFT) - units = {"cpu":" tf", "pg":" MW", "droneBandwidth":" mbit/s", "droneBay":u" m\u00B3", "fighterBay":u" m\u00B3", "cargoBay":u" m\u00B3"} - lbl = wx.StaticText(parent, wx.ID_ANY, "%s" % units[type]) + units = {"cpu": " tf", "pg": " MW", "droneBandwidth": " mbit/s", "droneBay": u" m\u00B3", "fighterBay": u" m\u00B3", "cargoBay": u" m\u00B3"} + lbl = wx.StaticText(parent, wx.ID_ANY, "%s" % units[type_]) absolute.Add(lbl, 0, wx.ALIGN_LEFT) # Gauges modif. - Darriele @@ -177,7 +175,7 @@ class ResourcesViewFull(StatsView): gauge.SetMinSize((self.getTextExtentW("1.999M/1.99M MW"), 23)) gauge.SetFractionDigits(2) - setattr(self, "gauge%s%s" % (panel.capitalize(),capitalizedType), gauge) + setattr(self, "gauge%s%s" % (panel.capitalize(), capitalizedType), gauge) stats.Add(gauge, 0, wx.ALIGN_CENTER) setattr(self, "base%s%s" % (panel.capitalize(), capitalizedType), b) @@ -186,30 +184,32 @@ class ResourcesViewFull(StatsView): self.toggleContext("drone") def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats = (("label%sUsedTurretHardpoints", lambda: fit.getHardpointsUsed(Hardpoint.TURRET), 0, 0, 0), - ("label%sTotalTurretHardpoints", lambda: fit.ship.getModifiedItemAttr('turretSlotsLeft'), 0, 0, 0), - ("label%sUsedLauncherHardpoints", lambda: fit.getHardpointsUsed(Hardpoint.MISSILE), 0, 0, 0), - ("label%sTotalLauncherHardpoints", lambda: fit.ship.getModifiedItemAttr('launcherSlotsLeft'), 0, 0, 0), - ("label%sUsedDronesActive", lambda: fit.activeDrones, 0, 0, 0), - ("label%sTotalDronesActive", lambda: fit.extraAttributes["maxActiveDrones"], 0, 0, 0), - ("label%sUsedFighterTubes", lambda: fit.fighterTubesUsed, 3, 0, 9), - ("label%sTotalFighterTubes", lambda: fit.ship.getModifiedItemAttr("fighterTubes"), 3, 0, 9), - ("label%sUsedCalibrationPoints", lambda: fit.calibrationUsed, 0, 0, 0), - ("label%sTotalCalibrationPoints", lambda: fit.ship.getModifiedItemAttr('upgradeCapacity'), 0, 0, 0), - ("label%sUsedPg", lambda: fit.pgUsed, 4, 0, 9), - ("label%sUsedCpu", lambda: fit.cpuUsed, 4, 0, 9), - ("label%sTotalPg", lambda: fit.ship.getModifiedItemAttr("powerOutput"), 4, 0, 9), - ("label%sTotalCpu", lambda: fit.ship.getModifiedItemAttr("cpuOutput"), 4, 0, 9), - ("label%sUsedDroneBay", lambda: fit.droneBayUsed, 3, 0, 9), - ("label%sUsedFighterBay", lambda: fit.fighterBayUsed, 3, 0, 9), - ("label%sUsedDroneBandwidth", lambda: fit.droneBandwidthUsed, 3, 0, 9), - ("label%sTotalDroneBay", lambda: fit.ship.getModifiedItemAttr("droneCapacity"), 3, 0, 9), - ("label%sTotalDroneBandwidth", lambda: fit.ship.getModifiedItemAttr("droneBandwidth"), 3, 0, 9), - ("label%sTotalFighterBay", lambda: fit.ship.getModifiedItemAttr("fighterCapacity"), 3, 0, 9), - ("label%sUsedCargoBay", lambda: fit.cargoBayUsed, 3, 0, 9), - ("label%sTotalCargoBay", lambda: fit.ship.getModifiedItemAttr("capacity"), 3, 0, 9)) + stats = ( + ("label%sUsedTurretHardpoints", lambda: fit.getHardpointsUsed(Hardpoint.TURRET), 0, 0, 0), + ("label%sTotalTurretHardpoints", lambda: fit.ship.getModifiedItemAttr('turretSlotsLeft'), 0, 0, 0), + ("label%sUsedLauncherHardpoints", lambda: fit.getHardpointsUsed(Hardpoint.MISSILE), 0, 0, 0), + ("label%sTotalLauncherHardpoints", lambda: fit.ship.getModifiedItemAttr('launcherSlotsLeft'), 0, 0, 0), + ("label%sUsedDronesActive", lambda: fit.activeDrones, 0, 0, 0), + ("label%sTotalDronesActive", lambda: fit.extraAttributes["maxActiveDrones"], 0, 0, 0), + ("label%sUsedFighterTubes", lambda: fit.fighterTubesUsed, 3, 0, 9), + ("label%sTotalFighterTubes", lambda: fit.ship.getModifiedItemAttr("fighterTubes"), 3, 0, 9), + ("label%sUsedCalibrationPoints", lambda: fit.calibrationUsed, 0, 0, 0), + ("label%sTotalCalibrationPoints", lambda: fit.ship.getModifiedItemAttr('upgradeCapacity'), 0, 0, 0), + ("label%sUsedPg", lambda: fit.pgUsed, 4, 0, 9), + ("label%sUsedCpu", lambda: fit.cpuUsed, 4, 0, 9), + ("label%sTotalPg", lambda: fit.ship.getModifiedItemAttr("powerOutput"), 4, 0, 9), + ("label%sTotalCpu", lambda: fit.ship.getModifiedItemAttr("cpuOutput"), 4, 0, 9), + ("label%sUsedDroneBay", lambda: fit.droneBayUsed, 3, 0, 9), + ("label%sUsedFighterBay", lambda: fit.fighterBayUsed, 3, 0, 9), + ("label%sUsedDroneBandwidth", lambda: fit.droneBandwidthUsed, 3, 0, 9), + ("label%sTotalDroneBay", lambda: fit.ship.getModifiedItemAttr("droneCapacity"), 3, 0, 9), + ("label%sTotalDroneBandwidth", lambda: fit.ship.getModifiedItemAttr("droneBandwidth"), 3, 0, 9), + ("label%sTotalFighterBay", lambda: fit.ship.getModifiedItemAttr("fighterCapacity"), 3, 0, 9), + ("label%sUsedCargoBay", lambda: fit.cargoBayUsed, 3, 0, 9), + ("label%sTotalCargoBay", lambda: fit.ship.getModifiedItemAttr("capacity"), 3, 0, 9), + ) panel = "Full" usedTurretHardpoints = 0 totalTurretHardpoints = 0 @@ -303,12 +303,14 @@ class ResourcesViewFull(StatsView): labelTCP.SetForegroundColour(colorC) if fit is not None: - resMax = (lambda: fit.ship.getModifiedItemAttr("cpuOutput"), - lambda: fit.ship.getModifiedItemAttr("powerOutput"), - lambda: fit.ship.getModifiedItemAttr("droneCapacity"), - lambda: fit.ship.getModifiedItemAttr("fighterCapacity"), - lambda: fit.ship.getModifiedItemAttr("droneBandwidth"), - lambda: fit.ship.getModifiedItemAttr("capacity")) + resMax = ( + lambda: fit.ship.getModifiedItemAttr("cpuOutput"), + lambda: fit.ship.getModifiedItemAttr("powerOutput"), + lambda: fit.ship.getModifiedItemAttr("droneCapacity"), + lambda: fit.ship.getModifiedItemAttr("fighterCapacity"), + lambda: fit.ship.getModifiedItemAttr("droneBandwidth"), + lambda: fit.ship.getModifiedItemAttr("capacity"), + ) i = 0 for resourceType in ("cpu", "pg", "droneBay", "fighterBay", "droneBandwidth", "cargoBay"): @@ -316,11 +318,11 @@ class ResourcesViewFull(StatsView): capitalizedType = resourceType[0].capitalize() + resourceType[1:] gauge = getattr(self, "gauge%s%s" % (panel, capitalizedType)) - resUsed = getattr(fit,"%sUsed" % resourceType) + resUsed = getattr(fit, "%sUsed" % resourceType) gauge.SetValueRange(resUsed or 0, resMax[i]() or 0) - i+=1 + i += 1 else: capitalizedType = resourceType[0].capitalize() + resourceType[1:] @@ -328,9 +330,10 @@ class ResourcesViewFull(StatsView): gauge.SetValueRange(0, 0) - i+=1 + i += 1 self.panel.Layout() self.headerPanel.Layout() + ResourcesViewFull.register() diff --git a/gui/builtinStatsViews/targetingMiscViewFull.py b/gui/builtinStatsViews/targetingMiscViewFull.py index 207fe46c5..e5be6110a 100644 --- a/gui/builtinStatsViews/targetingMiscViewFull.py +++ b/gui/builtinStatsViews/targetingMiscViewFull.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,29 +15,31 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.utils.numberFormatter import formatAmount -import locale + try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict + class TargetingMiscViewFull(StatsView): name = "targetingmiscViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent self._cachedValues = [] + def getHeaderText(self, fit): return "Targeting && Misc" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -46,7 +48,7 @@ class TargetingMiscViewFull(StatsView): self.panel = contentPanel self.headerPanel = headerPanel gridTargetingMisc = wx.FlexGridSizer(1, 3) - contentSizer.Add( gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0) + contentSizer.Add(gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0) gridTargetingMisc.AddGrowableCol(0) gridTargetingMisc.AddGrowableCol(2) # Targeting @@ -68,17 +70,17 @@ class TargetingMiscViewFull(StatsView): box = wx.BoxSizer(wx.HORIZONTAL) gridTargeting.Add(box, 0, wx.ALIGN_LEFT) - lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" %unit) + lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" % unit) setattr(self, "label%s" % labelShort, lbl) box.Add(lbl, 0, wx.ALIGN_LEFT) self._cachedValues.append({"main": 0}) # Misc - gridTargetingMisc.Add( wx.StaticLine( contentPanel, wx.ID_ANY, style = wx.VERTICAL),0, wx.EXPAND, 3 ) + gridTargetingMisc.Add(wx.StaticLine(contentPanel, wx.ID_ANY, style=wx.VERTICAL), 0, wx.EXPAND, 3) gridMisc = wx.FlexGridSizer(5, 2) gridMisc.AddGrowableCol(1) - gridTargetingMisc.Add(gridMisc,0 , wx.ALIGN_LEFT | wx.ALL, 5) + gridTargetingMisc.Add(gridMisc, 0, wx.ALIGN_LEFT | wx.ALL, 5) labels = (("Speed", "Speed", "m/s"), ("Align time", "AlignTime", "s"), @@ -98,9 +100,8 @@ class TargetingMiscViewFull(StatsView): self._cachedValues.append({"main": 0}) - def refreshPanel(self, fit): - #If we did anything interesting, we'd update our labels to reflect the new fit's stats here + # If we did anything interesting, we'd update our labels to reflect the new fit's stats here cargoNamesOrder = OrderedDict(( ("fleetHangarCapacity", "Fleet hangar"), @@ -155,9 +156,9 @@ class TargetingMiscViewFull(StatsView): ("labelFullCargo", cargoValues, 4, 0, 9, u"m\u00B3")) counter = 0 - RADII = [("Pod",25), ("Interceptor",33), ("Frigate",38), + RADII = [("Pod", 25), ("Interceptor", 33), ("Frigate", 38), ("Destroyer", 83), ("Cruiser", 130), - ("Battlecruiser", 265), ("Battleship",420), + ("Battlecruiser", 265), ("Battleship", 420), ("Carrier", 3000)] for labelName, valueDict, prec, lowest, highest, unit in stats: label = getattr(self, labelName) @@ -173,13 +174,13 @@ class TargetingMiscViewFull(StatsView): # Get sum of all cargoholds except for maintenance bay additionalCargo = sum(otherValues.values()) if additionalCargo > 0: - label.SetLabel("%s+%s %s" %(formatAmount(mainValue, prec, lowest, highest), - formatAmount(additionalCargo, prec, lowest, highest), - unit)) + label.SetLabel("%s+%s %s" % (formatAmount(mainValue, prec, lowest, highest), + formatAmount(additionalCargo, prec, lowest, highest), + unit)) else: - label.SetLabel("%s %s" %(formatAmount(mainValue, prec, lowest, highest), unit)) + label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit)) else: - label.SetLabel("%s %s" %(formatAmount(mainValue, prec, lowest, highest), unit)) + label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit)) # Tooltip stuff if fit: if labelName == "labelScanRes": @@ -187,21 +188,21 @@ class TargetingMiscViewFull(StatsView): for size, radius in RADII: left = "%.1fs" % fit.calculateLockTime(radius) right = "%s [%d]" % (size, radius) - lockTime += "%5s\t%s\n" % (left,right) + lockTime += "%5s\t%s\n" % (left, right) label.SetToolTip(wx.ToolTip(lockTime)) elif labelName == "labelFullSigRadius": - label.SetToolTip(wx.ToolTip("Probe Size: %.3f" % (fit.probeSize or 0) )) + label.SetToolTip(wx.ToolTip("Probe Size: %.3f" % (fit.probeSize or 0))) elif labelName == "labelFullWarpSpeed": label.SetToolTip(wx.ToolTip("Max Warp Distance: %.1f AU" % fit.maxWarpDistance)) elif labelName == "labelSensorStr": if fit.jamChance > 0: - label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance))) + label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance))) else: - label.SetToolTip(wx.ToolTip("Type: %s" % (fit.scanType))) + label.SetToolTip(wx.ToolTip("Type: %s" % (fit.scanType))) elif labelName == "labelFullAlignTime": - alignTime = "Align:\t%.3fs"%mainValue + alignTime = "Align:\t%.3fs" % mainValue mass = 'Mass:\t{:,.0f}kg'.format(fit.ship.getModifiedItemAttr("mass")) - agility = "Agility:\t%.3fx"%(fit.ship.getModifiedItemAttr("agility") or 0) + agility = "Agility:\t%.3fx" % (fit.ship.getModifiedItemAttr("agility") or 0) label.SetToolTip(wx.ToolTip("%s\n%s\n%s" % (alignTime, mass, agility))) elif labelName == "labelFullCargo": tipLines = [] @@ -247,4 +248,5 @@ class TargetingMiscViewFull(StatsView): self.panel.Layout() self.headerPanel.Layout() + TargetingMiscViewFull.register() diff --git a/gui/builtinViewColumns/abilities.py b/gui/builtinViewColumns/abilities.py index f61557446..e42a2eed3 100644 --- a/gui/builtinViewColumns/abilities.py +++ b/gui/builtinViewColumns/abilities.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -16,17 +15,18 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -from gui.viewColumn import ViewColumn -import gui.mainFrame import wx from eos.types import Fighter +from gui.viewColumn import ViewColumn +import gui.mainFrame class Abilities(ViewColumn): name = "Fighter Abilities" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) @@ -41,4 +41,5 @@ class Abilities(ViewColumn): return "None" return ", ".join(active) + Abilities.register() diff --git a/gui/builtinViewColumns/ammo.py b/gui/builtinViewColumns/ammo.py index 698e8595b..27a742bce 100644 --- a/gui/builtinViewColumns/ammo.py +++ b/gui/builtinViewColumns/ammo.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,17 +15,17 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -from gui import builtinViewColumns -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader import wx from eos.types import Fighter +from gui.viewColumn import ViewColumn +from gui.bitmapLoader import BitmapLoader class Ammo(ViewColumn): name = "Ammo" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) self.mask = wx.LIST_MASK_IMAGE @@ -43,7 +43,7 @@ class Ammo(ViewColumn): charges = stuff.numCharges if charges > 0: cycles = stuff.numShots - if cycles !=0 and charges != cycles: + if cycles != 0 and charges != cycles: return "%s (%d, %d cycles)" % (stuff.charge.name, charges, cycles) else: return "%s (%d)" % (stuff.charge.name, charges) @@ -54,5 +54,5 @@ class Ammo(ViewColumn): def getImageId(self, mod): return -1 -Ammo.register() +Ammo.register() diff --git a/gui/builtinViewColumns/ammoIcon.py b/gui/builtinViewColumns/ammoIcon.py index 2403077bd..1dc6906e3 100644 --- a/gui/builtinViewColumns/ammoIcon.py +++ b/gui/builtinViewColumns/ammoIcon.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,16 +15,16 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -from gui import builtinViewColumns from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader import wx from eos.types import Module + class AmmoIcon(ViewColumn): name = "Ammo Icon" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) self.size = 24 @@ -52,4 +52,5 @@ class AmmoIcon(ViewColumn): if isinstance(mod, Module) and mod.charge is not None: return mod.charge.name + AmmoIcon.register() diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index c8bda870c..26994a956 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,9 +15,10 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + +import wx -from gui import builtinViewColumns from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount @@ -25,10 +26,10 @@ from gui.utils.numberFormatter import formatAmount from service.attribute import Attribute from service.market import Market -import wx class AttributeDisplay(ViewColumn): name = "attr" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) sAttr = Attribute.getInstance() @@ -60,8 +61,9 @@ class AttributeDisplay(ViewColumn): self.view = fittingView originalRefresh = fittingView.refresh sMkt = Market.getInstance() - #Hack into our master view and add a callback for ourselves to know when to query + def refresh(stuff): + # Hack into our master view and add a callback for ourselves to know when to query self.directInfo = sMkt.directAttrRequest(stuff, info) if stuff else None originalRefresh(stuff) @@ -78,10 +80,10 @@ class AttributeDisplay(ViewColumn): attr = mod.getAttribute(self.info.name) if self.info.name == "volume": - str = (formatAmount(attr, 3, 0, 3)) + str_ = (formatAmount(attr, 3, 0, 3)) if hasattr(mod, "amount"): - str = str + u"m\u00B3 (%s m\u00B3)"%(formatAmount(attr*mod.amount, 3, 0, 3)) - attr = str + str_ = str_ + u"m\u00B3 (%s m\u00B3)" % (formatAmount(attr * mod.amount, 3, 0, 3)) + attr = str_ if isinstance(attr, (float, int)): attr = (formatAmount(attr, 3, 0, 3)) @@ -104,4 +106,5 @@ class AttributeDisplay(ViewColumn): ("showIcon", bool, True), ("direct", bool, False)) + AttributeDisplay.register() diff --git a/gui/builtinViewColumns/baseIcon.py b/gui/builtinViewColumns/baseIcon.py index 868c73a21..d767a97a6 100644 --- a/gui/builtinViewColumns/baseIcon.py +++ b/gui/builtinViewColumns/baseIcon.py @@ -1,11 +1,11 @@ -from gui import builtinViewColumns -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader import wx from eos.types import Drone, Fit, Module, Slot, Rack, Implant +from gui.viewColumn import ViewColumn + class BaseIcon(ViewColumn): name = "Base Icon" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) self.size = 24 @@ -17,16 +17,16 @@ class BaseIcon(ViewColumn): def getImageId(self, stuff): if isinstance(stuff, Drone): return -1 - if isinstance(stuff, Fit): + elif isinstance(stuff, Fit): return self.shipImage - if isinstance(stuff, Rack): + elif isinstance(stuff, Rack): return -1 - if isinstance(stuff, Implant): + elif isinstance(stuff, Implant): if stuff.character: # if it has a character as it's parent return self.fittingView.imageList.GetImageIndex("character_small", "gui") else: return self.shipImage - if isinstance(stuff, Module): + elif isinstance(stuff, Module): if stuff.isEmpty: return self.fittingView.imageList.GetImageIndex("slot_%s_small" % Slot.getName(stuff.slot).lower(), "gui") else: @@ -41,4 +41,5 @@ class BaseIcon(ViewColumn): else: return -1 + BaseIcon.register() diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index 9381509a0..9018ac453 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -16,18 +15,18 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +import wx +from eos.types import Drone, Cargo, Module, Slot, Rack, Implant, Fighter +from service.fit import Fit from gui.viewColumn import ViewColumn import gui.mainFrame -#from eos.saveddata.fit import Fit -from service.fit import Fit -import wx -from eos.types import Drone, Cargo, Module, Slot, Rack, Implant, Fighter class BaseName(ViewColumn): name = "Base Name" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) @@ -40,7 +39,7 @@ class BaseName(ViewColumn): def getText(self, stuff): if isinstance(stuff, Drone): return "%dx %s" % (stuff.amount, stuff.item.name) - if isinstance(stuff, Fighter): + elif isinstance(stuff, Fighter): return "%d/%d %s" % (stuff.amountActive, stuff.getModifiedItemAttr("fighterSquadronMaxSize"), stuff.item.name) elif isinstance(stuff, Cargo): return "%dx %s" % (stuff.amount, stuff.item.name) @@ -74,10 +73,11 @@ class BaseName(ViewColumn): if marketShortcut: # use unicode subscript to display shortcut value - shortcut = unichr(marketShortcut+8320)+u" " + shortcut = unichr(marketShortcut + 8320) + u" " del item.marketShortcut - return shortcut+item.name + return shortcut + item.name return item.name + BaseName.register() diff --git a/gui/builtinViewColumns/capacitorUse.py b/gui/builtinViewColumns/capacitorUse.py index 9648d4fde..cc5efa16c 100644 --- a/gui/builtinViewColumns/capacitorUse.py +++ b/gui/builtinViewColumns/capacitorUse.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,25 +15,26 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx +from eos.types import Mode +from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader -from eos.types import Mode -from service.attribute import Attribute + class CapacitorUse(ViewColumn): name = "Capacitor Usage" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) self.mask = wx.LIST_MASK_IMAGE - sAttr = Attribute.getInstance() - info = sAttr.getAttributeInfo("capacitorNeed") + Attribute.getInstance().getAttributeInfo("capacitorNeed") self.imageId = fittingView.imageList.GetImageIndex("capacitorRecharge_small", "gui") self.bitmap = BitmapLoader.getBitmap("capacitorRecharge_small", "gui") @@ -53,4 +54,5 @@ class CapacitorUse(ViewColumn): def getToolTip(self, mod): return self.name + CapacitorUse.register() diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py index 8f3b4f4eb..ea26d4d67 100644 --- a/gui/builtinViewColumns/maxRange.py +++ b/gui/builtinViewColumns/maxRange.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,23 +15,24 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -from gui import builtinViewColumns +import wx + +from eos.types import Mode +from service.attribute import Attribute from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount -import wx -from eos.types import Mode -from service.attribute import Attribute class MaxRange(ViewColumn): name = "Max Range" - def __init__(self, fittingView, params = None): - if params == None: - params = {"showIcon": True, - "displayName": False} + + def __init__(self, fittingView, params=None): + if params is None: + params = {"showIcon": True, "displayName": False} + ViewColumn.__init__(self, fittingView) sAttr = Attribute.getInstance() @@ -72,10 +73,10 @@ class MaxRange(ViewColumn): return -1 def getParameters(self): - return (("displayName", bool, False), - ("showIcon", bool, True)) + return (("displayName", bool, False), ("showIcon", bool, True)) def getToolTip(self, mod): return "Optimal + Falloff" + MaxRange.register() diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index 0c7f9ef8f..95a8b07bd 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,24 +15,26 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +import wx +from service.fit import Fit +from service.market import Market import gui.mainFrame from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount from gui.utils.listFormatter import formatList -from service.fit import Fit, Market -import wx class Miscellanea(ViewColumn): name = "Miscellanea" - def __init__(self, fittingView, params = None): - if params == None: - params = {"showIcon": True, - "displayName": False} + + def __init__(self, fittingView, params=None): + if params is None: + params = {"showIcon": True, "displayName": False} + ViewColumn.__init__(self, fittingView) if params["showIcon"]: self.imageId = fittingView.imageList.GetImageIndex("column_misc", "gui") @@ -47,19 +49,16 @@ class Miscellanea(ViewColumn): self.mainFrame = gui.mainFrame.MainFrame.getInstance() def getText(self, stuff): - text = self.__getData(stuff)[0] - return text + return self.__getData(stuff)[0] def getToolTip(self, mod): - text = self.__getData(mod)[1] - return text + return self.__getData(mod)[1] def getImageId(self, mod): return -1 def getParameters(self): - return (("displayName", bool, False), - ("showIcon", bool, True)) + return (("displayName", bool, False), ("showIcon", bool, True)) def __getData(self, stuff): item = stuff.item @@ -81,10 +80,10 @@ class Miscellanea(ViewColumn): slots = ("hi", "med", "low") info = [] for slot in slots: - n = int(stuff.getModifiedItemAttr("%sSlotModifier"%slot)) + n = int(stuff.getModifiedItemAttr("%sSlotModifier" % slot)) if n > 0: info.append("{0}{1}".format(n, slot[0].upper())) - return "+ "+", ".join(info), "Slot Modifiers" + return "+ " + ", ".join(info), "Slot Modifiers" elif itemGroup == "Energy Neutralizer": neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") cycleTime = stuff.cycleTime @@ -276,7 +275,7 @@ class Miscellanea(ViewColumn): recalibration = stuff.getModifiedItemAttr("cloakingTargetingDelay") if recalibration is None: return "", None - text = "{0}s".format(formatAmount(float(recalibration)/1000, 3, 0, 3)) + text = "{0}s".format(formatAmount(float(recalibration) / 1000, 3, 0, 3)) tooltip = "Sensor recalibration time" return text, tooltip elif itemGroup == "Remote Armor Repairer": @@ -487,10 +486,10 @@ class Miscellanea(ViewColumn): return text, tooltip elif itemGroup == "Armor Resistance Shift Hardener": - itemArmorResistanceShiftHardenerEM = (1-stuff.getModifiedItemAttr("armorEmDamageResonance"))*100 - itemArmorResistanceShiftHardenerTherm = (1-stuff.getModifiedItemAttr("armorThermalDamageResonance")) * 100 - itemArmorResistanceShiftHardenerKin = (1-stuff.getModifiedItemAttr("armorKineticDamageResonance")) * 100 - itemArmorResistanceShiftHardenerExp = (1-stuff.getModifiedItemAttr("armorExplosiveDamageResonance")) * 100 + itemArmorResistanceShiftHardenerEM = (1 - stuff.getModifiedItemAttr("armorEmDamageResonance")) * 100 + itemArmorResistanceShiftHardenerTherm = (1 - stuff.getModifiedItemAttr("armorThermalDamageResonance")) * 100 + itemArmorResistanceShiftHardenerKin = (1 - stuff.getModifiedItemAttr("armorKineticDamageResonance")) * 100 + itemArmorResistanceShiftHardenerExp = (1 - stuff.getModifiedItemAttr("armorExplosiveDamageResonance")) * 100 text = "{0}% | {1}% | {2}% | {3}%".format( formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), @@ -540,4 +539,5 @@ class Miscellanea(ViewColumn): else: return "", None + Miscellanea.register() diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 6f1f95eb2..68ad87e1e 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos, Lucas Thode # # This file is part of pyfa. @@ -15,17 +15,20 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +import wx + +from eos.types import Drone, Cargo +from service.market import Market from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount -from eos.types import Drone, Cargo -import wx -from service.market import Market + class Price(ViewColumn): name = "Price" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) self.mask = wx.LIST_MASK_IMAGE @@ -45,21 +48,22 @@ class Price(ViewColumn): price = price.price # Set new price variable with what we need if isinstance(stuff, Drone) or isinstance(stuff, Cargo): - price *= stuff.amount + price *= stuff.amount return formatAmount(price, 3, 3, 9, currency=True) def delayedText(self, mod, display, colItem): sMkt = Market.getInstance() + def callback(item): price = sMkt.getPriceNow(item.ID) text = formatAmount(price.price, 3, 3, 9, currency=True) if price.price else "" - if price.failed: text += " (!)" + if price.failed: + text += " (!)" colItem.SetText(text) display.SetItem(colItem) - sMkt.waitForPrice(mod.item, callback) def getImageId(self, mod): @@ -68,4 +72,5 @@ class Price(ViewColumn): def getToolTip(self, mod): return self.name + Price.register() diff --git a/gui/builtinViewColumns/propertyDisplay.py b/gui/builtinViewColumns/propertyDisplay.py index cb1008fb1..71f22ca5d 100644 --- a/gui/builtinViewColumns/propertyDisplay.py +++ b/gui/builtinViewColumns/propertyDisplay.py @@ -1,69 +1,73 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -#=============================================================================== - -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader -from gui.utils.numberFormatter import formatAmount -import wx - -class PropertyDisplay(ViewColumn): - name = "prop" - def __init__(self, fittingView, params): - ViewColumn.__init__(self, fittingView) - sAttr = Attribute.getInstance() - attributeSlave = params["attributeSlave"] or params["property"] - # This function can throw an exception if the database isn't sane - # We need to do a sanity check before this runs - info = sAttr.getAttributeInfo(attributeSlave) - - self.mask = 0 - self.propertyName = params["property"] - self.info = info - if params["showIcon"]: - if info.name == "power": - iconFile = "pg_small" - iconType = "gui" - else: - iconFile = info.icon.iconFile if info.icon else None - iconType = "icons" - if iconFile: - self.imageId = fittingView.imageList.GetImageIndex(iconFile, iconType) - else: - self.imageId = -1 - else: - self.imageId = -1 - - if params["displayName"] or self.imageId == -1: - self.columnText = info.displayName if info.displayName != "" else info.name - - def getText(self, stuff): - attr = getattr(stuff, self.propertyName, None) - if attr: - return (formatAmount(attr, 3, 0, 3)) - else: - return "" - - @staticmethod - def getParameters(): - return (("property", str, None), - ("attributeSlave", str, None), - ("displayName", bool, False), - ("showIcon", bool, True)) - -PropertyDisplay.register() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +from gui.viewColumn import ViewColumn +from gui.bitmapLoader import BitmapLoader +from gui.utils.numberFormatter import formatAmount +from service.attribute import Attribute +import wx + + +class PropertyDisplay(ViewColumn): + name = "prop" + + def __init__(self, fittingView, params): + ViewColumn.__init__(self, fittingView) + sAttr = Attribute.getInstance() + attributeSlave = params["attributeSlave"] or params["property"] + # This function can throw an exception if the database isn't sane + # We need to do a sanity check before this runs + info = sAttr.getAttributeInfo(attributeSlave) + + self.mask = 0 + self.propertyName = params["property"] + self.info = info + if params["showIcon"]: + if info.name == "power": + iconFile = "pg_small" + iconType = "gui" + else: + iconFile = info.icon.iconFile if info.icon else None + iconType = "icons" + if iconFile: + self.imageId = fittingView.imageList.GetImageIndex(iconFile, iconType) + else: + self.imageId = -1 + else: + self.imageId = -1 + + if params["displayName"] or self.imageId == -1: + self.columnText = info.displayName if info.displayName != "" else info.name + + def getText(self, stuff): + attr = getattr(stuff, self.propertyName, None) + if attr: + return (formatAmount(attr, 3, 0, 3)) + else: + return "" + + @staticmethod + def getParameters(): + return (("property", str, None), + ("attributeSlave", str, None), + ("displayName", bool, False), + ("showIcon", bool, True)) + + +PropertyDisplay.register() diff --git a/gui/builtinViewColumns/state.py b/gui/builtinViewColumns/state.py index cc3dee59b..b6c8698ec 100644 --- a/gui/builtinViewColumns/state.py +++ b/gui/builtinViewColumns/state.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,18 +15,21 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader -import gui.mainFrame import wx + from eos.types import Drone, Module, Rack, Fit, Implant from eos.types import State as State_ +from gui.viewColumn import ViewColumn + +import gui.mainFrame + class State(ViewColumn): name = "State" + def __init__(self, fittingView, params): ViewColumn.__init__(self, fittingView) self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -78,4 +81,5 @@ class State(ViewColumn): return generic_active return generic_inactive + State.register() diff --git a/gui/builtinViews/emptyView.py b/gui/builtinViews/emptyView.py index 3088c65a1..0dc8d958e 100644 --- a/gui/builtinViews/emptyView.py +++ b/gui/builtinViews/emptyView.py @@ -3,6 +3,7 @@ import gui.globalEvents as GE import gui.chromeTabs import gui.mainFrame + class BlankPage(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent, size=(0, 0)) @@ -22,8 +23,8 @@ class BlankPage(wx.Panel): def pageChanged(self, event): if self.parent.IsActive(self): fitID = None -# sFit = Fit.getInstance() -# sFit.switchFit(fitID) + # sFit = Fit.getInstance() + # sFit.switchFit(fitID) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - event.Skip() \ No newline at end of file + event.Skip() diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index f4cac9753..cdcddde14 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import wx.lib.newevent @@ -36,7 +36,8 @@ from service.market import Market import gui.globalEvents as GE -#Tab spawning handler + +# Tab spawning handler class FitSpawner(gui.multiSwitch.TabSpawner): def __init__(self, multiSwitch): self.multiSwitch = multiSwitch @@ -86,9 +87,11 @@ class FitSpawner(gui.multiSwitch.TabSpawner): self.multiSwitch.AddPage(view) view.handleDrag(type, fitID) + FitSpawner.register() -#Drag'n'drop handler + +# Drag'n'drop handler class FittingViewDrop(wx.PyDropTarget): def __init__(self, dropFn): wx.PyDropTarget.__init__(self) @@ -103,6 +106,7 @@ class FittingViewDrop(wx.PyDropTarget): self.dropFn(x, y, data) return t + class FittingView(d.Display): DEFAULT_COLS = ["State", "Ammo Icon", @@ -118,7 +122,7 @@ class FittingView(d.Display): ] def __init__(self, parent): - d.Display.__init__(self, parent, size = (0,0), style = wx.BORDER_NONE) + d.Display.__init__(self, parent, size=(0, 0), style=wx.BORDER_NONE) self.Show(False) self.parent = parent self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) @@ -128,7 +132,7 @@ class FittingView(d.Display): self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) @@ -193,7 +197,7 @@ class FittingView(d.Display): self.addModule(x, y, int(data[1])) def handleDrag(self, type, fitID): - #Those are drags coming from pyfa sources, NOT builtin wx drags + # Those are drags coming from pyfa sources, NOT builtin wx drags if type == "fit": wx.PostEvent(self.mainFrame, gui.shipBrowser.FitSelected(fitID=fitID)) @@ -223,11 +227,11 @@ class FittingView(d.Display): if row != -1 and row not in self.blanks: data = wx.PyTextDataObject() - data.SetText("fitting:"+str(self.mods[row].position)) + data.SetText("fitting:" + str(self.mods[row].position)) dropSource = wx.DropSource(self) dropSource.SetData(data) - res = dropSource.DoDragDrop() + dropSource.DoDragDrop() def getSelectedMods(self): sel = [] @@ -238,15 +242,14 @@ class FittingView(d.Display): return sel - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() - firstSel = row while row != -1: if row not in self.blanks: self.removeModule(self.mods[row]) - self.Select(row,0) + self.Select(row, 0) row = self.GetNextSelected(row) event.Skip() @@ -309,7 +312,7 @@ class FittingView(d.Display): if self.parent.IsActive(self): itemID = event.itemID fitID = self.activeFitID - if fitID != None: + if fitID is not None: sFit = Fit.getInstance() if sFit.isAmmo(itemID): modules = [] @@ -349,7 +352,6 @@ class FittingView(d.Display): def addModule(self, x, y, srcIdx): '''Add a module from the market browser''' - mstate = wx.GetMouseState() dstRow, _ = self.HitTest((x, y)) if dstRow != -1 and dstRow not in self.blanks: @@ -433,12 +435,12 @@ class FittingView(d.Display): for i, mod in enumerate(self.mods): if mod.slot != slotDivider: slotDivider = mod.slot - self.blanks.append((i, slotDivider)) # where and what + self.blanks.append((i, slotDivider)) # where and what # second loop modifies self.mods, rewrites self.blanks to represent actual index of blanks for i, (x, slot) in enumerate(self.blanks): - self.blanks[i] = x+i # modify blanks with actual index - self.mods.insert(x+i, Rack.buildRack(slot)) + self.blanks[i] = x + i # modify blanks with actual index + self.mods.insert(x + i, Rack.buildRack(slot)) if fit.mode: # Modes are special snowflakes and need a little manual loving @@ -494,13 +496,13 @@ class FittingView(d.Display): srcContext = "fittingModule" itemContext = sMkt.getCategoryByItem(mod.item).name fullContext = (srcContext, itemContext) - if not srcContext in tuple(fCtxt[0] for fCtxt in contexts): + if srcContext not in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) if mod.charge is not None: srcContext = "fittingCharge" itemContext = sMkt.getCategoryByItem(mod.charge).name fullContext = (srcContext, itemContext) - if not srcContext in tuple(fCtxt[0] for fCtxt in contexts): + if srcContext not in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) selection.append(mod) @@ -530,7 +532,7 @@ class FittingView(d.Display): sel = [] curr = self.GetFirstSelected() - while curr != -1 and row not in self.blanks : + while curr != -1 and row not in self.blanks: sel.append(curr) curr = self.GetNextSelected(curr) @@ -611,7 +613,7 @@ class FittingView(d.Display): try: self.MakeSnapshot() except: - pass + pass def OnShow(self, event): if event.GetShow(): @@ -624,19 +626,19 @@ class FittingView(d.Display): def Snapshot(self): return self.FVsnapshot - def MakeSnapshot(self, maxColumns = 1337): + def MakeSnapshot(self, maxColumns=1337): if self.FVsnapshot: del self.FVsnapshot - tbmp = wx.EmptyBitmap(16,16) + tbmp = wx.EmptyBitmap(16, 16) tdc = wx.MemoryDC() tdc.SelectObject(tbmp) font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) tdc.SetFont(font) columnsWidths = [] - for i in xrange(len(self.DEFAULT_COLS)): + for i in range(len(self.DEFAULT_COLS)): columnsWidths.append(0) sFit = Fit.getInstance() @@ -660,25 +662,25 @@ class FittingView(d.Display): maxWidth = 0 maxRowHeight = isize rows = 0 - for id,st in enumerate(self.mods): + for st in self.mods: for i, col in enumerate(self.activeColumns): - if i>maxColumns: + if i > maxColumns: break name = col.getText(st) if not isinstance(name, basestring): name = "" - nx,ny = tdc.GetTextExtent(name) + nx, ny = tdc.GetTextExtent(name) imgId = col.getImageId(st) cw = 0 if imgId != -1: cw += isize + padding if name != "": - cw += nx + 4*padding + cw += nx + 4 * padding if imgId == -1 and name == "": - cw += isize +padding + cw += isize + padding maxRowHeight = max(ny, maxRowHeight) columnsWidths[i] = max(columnsWidths[i], cw) @@ -687,7 +689,7 @@ class FittingView(d.Display): render = wx.RendererNative.Get() - #Fix column widths (use biggest between header or items) + # Fix column widths (use biggest between header or items) for i, col in enumerate(self.activeColumns): if i > maxColumns: @@ -705,26 +707,23 @@ class FittingView(d.Display): opts.m_labelText = name if imgId != -1: - opts.m_labelBitmap = wx.EmptyBitmap(isize,isize) + opts.m_labelBitmap = wx.EmptyBitmap(isize, isize) - width = render.DrawHeaderButton(self, tdc, (0, 0, 16, 16), - sortArrow = wx.HDR_SORT_ICON_NONE, params = opts) + width = render.DrawHeaderButton(self, tdc, (0, 0, 16, 16), sortArrow=wx.HDR_SORT_ICON_NONE, params=opts) columnsWidths[i] = max(columnsWidths[i], width) tdc.SelectObject(wx.NullBitmap) - maxWidth = padding * 2 - for i in xrange(len(self.DEFAULT_COLS)): + for i in range(len(self.DEFAULT_COLS)): if i > maxColumns: break maxWidth += columnsWidths[i] - mdc = wx.MemoryDC() - mbmp = wx.EmptyBitmap(maxWidth, (maxRowHeight) * rows + padding*4 + headerSize) + mbmp = wx.EmptyBitmap(maxWidth, (maxRowHeight) * rows + padding * 4 + headerSize) mdc.SelectObject(mbmp) @@ -754,8 +753,8 @@ class FittingView(d.Display): bmp = col.bitmap opts.m_labelBitmap = bmp - width = render.DrawHeaderButton (self, mdc, (cx, padding, columnsWidths[i], headerSize), wx.CONTROL_CURRENT, - sortArrow = wx.HDR_SORT_ICON_NONE, params = opts) + width = render.DrawHeaderButton(self, mdc, (cx, padding, columnsWidths[i], headerSize), wx.CONTROL_CURRENT, + sortArrow=wx.HDR_SORT_ICON_NONE, params=opts) cx += columnsWidths[i] @@ -765,15 +764,15 @@ class FittingView(d.Display): mdc.SetPen(pen) mdc.SetBrush(brush) - cy = padding*2 + headerSize - for id,st in enumerate(self.mods): + cy = padding * 2 + headerSize + for st in self.mods: cx = padding if slotMap[st.slot]: - mdc.DrawRectangle(cx,cy,maxWidth - cx,maxRowHeight) + mdc.DrawRectangle(cx, cy, maxWidth - cx, maxRowHeight) for i, col in enumerate(self.activeColumns): - if i>maxColumns: + if i > maxColumns: break name = col.getText(st) @@ -784,14 +783,14 @@ class FittingView(d.Display): tcx = cx if imgId != -1: - self.imageList.Draw(imgId,mdc,cx,cy,wx.IMAGELIST_DRAW_TRANSPARENT,False) + self.imageList.Draw(imgId, mdc, cx, cy, wx.IMAGELIST_DRAW_TRANSPARENT, False) tcx += isize + padding if name != "": - nx,ny = mdc.GetTextExtent(name) + nx, ny = mdc.GetTextExtent(name) rect = wx.Rect() rect.top = cy - rect.left = cx + 2*padding + rect.left = cx + 2 * padding rect.width = nx rect.height = maxRowHeight + padding mdc.DrawLabel(name, rect, wx.ALIGN_CENTER_VERTICAL) @@ -803,4 +802,4 @@ class FittingView(d.Display): mdc.SelectObject(wx.NullBitmap) - self.FVsnapshot = mbmp \ No newline at end of file + self.FVsnapshot = mbmp diff --git a/gui/builtinViews/fleetView.py b/gui/builtinViews/fleetView.py index 7112778f3..3b4347ff2 100644 --- a/gui/builtinViews/fleetView.py +++ b/gui/builtinViews/fleetView.py @@ -1,139 +1,142 @@ -import wx.gizmos -import gui.fleetBrowser -from gui.bitmapLoader import BitmapLoader - -#Tab spawning handler -class FleetSpawner(gui.multiSwitch.TabSpawner): - def __init__(self, multiSwitch): - self.multiSwitch = multiSwitch - mainFrame = gui.mainFrame.MainFrame.getInstance() - mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_SELECTED, self.fleetSelected) - - def fleetSelected(self, event): - if self.multiSwitch.GetPageCount() == 0: - self.multiSwitch.AddPage(wx.Panel(self.multiSwitch, size = (0,0)), "Empty Tab") - - view = FleetView(self.multiSwitch) - self.multiSwitch.ReplaceActivePage(view) - view.populate(event.fleetID) - view.Show() - -FleetSpawner.register() - -class FleetView(wx.gizmos.TreeListCtrl): - def __init__(self, parent, size = (0,0)): - wx.gizmos.TreeListCtrl.__init__(self, parent, size = size) - - self.tabManager = parent - - self.fleetId = None - self.fleetImg = BitmapLoader.getImage("53_16", "icons") - - self.imageList = wx.ImageList(16, 16) - self.SetImageList(self.imageList) - - for col in ("", "Fit", "Shiptype", "Character", "Bonusses"): - self.AddColumn(col) - - self.SetMainColumn(1) - self.icons = {} - self.addImage = self.imageList.Add(BitmapLoader.getBitmap("add_small", "gui")) - for icon in ("fb", "fc", "sb", "sc", "wb", "wc"): - self.icons[icon] = self.imageList.Add(BitmapLoader.getBitmap("fleet_%s_small" % icon, "gui")) - - self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.checkNew) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_RENAMED, self.fleetRenamed) - self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_REMOVED, self.fleetRemoved) - - def Destroy(self): - self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_REMOVED, handler = self.fleetRemoved) - self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_RENAMED, handler = self.fleetRenamed) - wx.gizmos.TreeListCtrl.Destroy(self) - - def fleetRenamed(self, event): - if event.fleetID == self.fleetId: - sFleet = service.Fleet.getInstance() - f = sFleet.getFleetByID(event.fleetID) - self.UpdateTab(f.name, self.fleetImg) - - event.Skip() - - def fleetRemoved(self, event): - if event.fleetID == self.fleetId: - self.tabManager.DeletePage(self.tabManager.GetPageIndex(self)) - - event.Skip() - - def checkNew(self, event): - data = self.GetPyData(event.Item) - if data and isinstance(data, tuple) and data[0] == "add": - layer = data[1] - - - def UpdateTab(self, name, img): - self.tabManager.SetPageTextIcon(self.tabManager.GetSelection(), name, img) - - def populate(self, fleetID): - sFleet = service.Fleet.getInstance() - f = sFleet.getFleetByID(fleetID) - self.fleetId = fleetID - - self.UpdateTab( f.name, self.fleetImg) - self.fleet = f - self.DeleteAllItems() - root = self.AddRoot("") - - self.setEntry(root, f.leader, "fleet", f) - for wing in f.wings: - wingId = self.AppendItem(root, "") - self.setEntry(wingId, wing.leader, "wing", wing) - for squad in wing.squads: - for member in squad.members: - memberId = self.AppendItem(wingId, "") - self.setEntry(memberId, member, "squad", squad) - - self.addAdder(wingId, "squad") - - self.addAdder(root, "wing") - - self.ExpandAll(root) - self.SetColumnWidth(0, 16) - for i in xrange(1, 5): - self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) - headerWidth = self.GetColumnWidth(i) + 5 - self.SetColumnWidth(i, wx.LIST_AUTOSIZE) - baseWidth = self.GetColumnWidth(i) - if baseWidth < headerWidth: - self.SetColumnWidth(i, headerWidth) - else: - self.SetColumnWidth(i, baseWidth) - - - def addAdder(self, treeItemId, layer): - id = self.AppendItem(treeItemId, "Add new %s" % layer.capitalize()) - self.SetPyData(id, ("add", layer)) - self.SetItemImage(id, self.addImage, 1) - - def setEntry(self, treeItemId, fit, layer, info): - self.SetPyData(treeItemId, info) - if fit is None: - self.SetItemText(treeItemId, "%s Commander" % layer.capitalize(), 1) - else: - fleet = self.fleet - if fit == info.booster: - self.SetItemImage(treeItemId, self.icons["%sb" % layer[0]], 0) - elif fit == info.leader: - self.SetItemImage(treeItemId, self.icons["%sc" % layer[0]], 1) - - self.SetItemText(treeItemId, fit.name, 1) - self.SetItemText(treeItemId, fit.ship.item.name, 2) - self.SetItemText(treeItemId, fit.character.name, 3) - boosts = fleet.store.getBoosts(fit) - if boosts: - bonusses = [] - for name, info in boosts.iteritems(): - bonusses.append("%s: %.2g" % (name, info[0])) - - self.SetItemText(treeItemId, ", ".join(bonusses), 3) +import wx.gizmos +import gui.fleetBrowser +from gui.bitmapLoader import BitmapLoader +from service.fleet import Fleet + + +# Tab spawning handler +class FleetSpawner(gui.multiSwitch.TabSpawner): + def __init__(self, multiSwitch): + self.multiSwitch = multiSwitch + mainFrame = gui.mainFrame.MainFrame.getInstance() + mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_SELECTED, self.fleetSelected) + + def fleetSelected(self, event): + if self.multiSwitch.GetPageCount() == 0: + self.multiSwitch.AddPage(wx.Panel(self.multiSwitch, size=(0, 0)), "Empty Tab") + + view = FleetView(self.multiSwitch) + self.multiSwitch.ReplaceActivePage(view) + view.populate(event.fleetID) + view.Show() + + +FleetSpawner.register() + + +class FleetView(wx.gizmos.TreeListCtrl): + def __init__(self, parent, size=(0, 0)): + wx.gizmos.TreeListCtrl.__init__(self, parent, size=size) + + self.tabManager = parent + + self.fleetId = None + self.fleetImg = BitmapLoader.getImage("53_16", "icons") + + self.imageList = wx.ImageList(16, 16) + self.SetImageList(self.imageList) + + for col in ("", "Fit", "Shiptype", "Character", "Bonusses"): + self.AddColumn(col) + + self.SetMainColumn(1) + self.icons = {} + self.addImage = self.imageList.Add(BitmapLoader.getBitmap("add_small", "gui")) + for icon in ("fb", "fc", "sb", "sc", "wb", "wc"): + self.icons[icon] = self.imageList.Add(BitmapLoader.getBitmap("fleet_%s_small" % icon, "gui")) + + self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.checkNew) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_RENAMED, self.fleetRenamed) + self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_REMOVED, self.fleetRemoved) + + def Destroy(self): + self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_REMOVED, handler=self.fleetRemoved) + self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_RENAMED, handler=self.fleetRenamed) + wx.gizmos.TreeListCtrl.Destroy(self) + + def fleetRenamed(self, event): + if event.fleetID == self.fleetId: + sFleet = Fleet.getInstance() + f = sFleet.getFleetByID(event.fleetID) + self.UpdateTab(f.name, self.fleetImg) + + event.Skip() + + def fleetRemoved(self, event): + if event.fleetID == self.fleetId: + self.tabManager.DeletePage(self.tabManager.GetPageIndex(self)) + + event.Skip() + + def checkNew(self, event): + data = self.GetPyData(event.Item) + if data and isinstance(data, tuple) and data[0] == "add": + layer = data[1] + + + def UpdateTab(self, name, img): + self.tabManager.SetPageTextIcon(self.tabManager.GetSelection(), name, img) + + def populate(self, fleetID): + sFleet = Fleet.getInstance() + f = sFleet.getFleetByID(fleetID) + self.fleetId = fleetID + + self.UpdateTab(f.name, self.fleetImg) + self.fleet = f + self.DeleteAllItems() + root = self.AddRoot("") + + self.setEntry(root, f.leader, "fleet", f) + for wing in f.wings: + wingId = self.AppendItem(root, "") + self.setEntry(wingId, wing.leader, "wing", wing) + for squad in wing.squads: + for member in squad.members: + memberId = self.AppendItem(wingId, "") + self.setEntry(memberId, member, "squad", squad) + + self.addAdder(wingId, "squad") + + self.addAdder(root, "wing") + + self.ExpandAll(root) + self.SetColumnWidth(0, 16) + for i in range(1, 5): + self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) + headerWidth = self.GetColumnWidth(i) + 5 + self.SetColumnWidth(i, wx.LIST_AUTOSIZE) + baseWidth = self.GetColumnWidth(i) + if baseWidth < headerWidth: + self.SetColumnWidth(i, headerWidth) + else: + self.SetColumnWidth(i, baseWidth) + + def addAdder(self, treeItemId, layer): + id_ = self.AppendItem(treeItemId, "Add new %s" % layer.capitalize()) + self.SetPyData(id_, ("add", layer)) + self.SetItemImage(id_, self.addImage, 1) + + def setEntry(self, treeItemId, fit, layer, info): + self.SetPyData(treeItemId, info) + if fit is None: + self.SetItemText(treeItemId, "%s Commander" % layer.capitalize(), 1) + else: + fleet = self.fleet + if fit == info.booster: + self.SetItemImage(treeItemId, self.icons["%sb" % layer[0]], 0) + elif fit == info.leader: + self.SetItemImage(treeItemId, self.icons["%sc" % layer[0]], 1) + + self.SetItemText(treeItemId, fit.name, 1) + self.SetItemText(treeItemId, fit.ship.item.name, 2) + self.SetItemText(treeItemId, fit.character.name, 3) + boosts = fleet.store.getBoosts(fit) + if boosts: + bonusses = [] + for name, info in boosts.iteritems(): + bonusses.append("%s: %.2g" % (name, info[0])) + + self.SetItemText(treeItemId, ", ".join(bonusses), 3) diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index 851f49754..b06a0ea5c 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -1,10 +1,12 @@ import wx -import gui.display as d -from gui.bitmapLoader import BitmapLoader -import gui.PFSearchBox as SBox -from gui.marketBrowser import SearchBox from wx.lib.buttons import GenBitmapButton + from service.market import Market +import gui.display as d +import gui.PFSearchBox as SBox +from gui.bitmapLoader import BitmapLoader +from gui.marketBrowser import SearchBox + class BaseImplantEditorView (wx.Panel): def addMarketViewImage(self, iconFile): @@ -17,7 +19,7 @@ class BaseImplantEditorView (wx.Panel): return self.availableImplantsImageList.Add(bitmap) def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -46,25 +48,23 @@ class BaseImplantEditorView (wx.Panel): availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND) - pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) - buttonSizer = wx.BoxSizer(wx.VERTICAL) - buttonSizer.AddSpacer(( 0, 0), 1) + buttonSizer.AddSpacer((0, 0), 1) - self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style = wx.BORDER_NONE) + self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style=wx.BORDER_NONE) buttonSizer.Add(self.btnAdd, 0) - self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style = wx.BORDER_NONE) + self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style=wx.BORDER_NONE) buttonSizer.Add(self.btnRemove, 0) - buttonSizer.AddSpacer(( 0, 0), 1) + buttonSizer.AddSpacer((0, 0), 1) pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 0) characterImplantSizer = wx.BoxSizer(wx.VERTICAL) self.pluggedImplantsTree = AvailableImplantsView(self) - characterImplantSizer.Add(self.pluggedImplantsTree, 1, wx.ALL|wx.EXPAND, 5) + characterImplantSizer.Add(self.pluggedImplantsTree, 1, wx.ALL | wx.EXPAND, 5) pmainSizer.Add(characterImplantSizer, 1, wx.EXPAND, 5) self.SetSizer(pmainSizer) @@ -80,13 +80,13 @@ class BaseImplantEditorView (wx.Panel): self.availableImplantsTree.SortChildren(self.availableRoot) - #Bind the event to replace dummies by real data + # Bind the event to replace dummies by real data self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.itemSelected) self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemSelected) - #Bind add & remove buttons + # Bind add & remove buttons self.btnAdd.Bind(wx.EVT_BUTTON, self.itemSelected) self.btnRemove.Bind(wx.EVT_BUTTON, self.removeItem) @@ -136,7 +136,7 @@ class BaseImplantEditorView (wx.Panel): # if the dummy item is a market group, replace with actual market groups if text == "dummy": - #Add 'real stoof!' instead + # Add 'real stoof!' instead currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent), eager="children") for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(childMktGrp)) @@ -194,6 +194,7 @@ class BaseImplantEditorView (wx.Panel): self.removeImplantFromContext(self.implants[pos]) self.update() + class AvailableImplantsView(d.Display): DEFAULT_COLS = ["attr:implantness", "Base Icon", @@ -203,6 +204,7 @@ class AvailableImplantsView(d.Display): d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL) self.Bind(wx.EVT_LEFT_DCLICK, parent.removeItem) + class ItemView(d.Display): DEFAULT_COLS = ["Base Icon", "Base Name", @@ -255,4 +257,4 @@ class ItemView(d.Display): self.items = sorted(list(items), key=lambda i: i.name) - self.update(self.items) \ No newline at end of file + self.update(self.items) diff --git a/gui/cachingImageList.py b/gui/cachingImageList.py index 4ec0b2f69..aceff6775 100644 --- a/gui/cachingImageList.py +++ b/gui/cachingImageList.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,21 +15,23 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + import wx from gui.bitmapLoader import BitmapLoader + class CachingImageList(wx.ImageList): def __init__(self, width, height): wx.ImageList.__init__(self, width, height) self.map = {} def GetImageIndex(self, *loaderArgs): - id = self.map.get(loaderArgs) - if id is None: + id_ = self.map.get(loaderArgs) + if id_ is None: bitmap = BitmapLoader.getBitmap(*loaderArgs) if bitmap is None: return -1 - id = self.map[loaderArgs] = wx.ImageList.Add(self,bitmap) - return id + id_ = self.map[loaderArgs] = wx.ImageList.Add(self, bitmap) + return id_ diff --git a/gui/cargoView.py b/gui/cargoView.py index a6b93c1ef..527b41691 100644 --- a/gui/cargoView.py +++ b/gui/cargoView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,17 +15,17 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import gui.display as d -import gui.marketBrowser as mb from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import globalEvents as GE from service.fit import Fit from service.market import Market + class CargoViewDrop(wx.PyDropTarget): def __init__(self, dropFn): wx.PyDropTarget.__init__(self) @@ -40,6 +40,7 @@ class CargoViewDrop(wx.PyDropTarget): self.dropFn(x, y, data) return t + # @todo: Was copied form another class and modified. Look through entire file, refine class CargoView(d.Display): DEFAULT_COLS = ["Base Icon", @@ -59,7 +60,7 @@ class CargoView(d.Display): self.SetDropTarget(CargoViewDrop(self.handleListDrag)) self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) @@ -85,13 +86,13 @@ class CargoView(d.Display): if row != -1: data = wx.PyTextDataObject() - data.SetText("cargo:"+str(row)) + data.SetText("cargo:" + str(row)) dropSource = wx.DropSource(self) dropSource.SetData(data) - res = dropSource.DoDragDrop() + dropSource.DoDragDrop() - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() @@ -112,16 +113,16 @@ class CargoView(d.Display): # Gather module information to get position module = fit.modules[modIdx] - if dstRow != -1: # we're swapping with cargo - if mstate.CmdDown(): # if copying, append to cargo + if dstRow != -1: # we're swapping with cargo + if mstate.CmdDown(): # if copying, append to cargo sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) - else: # else, move / swap + else: # else, move / swap sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, dstRow) - else: # dragging to blank spot, append + else: # dragging to blank spot, append sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) - if not mstate.CmdDown(): # if not copying, remove module - sFit.removeModule(self.mainFrame.getActiveFit(), module.position) + if not mstate.CmdDown(): # if not copying, remove module + sFit.removeModule(self.mainFrame.getActiveFit(), module.position) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) @@ -131,7 +132,7 @@ class CargoView(d.Display): self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -140,7 +141,8 @@ class CargoView(d.Display): self.original = fit.cargo if fit is not None else None self.cargo = stuff = fit.cargo if fit is not None else None - if stuff is not None: stuff.sort(key=lambda cargo: cargo.itemID) + if stuff is not None: + stuff.sort(key=lambda cargo: cargo.itemID) if event.fitID != self.lastFitId: self.lastFitId = event.fitID diff --git a/gui/characterEditor.py b/gui/characterEditor.py index e551ce9fa..e9d512a74 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx diff --git a/gui/characterSelection.py b/gui/characterSelection.py index 7ff6be57e..b63f87475 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.bitmapLoader import BitmapLoader @@ -24,6 +24,7 @@ import gui.mainFrame from service.character import Character from service.fit import Fit + class CharacterSelection(wx.Panel): def __init__(self, parent): self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -43,9 +44,9 @@ class CharacterSelection(wx.Panel): self.refreshCharacterList() self.cleanSkills = BitmapLoader.getBitmap("skill_big", "gui") - self.redSkills = BitmapLoader.getBitmap("skillRed_big", "gui") + self.redSkills = BitmapLoader.getBitmap("skillRed_big", "gui") self.greenSkills = BitmapLoader.getBitmap("skillGreen_big", "gui") - self.refresh = BitmapLoader.getBitmap("refresh", "gui") + self.refresh = BitmapLoader.getBitmap("refresh", "gui") self.btnRefresh = wx.BitmapButton(self, wx.ID_ANY, self.refresh) size = self.btnRefresh.GetSize() @@ -67,7 +68,7 @@ class CharacterSelection(wx.Panel): self.mainFrame.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList) self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) - self.SetMinSize(wx.Size(25,-1)) + self.SetMinSize(wx.Size(25, -1)) self.charChoice.Enable(False) @@ -138,16 +139,16 @@ class CharacterSelection(wx.Panel): def selectChar(self, charID): choice = self.charChoice numItems = len(choice.GetItems()) - for i in xrange(numItems): - id = choice.GetClientData(i) - if id == charID: + for i in range(numItems): + id_ = choice.GetClientData(i) + if id_ == charID: choice.SetSelection(i) return True return False def fitChanged(self, event): - self.charChoice.Enable(event.fitID != None) + self.charChoice.Enable(event.fitID is not None) choice = self.charChoice sFit = Fit.getInstance() currCharID = choice.GetClientData(choice.GetCurrentSelection()) @@ -159,23 +160,23 @@ class CharacterSelection(wx.Panel): else: sCharacter = Character.getInstance() reqs = sCharacter.checkRequirements(fit) - sCharacter.skillReqsDict = {'charname':fit.character.name, 'skills':[]} + sCharacter.skillReqsDict = {'charname': fit.character.name, 'skills': []} if len(reqs) == 0: tip = "All skill prerequisites have been met" self.skillReqsStaticBitmap.SetBitmap(self.greenSkills) else: - tip = "Skills required:\n" + tip = "Skills required:\n" condensed = sFit.serviceFittingOptions["compactSkills"] if condensed: - dict = self._buildSkillsTooltipCondensed(reqs, skillsMap = {}) - for key in sorted(dict): - tip += "%s: %d\n" % (key, dict[key]) + dict_ = self._buildSkillsTooltipCondensed(reqs, skillsMap={}) + for key in sorted(dict_): + tip += "%s: %d\n" % (key, dict_[key]) else: tip += self._buildSkillsTooltip(reqs) self.skillReqsStaticBitmap.SetBitmap(self.redSkills) self.skillReqsStaticBitmap.SetToolTipString(tip.strip()) - if newCharID == None: + if newCharID is None: sChar = Character.getInstance() self.selectChar(sChar.all5ID()) @@ -183,10 +184,9 @@ class CharacterSelection(wx.Panel): self.selectChar(newCharID) self.charChanged(None) - event.Skip() - def _buildSkillsTooltip(self, reqs, currItem = "", tabulationLevel = 0): + def _buildSkillsTooltip(self, reqs, currItem="", tabulationLevel=0): tip = "" sCharacter = Character.getInstance() @@ -198,11 +198,11 @@ class CharacterSelection(wx.Panel): for name, info in reqs.iteritems(): level, ID, more = info sCharacter.skillReqsDict['skills'].append({ - 'item' : currItem, - 'skillID' : ID, - 'skill' : name, - 'level' : level, - 'indent' : tabulationLevel + 'item': currItem, + 'skillID': ID, + 'skill': name, + 'level': level, + 'indent': tabulationLevel, }) tip += "%s%s: %d\n" % (" " * tabulationLevel, name, level) @@ -210,7 +210,10 @@ class CharacterSelection(wx.Panel): return tip - def _buildSkillsTooltipCondensed(self, reqs, currItem = "", tabulationLevel = 0, skillsMap = {}): + def _buildSkillsTooltipCondensed(self, reqs, currItem="", tabulationLevel=0, skillsMap=None): + if skillsMap is None: + skillsMap = {} + sCharacter = Character.getInstance() if tabulationLevel == 0: @@ -221,11 +224,11 @@ class CharacterSelection(wx.Panel): for name, info in reqs.iteritems(): level, ID, more = info sCharacter.skillReqsDict['skills'].append({ - 'item' : currItem, - 'skillID' : ID, - 'skill' : name, - 'level' : level, - 'indent' : tabulationLevel + 'item': currItem, + 'skillID': ID, + 'skill': name, + 'level': level, + 'indent': tabulationLevel, }) if name not in skillsMap: @@ -233,6 +236,6 @@ class CharacterSelection(wx.Panel): elif skillsMap[name] < level: skillsMap[name] = level - skillMap = self._buildSkillsTooltipCondensed(more, currItem, tabulationLevel + 1, skillsMap) + skillsMap = self._buildSkillsTooltipCondensed(more, currItem, tabulationLevel + 1, skillsMap) return skillsMap diff --git a/gui/chromeTabs.py b/gui/chromeTabs.py index db1ad694c..be2342622 100644 --- a/gui/chromeTabs.py +++ b/gui/chromeTabs.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Darriele # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import wx.lib.newevent @@ -23,7 +23,6 @@ import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.fonts as fonts from gui.bitmapLoader import BitmapLoader -import gui.utils.fonts as fonts from service.fit import Fit @@ -35,6 +34,7 @@ _PageClosing, EVT_NOTEBOOK_PAGE_CLOSING = wx.lib.newevent.NewEvent() PageAdded, EVT_NOTEBOOK_PAGE_ADDED = wx.lib.newevent.NewEvent() PageClosed, EVT_NOTEBOOK_PAGE_CLOSED = wx.lib.newevent.NewEvent() + class VetoAble(): def __init__(self): self.__vetoed = False @@ -92,7 +92,6 @@ class PageAdding(_PageAdding, VetoAble): class PFNotebook(wx.Panel): - def __init__(self, parent, canAdd=True): """ Instance of Pyfa Notebook. Initializes general layout, includes methods @@ -101,8 +100,8 @@ class PFNotebook(wx.Panel): parent - wx parent element canAdd - True if tabs be deleted and added, passed directly to PFTabsContainer - """ + wx.Panel.__init__(self, parent, wx.ID_ANY, size=(-1, -1)) self.pages = [] @@ -235,7 +234,7 @@ class PFNotebook(wx.Panel): try: # Set page to the first non-disabled page self.SetSelection(next(i for i, _ in enumerate(self.pages) if not self.tabsContainer.tabs[i].disabled)) - except StopIteration, ex: + except StopIteration: self.SetSelection(0) self.tabsContainer.DisableTab(idx, toggle) @@ -338,7 +337,7 @@ class PFNotebook(wx.Panel): class PFTabRenderer: - def __init__(self, size=(36, 24), text=wx.EmptyString, img=None, inclination=6 , closeButton=True): + def __init__(self, size=(36, 24), text=wx.EmptyString, img=None, inclination=6, closeButton=True): """ Renders a new tab @@ -433,7 +432,7 @@ class PFTabRenderer: mdc.SelectObject(ebmp) mdc.SetFont(self.font) textSizeX, textSizeY = mdc.GetTextExtent(self.text) - totalSize = self.leftWidth + self.rightWidth + textSizeX + self.closeBtnWidth/2 + 16 + self.padding*2 + totalSize = self.leftWidth + self.rightWidth + textSizeX + self.closeBtnWidth / 2 + 16 + self.padding * 2 mdc.SelectObject(wx.NullBitmap) return totalSize, self.tabHeight @@ -498,7 +497,7 @@ class PFTabRenderer: mdc = wx.MemoryDC() mdc.SelectObject(bkbmp) - #mdc.SetBackground(wx.Brush((0x12, 0x23, 0x32))) + # mdc.SetBackground(wx.Brush((0x12, 0x23, 0x32))) mdc.Clear() mdc.DrawBitmap(self.ctabLeftBmp, 0, 0) # set the left bitmap @@ -507,14 +506,14 @@ class PFTabRenderer: cm = self.ctabMiddleBmp.ConvertToImage() mimg = cm.Scale(self.contentWidth, self.ctabMiddle.GetHeight(), wx.IMAGE_QUALITY_NORMAL) mbmp = wx.BitmapFromImage(mimg) - mdc.DrawBitmap(mbmp, self.leftWidth, 0 ) # set middle bitmap, offset by left + mdc.DrawBitmap(mbmp, self.leftWidth, 0) # set middle bitmap, offset by left # set right bitmap offset by left + middle mdc.DrawBitmap(self.ctabRightBmp, self.contentWidth + self.leftWidth, 0) mdc.SelectObject(wx.NullBitmap) - #bkbmp.SetMaskColour((0x12, 0x23, 0x32)) + # bkbmp.SetMaskColour((0x12, 0x23, 0x32)) if self.tabBackBitmap: del self.tabBackBitmap @@ -528,7 +527,7 @@ class PFTabRenderer: """ self.tabRegion = wx.RegionFromBitmap(self.tabBackBitmap) self.closeBtnRegion = wx.RegionFromBitmap(self.ctabCloseBmp) - self.closeBtnRegion.Offset(self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth()/2, (self.tabHeight - self.ctabCloseBmp.GetHeight())/2) + self.closeBtnRegion.Offset(self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth() / 2, (self.tabHeight - self.ctabCloseBmp.GetHeight()) / 2) def InitColors(self): """Determines colors used for tab, based on system settings""" @@ -546,33 +545,33 @@ class PFTabRenderer: height = self.tabHeight - #rect = wx.Rect(0, 0, self.tabWidth, self.tabHeight) + # rect = wx.Rect(0, 0, self.tabWidth, self.tabHeight) canvas = wx.EmptyBitmap(self.tabWidth, self.tabHeight, 24) mdc = wx.MemoryDC() mdc.SelectObject(canvas) - #mdc.SetBackground(wx.Brush ((0x12,0x23,0x32))) + # mdc.SetBackground(wx.Brush ((0x12,0x23,0x32))) mdc.Clear() - #r = copy.copy(rect) - #r.top = r.left = 0 - #r.height = height + # r = copy.copy(rect) + # r.top = r.left = 0 + # r.height = height mdc.DrawBitmap(self.tabBackBitmap, 0, 0, True) if self.tabImg: bmp = wx.BitmapFromImage(self.tabImg.ConvertToGreyscale() if self.disabled else self.tabImg) if self.contentWidth > 16: # @todo: is this conditional relevant anymore? # Draw tab icon - mdc.DrawBitmap(bmp, self.leftWidth + self.padding - bmp.GetWidth()/2, (height - bmp.GetHeight())/2) - textStart = self.leftWidth + self.padding + bmp.GetWidth()/2 + mdc.DrawBitmap(bmp, self.leftWidth + self.padding - bmp.GetWidth() / 2, (height - bmp.GetHeight()) / 2) + textStart = self.leftWidth + self.padding + bmp.GetWidth() / 2 else: textStart = self.leftWidth mdc.SetFont(self.font) - maxsize = self.tabWidth - textStart - self.rightWidth - self.padding*4 + maxsize = self.tabWidth - textStart - self.rightWidth - self.padding * 4 color = self.selectedColor if self.selected else self.inactiveColor mdc.SetTextForeground(colorUtils.GetSuitableColor(color, 1)) @@ -590,9 +589,10 @@ class PFTabRenderer: cbmp = wx.BitmapFromImage(cimg) mdc.DrawBitmap( - cbmp, - self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth()/2, - (height - self.ctabCloseBmp.GetHeight())/2) + cbmp, + self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth() / 2, + (height - self.ctabCloseBmp.GetHeight()) / 2, + ) mdc.SelectObject(wx.NullBitmap) @@ -610,6 +610,7 @@ class PFTabRenderer: self.text, self.disabled, hex(id(self)) ) + class PFAddRenderer: def __init__(self): """Renders the add tab button""" @@ -629,7 +630,7 @@ class PFAddRenderer: def GetPosition(self): return self.position - def SetPosition(self,pos): + def SetPosition(self, pos): self.position = pos def GetSize(self): @@ -690,7 +691,7 @@ class PFTabsContainer(wx.Panel): """ wx.Panel.__init__(self, parent, id, pos, size) - if wx.VERSION >= (3,0): + if wx.VERSION >= (3, 0): self.SetBackgroundStyle(wx.BG_STYLE_PAINT) self.tabs = [] @@ -866,7 +867,8 @@ class PFTabsContainer(wx.Panel): return True if self.TabHitTest(tab, x, y): - if tab.disabled: return + if tab.disabled: + return tab.SetSelected(True) oldSelTab.SetSelected(False) @@ -943,7 +945,7 @@ class PFTabsContainer(wx.Panel): closeBtnReg = tab.GetCloseButtonRegion() tabPos = tab.GetPosition() tabPosX, tabPosY = tabPos - closeBtnReg.Offset(tabPosX,tabPosY) + closeBtnReg.Offset(tabPosX, tabPosY) if closeBtnReg.Contains(x, y): if not tab.GetCloseButtonHoverStatus(): @@ -986,7 +988,6 @@ class PFTabsContainer(wx.Panel): def GetTabAtRight(self, tabIndex): return self.tabs[tabIndex + 1] if tabIndex < self.GetTabsCount() - 1 else None - def SwitchTabs(self, src, dest, draggedTab=None): self.tabs[src], self.tabs[dest] = self.tabs[dest], self.tabs[src] self.UpdateTabsPosition(draggedTab) @@ -1111,14 +1112,12 @@ class PFTabsContainer(wx.Panel): def OnPaint(self, event): if "wxGTK" in wx.PlatformInfo: mdc = wx.AutoBufferedPaintDC(self) - else: - rect = self.GetRect() mdc = wx.BufferedPaintDC(self) selected = 0 - if 'wxMac' in wx.PlatformInfo and wx.VERSION < (3,0): + if 'wxMac' in wx.PlatformInfo and wx.VERSION < (3, 0): color = wx.Colour(0, 0, 0) brush = wx.Brush(color) @@ -1129,7 +1128,7 @@ class PFTabsContainer(wx.Panel): brush = wx.Brush(color) if "wxGTK" not in wx.PlatformInfo: - mdc.SetBackground (brush) + mdc.SetBackground(brush) mdc.Clear() selected = None @@ -1137,21 +1136,18 @@ class PFTabsContainer(wx.Panel): tabsWidth = 0 for tab in self.tabs: - tabsWidth += tab.tabWidth - self.inclination*2 - - pos = tabsWidth + tabsWidth += tab.tabWidth - self.inclination * 2 if self.showAddButton: - ax,ay = self.addButton.GetPosition() + ax, ay = self.addButton.GetPosition() mdc.DrawBitmap(self.addButton.Render(), ax, ay, True) - for i in xrange(len(self.tabs) - 1, -1, -1): + for i in range(len(self.tabs) - 1, -1, -1): tab = self.tabs[i] - width = tab.tabWidth - 6 posx, posy = tab.GetPosition() if not tab.IsSelected(): - mdc.DrawBitmap(self.efxBmp, posx, posy, True ) + mdc.DrawBitmap(self.efxBmp, posx, posy, True) bmp = tab.Render() img = bmp.ConvertToImage() img = img.AdjustChannels(1, 1, 1, 0.85) @@ -1266,13 +1262,13 @@ class PFTabsContainer(wx.Panel): def UpdateTabsPosition(self, skipTab=None): tabsWidth = 0 for tab in self.tabs: - tabsWidth += tab.tabWidth - self.inclination*2 + tabsWidth += tab.tabWidth - self.inclination * 2 pos = tabsWidth selected = None - for i in xrange(len(self.tabs) - 1, -1, -1): + for i in range(len(self.tabs) - 1, -1, -1): tab = self.tabs[i] - width = tab.tabWidth - self.inclination*2 + width = tab.tabWidth - self.inclination * 2 pos -= width if not tab.IsSelected(): tab.SetPosition((pos, self.containerHeight - self.height)) @@ -1281,7 +1277,7 @@ class PFTabsContainer(wx.Panel): selpos = pos if selected is not skipTab: selected.SetPosition((selpos, self.containerHeight - self.height)) - self.addButton.SetPosition((round(tabsWidth) + self.inclination*2, self.containerHeight - self.height/2 - self.addButton.GetHeight()/3)) + self.addButton.SetPosition((round(tabsWidth) + self.inclination * 2, self.containerHeight - self.height / 2 - self.addButton.GetHeight() / 3)) def OnLeaveWindow(self, event): @@ -1306,17 +1302,15 @@ class PFTabsContainer(wx.Panel): if not self.previewTab.GetSelected(): page = self.Parent.GetPage(self.GetTabIndex(self.previewTab)) if page.Snapshot(): - self.previewWnd = PFNotebookPagePreview(self, (mposx+3, mposy+3), page.Snapshot(), self.previewTab.text) + self.previewWnd = PFNotebookPagePreview(self, (mposx + 3, mposy + 3), page.Snapshot(), self.previewTab.text) self.previewWnd.Show() event.Skip() + class PFNotebookPagePreview(wx.Frame): - def __init__ (self,parent, pos, bitmap, title): - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=pos, size=wx.DefaultSize, style= - wx.NO_BORDER - | wx.FRAME_NO_TASKBAR - | wx.STAY_ON_TOP) + def __init__(self, parent, pos, bitmap, title): + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=pos, size=wx.DefaultSize, style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) self.title = title self.bitmap = bitmap @@ -1343,13 +1337,13 @@ class PFNotebookPagePreview(wx.Frame): else: width = bitmap.GetWidth() - self.SetSize((width, bitmap.GetHeight()+16)) + self.SetSize((width, bitmap.GetHeight() + 16)) self.SetTransparent(0) self.Refresh() def OnTimer(self, event): - self.transp += 20*self.direction + self.transp += 20 * self.direction if self.transp > 220: self.transp = 220 @@ -1358,7 +1352,7 @@ class PFNotebookPagePreview(wx.Frame): if self.transp < 0: self.transp = 0 self.timer.Stop() - wx.Frame.Show(self,False) + wx.Frame.Show(self, False) self.Destroy() return self.SetTransparent(self.transp) @@ -1382,8 +1376,7 @@ class PFNotebookPagePreview(wx.Frame): self.direction = -1 self.timer.Start(10) - - def OnWindowEraseBk(self,event): + def OnWindowEraseBk(self, event): pass def OnWindowPaint(self, event): @@ -1395,17 +1388,17 @@ class PFNotebookPagePreview(wx.Frame): mdc.SetBackground(wx.Brush(color)) mdc.Clear() - font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL,wx.NORMAL, False) + font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) mdc.SetFont(font) - x,y = mdc.GetTextExtent(self.title) + x, y = mdc.GetTextExtent(self.title) mdc.SetBrush(wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT))) mdc.DrawRectangle(0, 0, rect.width, 16) mdc.SetTextForeground(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - mdc.DrawText(self.title, (rect.width - x)/2, (16 - y)/2) + mdc.DrawText(self.title, (rect.width - x) / 2, (16 - y) / 2) mdc.DrawBitmap(self.bitmap, 0, 16) diff --git a/gui/commandView.py b/gui/commandView.py index 37caba85f..10f8fda1d 100644 --- a/gui/commandView.py +++ b/gui/commandView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import gui.display as d @@ -34,29 +34,32 @@ class DummyItem: self.name = txt self.icon = None + class DummyEntry: def __init__(self, txt): self.item = DummyItem(txt) -class CommandViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t +class CommandViewDrop(wx.PyDropTarget): + def __init__(self, dropFn): + wx.PyDropTarget.__init__(self) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t + class CommandView(d.Display): - DEFAULT_COLS = ["Base Name",] + DEFAULT_COLS = ["Base Name"] def __init__(self, parent): - d.Display.__init__(self, parent, style = wx.LC_SINGLE_SEL | wx.BORDER_NONE) + d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) self.lastFitId = None @@ -68,7 +71,7 @@ class CommandView(d.Display): self.droneView = gui.droneView.DroneView - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) @@ -86,7 +89,7 @@ class CommandView(d.Display): ''' pass - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() @@ -97,7 +100,7 @@ class CommandView(d.Display): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) def handleDrag(self, type, fitID): - #Those are drags coming from pyfa sources, NOT builtin wx drags + # Those are drags coming from pyfa sources, NOT builtin wx drags if type == "fit": activeFit = self.mainFrame.getActiveFit() if activeFit: @@ -110,7 +113,7 @@ class CommandView(d.Display): row = event.GetIndex() if row != -1 and isinstance(self.get(row), es_Drone): data = wx.PyTextDataObject() - data.SetText("command:"+str(self.GetItemData(row))) + data.SetText("command:" + str(self.GetItemData(row))) dropSource = wx.DropSource(self) dropSource.SetData(data) @@ -125,7 +128,7 @@ class CommandView(d.Display): self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -184,11 +187,11 @@ class CommandView(d.Display): menu = None if sel != -1: item = self.get(sel) - if item is None: return - sMkt = Market.getInstance() + if item is None: + return fitSrcContext = "commandFit" fitItemContext = item.name - context = ((fitSrcContext,fitItemContext),) + context = ((fitSrcContext, fitItemContext),) context = context + (("command",),) menu = ContextMenu.getMenu((item,), *context) elif sel == -1: diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 61823ff3a..2a8033b44 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,16 +15,18 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import logging + logger = logging.getLogger(__name__) + class ContextMenu(object): menus = [] - _ids = [] #[wx.NewId() for x in xrange(200)] # init with decent amount + _ids = [] # [wx.NewId() for x in xrange(200)] # init with decent amount _idxid = -1 @classmethod @@ -175,4 +177,4 @@ class ContextMenu(object): return None -from gui.builtinContextMenus import * +from gui.builtinContextMenus import * # noqa diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index ea2e44620..659e01b5a 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Lucas Thode # # This file is part of pyfa. @@ -15,11 +15,12 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx + class CopySelectDialog(wx.Dialog): copyFormatEft = 0 copyFormatEftImps = 1 @@ -29,7 +30,7 @@ class CopySelectDialog(wx.Dialog): copyFormatMultiBuy = 5 def __init__(self, parent): - wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Select a format", size = (-1,-1), style = wx.DEFAULT_DIALOG_STYLE) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Select a format", size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE) mainSizer = wx.BoxSizer(wx.VERTICAL) copyFormats = [u"EFT", u"EFT (Implants)", u"XML", u"DNA", u"CREST", u"MultiBuy"] @@ -39,7 +40,7 @@ class CopySelectDialog(wx.Dialog): CopySelectDialog.copyFormatDna: u"A one-line text format", CopySelectDialog.copyFormatCrest: u"A JSON format used for EVE CREST", CopySelectDialog.copyFormatMultiBuy: u"MultiBuy text format"} - selector = wx.RadioBox(self, wx.ID_ANY, label = u"Copy to the clipboard using:", choices = copyFormats, style = wx.RA_SPECIFY_ROWS) + selector = wx.RadioBox(self, wx.ID_ANY, label=u"Copy to the clipboard using:", choices=copyFormats, style=wx.RA_SPECIFY_ROWS) selector.Bind(wx.EVT_RADIOBOX, self.Selected) for format, tooltip in copyFormatTooltips.iteritems(): selector.SetItemToolTip(format, tooltip) @@ -47,21 +48,18 @@ class CopySelectDialog(wx.Dialog): self.copyFormat = CopySelectDialog.copyFormatEft selector.SetSelection(self.copyFormat) - mainSizer.Add(selector,0,wx.EXPAND | wx.ALL, 5) + mainSizer.Add(selector, 0, wx.EXPAND | wx.ALL, 5) buttonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL) if (buttonSizer): - mainSizer.Add(buttonSizer,0, wx.EXPAND | wx.ALL, 5) + mainSizer.Add(buttonSizer, 0, wx.EXPAND | wx.ALL, 5) self.SetSizer(mainSizer) self.Fit() self.Center() - def Selected(self, event): self.copyFormat = event.GetSelection() def GetSelected(self): return self.copyFormat - - diff --git a/gui/display.py b/gui/display.py index 707623581..2aebc1db7 100644 --- a/gui/display.py +++ b/gui/display.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,19 +15,19 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import sys import wx import gui.mainFrame - from gui.viewColumn import ViewColumn from gui.cachingImageList import CachingImageList -class Display(wx.ListCtrl): - def __init__(self, parent, size = wx.DefaultSize, style = 0): - wx.ListCtrl.__init__(self, parent,size = size, style=wx.LC_REPORT | style ) +class Display(wx.ListCtrl): + def __init__(self, parent, size=wx.DefaultSize, style=0): + + wx.ListCtrl.__init__(self, parent, size=size, style=wx.LC_REPORT | style) self.imageList = CachingImageList(16, 16) self.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) self.activeColumns = [] @@ -110,15 +110,14 @@ class Display(wx.ListCtrl): return (rowIndex, 0, -1) - - def OnEraseBk(self,event): - if self.GetItemCount() >0: + def OnEraseBk(self, event): + if self.GetItemCount() > 0: width, height = self.GetClientSize() dc = event.GetDC() dc.DestroyClippingRegion() dc.SetClippingRegion(0, 0, width, height) - x,y,w,h = dc.GetClippingBox() + x, y, w, h = dc.GetClippingBox() topItem = self.GetTopItem() bottomItem = topItem + self.GetCountPerPage() @@ -129,10 +128,9 @@ class Display(wx.ListCtrl): topRect = self.GetItemRect(topItem, wx.LIST_RECT_LABEL) bottomRect = self.GetItemRect(bottomItem, wx.LIST_RECT_BOUNDS) + items_rect = wx.Rect(topRect.left, 0, bottomRect.right - topRect.left, bottomRect.bottom) - items_rect = wx.Rect(topRect.left, 0, bottomRect.right - topRect.left, bottomRect.bottom ) - - updateRegion = wx.Region(x,y,w,h) + updateRegion = wx.Region(x, y, w, h) updateRegion.SubtractRect(items_rect) dc.DestroyClippingRegion() @@ -171,41 +169,37 @@ class Display(wx.ListCtrl): # we veto header cell resize by default till we find a way # to assure a minimal size for the resized header cell column = event.GetColumn() - wx.CallAfter(self.checkColumnSize,column) + wx.CallAfter(self.checkColumnSize, column) event.Skip() def resizeSkip(self, event): column = event.GetColumn() - if column > len (self.activeColumns)-1: + if column > len(self.activeColumns) - 1: self.SetColumnWidth(column, 0) event.Veto() return - colItem = self.activeColumns[column] + # colItem = self.activeColumns[column] if self.activeColumns[column].maxsize != -1: event.Veto() else: event.Skip() - def checkColumnSize(self,column): + def checkColumnSize(self, column): colItem = self.activeColumns[column] if self.GetColumnWidth(column) < self.columnsMinWidth[column]: - self.SetColumnWidth(column,self.columnsMinWidth[column]) + self.SetColumnWidth(column, self.columnsMinWidth[column]) colItem.resized = True - def getLastItem( self, state = wx.LIST_STATE_DONTCARE): - lastFound = -1 - while True: - index = self.GetNextItem( - lastFound, - wx.LIST_NEXT_ALL, - state, - ) - if index == -1: - break - else: - lastFound = index + def getLastItem(self, state=wx.LIST_STATE_DONTCARE): + lastFound = -1 + while True: + index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state) + if index == -1: + break + else: + lastFound = index - return lastFound + return lastFound def deselectItems(self): sel = self.GetFirstSelected() @@ -220,26 +214,25 @@ class Display(wx.ListCtrl): stuffItemCount = len(stuff) if listItemCount < stuffItemCount: - for i in xrange(stuffItemCount - listItemCount): - index = self.InsertStringItem(sys.maxint, "") + for i in range(stuffItemCount - listItemCount): + self.InsertStringItem(sys.maxint, "") if listItemCount > stuffItemCount: if listItemCount - stuffItemCount > 20 and stuffItemCount < 20: self.DeleteAllItems() - for i in xrange(stuffItemCount): - index = self.InsertStringItem(sys.maxint, "") + for i in range(stuffItemCount): + self.InsertStringItem(sys.maxint, "") else: - for i in xrange(listItemCount - stuffItemCount): + for i in range(listItemCount - stuffItemCount): self.DeleteItem(self.getLastItem()) self.Refresh() - def refresh(self, stuff): - if stuff == None: + if stuff is None: return item = -1 - for id, st in enumerate(stuff): + for id_, st in enumerate(stuff): item = self.GetNextItem(item) @@ -270,11 +263,11 @@ class Display(wx.ListCtrl): colItem.SetMask(mask) self.SetItem(colItem) - self.SetItemData(item, id) + self.SetItemData(item, id_) -# self.Freeze() + # self.Freeze() if 'wxMSW' in wx.PlatformInfo: - for i,col in enumerate(self.activeColumns): + for i, col in enumerate(self.activeColumns): if not col.resized: self.SetColumnWidth(i, col.size) else: @@ -289,9 +282,7 @@ class Display(wx.ListCtrl): self.SetColumnWidth(i, headerWidth) else: self.SetColumnWidth(i, col.size) -# self.Thaw() - - + # self.Thaw() def update(self, stuff): self.populate(stuff) diff --git a/gui/droneView.py b/gui/droneView.py index 89dbc4dd4..4d389e8ea 100644 --- a/gui/droneView.py +++ b/gui/droneView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx @@ -27,29 +27,33 @@ from gui.contextMenu import ContextMenu from service.fit import Fit from service.market import Market -class DroneViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t +class DroneViewDrop(wx.PyDropTarget): + def __init__(self, dropFn): + wx.PyDropTarget.__init__(self) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t + class DroneView(d.Display): - DEFAULT_COLS = ["State", - #"Base Icon", - "Base Name", - # "prop:droneDps,droneBandwidth", - "Max Range", - "Miscellanea", - "attr:maxVelocity", - "Price",] + DEFAULT_COLS = [ + "State", + # "Base Icon", + "Base Name", + # "prop:droneDps,droneBandwidth", + "Max Range", + "Miscellanea", + "attr:maxVelocity", + "Price", + ] def __init__(self, parent): d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) @@ -67,12 +71,11 @@ class DroneView(d.Display): self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) - self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) self.SetDropTarget(DroneViewDrop(self.handleDragDrop)) @@ -108,7 +111,6 @@ class DroneView(d.Display): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() - firstSel = row if row != -1: drone = self.drones[self.GetItemData(row)] self.removeDrone(drone) @@ -119,11 +121,11 @@ class DroneView(d.Display): row = event.GetIndex() if row != -1: data = wx.PyTextDataObject() - data.SetText("drone:"+str(row)) + data.SetText("drone:" + str(row)) dropSource = wx.DropSource(self) dropSource.SetData(data) - res = dropSource.DoDragDrop() + dropSource.DoDragDrop() def handleDragDrop(self, x, y, data): ''' @@ -151,6 +153,7 @@ class DroneView(d.Display): 'Heavy Attack Drones', 'Sentry Drones', 'Fighters', 'Fighter Bombers', 'Combat Utility Drones', 'Electronic Warfare Drones', 'Logistic Drones', 'Mining Drones', 'Salvage Drones') + def droneKey(self, drone): sMkt = Market.getInstance() @@ -165,7 +168,7 @@ class DroneView(d.Display): self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -178,7 +181,6 @@ class DroneView(d.Display): if stuff is not None: stuff.sort(key=self.droneKey) - if event.fitID != self.lastFitId: self.lastFitId = event.fitID @@ -192,7 +194,6 @@ class DroneView(d.Display): self.update(stuff) event.Skip() - def addItem(self, event): sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() diff --git a/gui/fighterView.py b/gui/fighterView.py index 63e7b3b22..56aeb73b1 100644 --- a/gui/fighterView.py +++ b/gui/fighterView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx diff --git a/gui/fleetBrowser.py b/gui/fleetBrowser.py index 7476d7d7f..1d3c6b38c 100644 --- a/gui/fleetBrowser.py +++ b/gui/fleetBrowser.py @@ -1,23 +1,18 @@ import wx -import copy -from gui.bitmapLoader import BitmapLoader -import gui.mainFrame -from gui.PFListPane import PFListPane -import service.fleet -from gui.utils.drawUtils import GetPartialText - from wx.lib.buttons import GenBitmapButton +import service.fleet +import gui.mainFrame import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils - import gui.sfBrowserItem as SFItem +from gui.bitmapLoader import BitmapLoader +from gui.PFListPane import PFListPane +from gui.utils.drawUtils import GetPartialText + FleetSelected, EVT_FLEET_SELECTED = wx.lib.newevent.NewEvent() FleetRenamed, EVT_FLEET_RENAMED = wx.lib.newevent.NewEvent() FleetRemoved, EVT_FLEET_REMOVED = wx.lib.newevent.NewEvent() - - FleetItemSelect, EVT_FLEET_ITEM_SELECT = wx.lib.newevent.NewEvent() FleetItemDelete, EVT_FLEET_ITEM_DELETE = wx.lib.newevent.NewEvent() FleetItemNew, EVT_FLEET_ITEM_NEW = wx.lib.newevent.NewEvent() @@ -25,7 +20,6 @@ FleetItemCopy, EVT_FLEET_ITEM_COPY = wx.lib.newevent.NewEvent() FleetItemRename, EVT_FLEET_ITEM_RENAME = wx.lib.newevent.NewEvent() - class FleetBrowser(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) @@ -38,8 +32,8 @@ class FleetBrowser(wx.Panel): self.hpane = FleetBrowserHeader(self) mainSizer.Add(self.hpane, 0, wx.EXPAND) - self.m_sl2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_sl2, 0, wx.EXPAND, 0 ) + self.m_sl2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_sl2, 0, wx.EXPAND, 0) self.fleetItemContainer = PFFleetItemContainer(self) @@ -79,7 +73,7 @@ class FleetBrowser(wx.Panel): fleet = self.sFleet.copyFleetByID(fleetID) fleetName = fleet.name + " Copy" - self.sFleet.renameFleet(fleet,fleetName) + self.sFleet.renameFleet(fleet, fleetName) self.fleetIDMustEditName = fleet.ID self.AddItem(fleet.ID, fleet.name, fleet.count()) @@ -94,27 +88,27 @@ class FleetBrowser(wx.Panel): newFleetName = event.fleetName self.sFleet.renameFleet(fleet, newFleetName) - wx.PostEvent(self.mainFrame, FleetRenamed(fleetID = fleet.ID)) + wx.PostEvent(self.mainFrame, FleetRenamed(fleetID=fleet.ID)) def DeleteFleetItem(self, event): self.sFleet.deleteFleetByID(event.fleetID) self.PopulateFleetList() - wx.PostEvent(self.mainFrame, FleetRemoved(fleetID = event.fleetID)) + wx.PostEvent(self.mainFrame, FleetRemoved(fleetID=event.fleetID)) - def AddItem (self, ID, name, count): + def AddItem(self, ID, name, count): self.fleetItemContainer.AddWidget(FleetItem(self, ID, name, count)) widget = self.fleetItemContainer.GetWidgetByFleetID(ID) self.fleetItemContainer.RefreshList(True) self.fleetItemContainer.ScrollChildIntoView(widget) - wx.PostEvent(self, FleetItemSelect(fleetID = ID)) + wx.PostEvent(self, FleetItemSelect(fleetID=ID)) def PopulateFleetList(self): self.Freeze() - filter = self.filter + filter_ = self.filter self.fleetItemContainer.RemoveAllChildren() fleetList = self.sFleet.getFleetList() for fleetID, fleetName, fleetCount in fleetList: - if fleetName.lower().find(filter.lower()) != -1: + if fleetName.lower().find(filter_.lower()) != -1: self.fleetItemContainer.AddWidget(FleetItem(self, fleetID, fleetName, fleetCount)) self.fleetItemContainer.RefreshList() self.Thaw() @@ -130,34 +124,34 @@ class FleetBrowser(wx.Panel): event.Skip() -class FleetBrowserHeader (wx.Panel): +class FleetBrowserHeader(wx.Panel): def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 24), style=wx.TAB_TRAVERSAL) - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 24), style=wx.TAB_TRAVERSAL) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) - self.newBmp = BitmapLoader.getBitmap("fit_add_small","gui") - bmpSize = (16,16) + self.newBmp = BitmapLoader.getBitmap("fit_add_small", "gui") + bmpSize = (16, 16) mainSizer = wx.BoxSizer(wx.HORIZONTAL) if 'wxMac' in wx.PlatformInfo: bgcolour = wx.Colour(0, 0, 0, 0) else: - bgcolour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) + bgcolour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) - self.fbNewFleet = PFGenBitmapButton( self, wx.ID_ANY, self.newBmp, wx.DefaultPosition, bmpSize, wx.BORDER_NONE ) - mainSizer.Add(self.fbNewFleet, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL , 5) - self.fbNewFleet.SetBackgroundColour( bgcolour ) + self.fbNewFleet = PFGenBitmapButton(self, wx.ID_ANY, self.newBmp, wx.DefaultPosition, bmpSize, wx.BORDER_NONE) + mainSizer.Add(self.fbNewFleet, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, 5) + self.fbNewFleet.SetBackgroundColour(bgcolour) - self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL ) - mainSizer.Add( self.sl1, 0, wx.EXPAND |wx.LEFT, 5 ) + self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL) + mainSizer.Add(self.sl1, 0, wx.EXPAND | wx.LEFT, 5) - self.tcFilter = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.tcFilter, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.tcFilter = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.tcFilter, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - self.stStatus = wx.StaticText( self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stStatus.Wrap( -1 ) - mainSizer.Add( self.stStatus, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 ) + self.stStatus = wx.StaticText(self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0) + self.stStatus.Wrap(-1) + mainSizer.Add(self.stStatus, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) self.SetSizer(mainSizer) self.Layout() @@ -178,7 +172,7 @@ class FleetBrowserHeader (wx.Panel): event.Skip() def OnNewFleetItem(self, event): - wx.PostEvent(self.Parent, FleetItemNew(fleetName = "New Fleet")) + wx.PostEvent(self.Parent, FleetItemNew(fleetName="New Fleet")) def fbNewEnterWindow(self, event): self.stStatus.SetLabel("New fleet") @@ -195,10 +189,9 @@ class FleetBrowserHeader (wx.Panel): event.Skip() - class PFFleetItemContainer(PFListPane): - def __init__(self,parent): - PFListPane.__init__(self,parent) + def __init__(self, parent): + PFListPane.__init__(self, parent) self.selectedWidget = -1 self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) @@ -236,7 +229,6 @@ class PFFleetItemContainer(PFListPane): self.selectedWidget = -1 self._wList.remove(child) - def RemoveAllChildren(self): for widget in self._wList: widget.Destroy() @@ -247,11 +239,12 @@ class PFFleetItemContainer(PFListPane): def OnLeftUp(self, event): event.Skip() + class FleetItem(SFItem.SFBrowserItem): def __init__(self, parent, fleetID, fleetName, fleetCount, id=wx.ID_ANY, pos=wx.DefaultPosition, - size=(0,40), style=0): - SFItem.SFBrowserItem.__init__(self, parent, size = size) + size=(0, 40), style=0): + SFItem.SFBrowserItem.__init__(self, parent, size=size) self.fleetBrowser = self.Parent self.fleetID = fleetID @@ -260,13 +253,13 @@ class FleetItem(SFItem.SFBrowserItem): self.padding = 4 - self.fontBig = wx.FontFromPixelSize((0,15),wx.SWISS, wx.NORMAL, wx.BOLD, False) - self.fontNormal = wx.FontFromPixelSize((0,14),wx.SWISS, wx.NORMAL, wx.NORMAL, False) - self.fontSmall = wx.FontFromPixelSize((0,12),wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.fontBig = wx.FontFromPixelSize((0, 15), wx.SWISS, wx.NORMAL, wx.BOLD, False) + self.fontNormal = wx.FontFromPixelSize((0, 14), wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.fontSmall = wx.FontFromPixelSize((0, 12), wx.SWISS, wx.NORMAL, wx.NORMAL, False) self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") - self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small","gui") + self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small", "gui") self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") self.fleetBmp = BitmapLoader.getBitmap("fleet_item_big", "gui") @@ -284,7 +277,7 @@ class FleetItem(SFItem.SFBrowserItem): self.toolbar.AddButton(self.deleteBmp, "Delete", self.DeleteFleetCB) self.editWidth = 150 - self.tcFleetName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fleetName, wx.DefaultPosition, (self.editWidth,-1), wx.TE_PROCESS_ENTER) + self.tcFleetName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fleetName, wx.DefaultPosition, (self.editWidth, -1), wx.TE_PROCESS_ENTER) if self.fleetBrowser.fleetIDMustEditName != self.fleetID: self.tcFleetName.Show(False) @@ -299,21 +292,20 @@ class FleetItem(SFItem.SFBrowserItem): self.tcFleetName.Bind(wx.EVT_TEXT_ENTER, self.RenameFleet) self.tcFleetName.Bind(wx.EVT_KEY_DOWN, self.EditCheckEsc) - self.animCount = 0 def MouseLeftUp(self, event): if self.tcFleetName.IsShown(): self.RestoreEditButton() else: - wx.PostEvent(self.fleetBrowser, FleetItemSelect(fleetID = self.fleetID)) + wx.PostEvent(self.fleetBrowser, FleetItemSelect(fleetID=self.fleetID)) def CopyFleetCB(self): if self.tcFleetName.IsShown(): self.RestoreEditButton() return - wx.PostEvent(self.fleetBrowser, FleetItemCopy(fleetID = self.fleetID)) + wx.PostEvent(self.fleetBrowser, FleetItemCopy(fleetID=self.fleetID)) def RenameFleetCB(self): @@ -341,14 +333,14 @@ class FleetItem(SFItem.SFBrowserItem): self.tcFleetName.Show(False) - wx.PostEvent(self.fleetBrowser, FleetItemRename(fleetID = self.fleetID, fleetName = self.fleetName)) + wx.PostEvent(self.fleetBrowser, FleetItemRename(fleetID=self.fleetID, fleetName=self.fleetName)) self.Refresh() def DeleteFleetCB(self): if self.tcFleetName.IsShown(): self.RestoreEditButton() return - wx.PostEvent(self.fleetBrowser, FleetItemDelete(fleetID = self.fleetID)) + wx.PostEvent(self.fleetBrowser, FleetItemDelete(fleetID=self.fleetID)) def RestoreEditButton(self): self.tcFleetName.Show(False) @@ -392,14 +384,14 @@ class FleetItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontSmall) - wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) + wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) self.thoverx = self.toolbarx - self.padding - wlabel - self.thovery = (rect.height - hlabel)/2 + self.thovery = (rect.height - hlabel) / 2 self.thoverw = wlabel def DrawItem(self, mdc): - rect = self.GetRect() + # rect = self.GetRect() windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) textColor = colorUtils.GetSuitableColor(windowColor, 1) @@ -410,13 +402,13 @@ class FleetItem(SFItem.SFBrowserItem): self.toolbar.SetPosition((self.toolbarx, self.toolbary)) mdc.DrawBitmap(self.fleetEffBmp, self.fleetBmpx + 3, self.fleetBmpy + 2) - mdc.DrawBitmap(self.fleetBmp, self.fleetBmpx,self.fleetBmpy) + mdc.DrawBitmap(self.fleetBmp, self.fleetBmpx, self.fleetBmpy) mdc.SetFont(self.fontNormal) - suffix = "%d ships" % self.fleetCount if self.fleetCount >1 else "%d ship" % self.fleetCount if self.fleetCount == 1 else "No ships" + suffix = "%d ships" % self.fleetCount if self.fleetCount > 1 else "%d ship" % self.fleetCount if self.fleetCount == 1 else "No ships" fleetCount = "Fleet size: %s" % suffix - fleetCount = drawUtils.GetPartialText(mdc, fleetCount, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + fleetCount = GetPartialText(mdc, fleetCount, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) mdc.DrawText(fleetCount, self.textStartx, self.fleetCounty) @@ -425,7 +417,7 @@ class FleetItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontBig) - pfname = drawUtils.GetPartialText(mdc, self.fleetName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + pfname = GetPartialText(mdc, self.fleetName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) mdc.DrawText(pfname, self.textStartx, self.fleetNamey) if self.tcFleetName.IsShown(): @@ -435,13 +427,13 @@ class FleetItem(SFItem.SFBrowserItem): fnEditSize = editCtl.GetSize() wSize = self.GetSize() fnEditPosX = end - fnEditPosY = (wSize.height - fnEditSize.height)/2 + fnEditPosY = (wSize.height - fnEditSize.height) / 2 if fnEditPosX < start: - editCtl.SetSize((self.editWidth + fnEditPosX - start,-1)) - editCtl.SetPosition((start,fnEditPosY)) + editCtl.SetSize((self.editWidth + fnEditPosX - start, -1)) + editCtl.SetPosition((start, fnEditPosY)) else: - editCtl.SetSize((self.editWidth,-1)) - editCtl.SetPosition((fnEditPosX,fnEditPosY)) + editCtl.SetSize((self.editWidth, -1)) + editCtl.SetPosition((fnEditPosX, fnEditPosY)) class PFGenBitmapButton(GenBitmapButton): diff --git a/gui/gangView.py b/gui/gangView.py index 57f7cffa0..ea422bc83 100644 --- a/gui/gangView.py +++ b/gui/gangView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,111 +15,109 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + import wx from wx.lib.scrolledpanel import ScrolledPanel -import gui.mainFrame -import gui.shipBrowser -import gui.globalEvents as GE - from service.fit import Fit from service.fleet import Fleet from service.character import Character from service.market import Market +import gui.mainFrame +import gui.shipBrowser +import gui.globalEvents as GE -from gui import characterEditor as CharEditor -class GangView ( ScrolledPanel ): - - def __init__( self, parent ): - ScrolledPanel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 100,20 ), style = wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL ) - mainSizer = wx.BoxSizer( wx.VERTICAL ) +class GangView(ScrolledPanel): + def __init__(self, parent): + ScrolledPanel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(100, 20), style=wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL) + mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.draggedFitID = None - help = '''Set fit as booster to display in dropdown, or drag fitting from\nship browser to this window, or right click fit and select booster role.''' - helpSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.helpText = wx.StaticText( self, wx.ID_ANY, help, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) - helpSizer.Add( self.helpText, 1, wx.ALL, 5 ) + help_msg = '''Set fit as booster to display in dropdown, or drag fitting from\nship browser to this window, or right click fit and select booster role.''' + helpSizer = wx.BoxSizer(wx.HORIZONTAL) + self.helpText = wx.StaticText(self, wx.ID_ANY, help_msg, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE) + helpSizer.Add(self.helpText, 1, wx.ALL, 5) self.options = ["Fleet", "Wing", "Squad"] self.fleet = {} - for id, option in enumerate(self.options): + for id_, option in enumerate(self.options): # set content for each commander - self.fleet[id] = {} - self.fleet[id]['stLabel'] = wx.StaticText( self, wx.ID_ANY, self.options[id]+':', wx.DefaultPosition, wx.DefaultSize, 0 ) - self.fleet[id]['stText'] = wx.StaticText( self, wx.ID_ANY, 'None', wx.DefaultPosition, wx.DefaultSize, 0 ) - self.fleet[id]['chFit'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] ) - self.fleet[id]['chChar'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] ) - self.fleet[id]['fitSizer'] = wx.BoxSizer( wx.VERTICAL ) + self.fleet[id_] = {} + self.fleet[id_]['stLabel'] = wx.StaticText(self, wx.ID_ANY, self.options[id_] + ':', wx.DefaultPosition, wx.DefaultSize, 0) + self.fleet[id_]['stText'] = wx.StaticText(self, wx.ID_ANY, 'None', wx.DefaultPosition, wx.DefaultSize, 0) + self.fleet[id_]['chFit'] = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) + self.fleet[id_]['chChar'] = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) + self.fleet[id_]['fitSizer'] = wx.BoxSizer(wx.VERTICAL) self.FitDNDPopupMenu = self.buildBoostermenu() - contentFGSizer = wx.FlexGridSizer( 5, 3, 0, 0 ) - contentFGSizer.AddGrowableCol( 1 ) - contentFGSizer.SetFlexibleDirection( wx.BOTH ) - contentFGSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + contentFGSizer = wx.FlexGridSizer(5, 3, 0, 0) + contentFGSizer.AddGrowableCol(1) + contentFGSizer.SetFlexibleDirection(wx.BOTH) + contentFGSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - ### Header - self.stBooster = wx.StaticText( self, wx.ID_ANY, u"Booster", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stBooster.Wrap( -1 ) - self.stBooster.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - contentFGSizer.Add( self.stBooster, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 5 ) + # Header + self.stBooster = wx.StaticText(self, wx.ID_ANY, u"Booster", wx.DefaultPosition, wx.DefaultSize, 0) + self.stBooster.Wrap(-1) + self.stBooster.SetFont(wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) + contentFGSizer.Add(self.stBooster, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL, 5) - self.stFits = wx.StaticText( self, wx.ID_ANY, u"Fits", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stFits.Wrap( -1 ) - self.stFits.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - contentFGSizer.Add( self.stFits, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) + self.stFits = wx.StaticText(self, wx.ID_ANY, u"Fits", wx.DefaultPosition, wx.DefaultSize, 0) + self.stFits.Wrap(-1) + self.stFits.SetFont(wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) + contentFGSizer.Add(self.stFits, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL, 5) - self.stCharacters = wx.StaticText( self, wx.ID_ANY, u"Characters", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stCharacters.Wrap( -1 ) - self.stCharacters.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - contentFGSizer.Add( self.stCharacters, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) + self.stCharacters = wx.StaticText(self, wx.ID_ANY, u"Characters", wx.DefaultPosition, wx.DefaultSize, 0) + self.stCharacters.Wrap(-1) + self.stCharacters.SetFont(wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) + contentFGSizer.Add(self.stCharacters, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL, 5) - self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - contentFGSizer.Add( self.m_staticline2, 0, wx.EXPAND, 5 ) + self.m_staticline2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + contentFGSizer.Add(self.m_staticline2, 0, wx.EXPAND, 5) - self.m_staticline3 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - contentFGSizer.Add( self.m_staticline3, 0, wx.EXPAND, 5 ) + self.m_staticline3 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + contentFGSizer.Add(self.m_staticline3, 0, wx.EXPAND, 5) - self.m_staticline4 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - contentFGSizer.Add( self.m_staticline4, 0, wx.EXPAND, 5 ) + self.m_staticline4 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + contentFGSizer.Add(self.m_staticline4, 0, wx.EXPAND, 5) - ### Content - for id in self.fleet: + # Content + for id_ in self.fleet: # set various properties - self.fleet[id]['stLabel'].Wrap( -1 ) - self.fleet[id]['stLabel'].SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - self.fleet[id]['stText'].Wrap( -1 ) + self.fleet[id_]['stLabel'].Wrap(-1) + self.fleet[id_]['stLabel'].SetFont(wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) + self.fleet[id_]['stText'].Wrap(-1) # bind text and choice events - self.fleet[id]['stText'].Bind(wx.EVT_LEFT_DCLICK, self.RemoveBooster) - self.fleet[id]['stText'].Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow) - self.fleet[id]['stText'].Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - self.fleet[id]['stText'].SetToolTip(wx.ToolTip("Double click to remove booster")) - self.fleet[id]['chChar'].Bind(wx.EVT_CHOICE, self.CharChanged) - self.fleet[id]['chFit'].Bind(wx.EVT_CHOICE, self.OnFitChoiceSelected) + self.fleet[id_]['stText'].Bind(wx.EVT_LEFT_DCLICK, self.RemoveBooster) + self.fleet[id_]['stText'].Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow) + self.fleet[id_]['stText'].Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) + self.fleet[id_]['stText'].SetToolTip(wx.ToolTip("Double click to remove booster")) + self.fleet[id_]['chChar'].Bind(wx.EVT_CHOICE, self.CharChanged) + self.fleet[id_]['chFit'].Bind(wx.EVT_CHOICE, self.OnFitChoiceSelected) # add fit text and choice to the fit sizer - self.fleet[id]['fitSizer'].Add( self.fleet[id]['stText'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) - self.fleet[id]['fitSizer'].Add( self.fleet[id]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 1 ) + self.fleet[id_]['fitSizer'].Add(self.fleet[id_]['stText'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) + self.fleet[id_]['fitSizer'].Add(self.fleet[id_]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, 1) # add everything to the content sizer - contentFGSizer.Add( self.fleet[id]['stLabel'], 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) - contentFGSizer.Add( self.fleet[id]['fitSizer'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 ) - contentFGSizer.Add( self.fleet[id]['chChar'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + contentFGSizer.Add(self.fleet[id_]['stLabel'], 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) + contentFGSizer.Add(self.fleet[id_]['fitSizer'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, 5) + contentFGSizer.Add(self.fleet[id_]['chChar'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) - mainSizer.Add( contentFGSizer, 1, wx.EXPAND, 0 ) - mainSizer.Add( helpSizer, 0, wx.EXPAND, 0 ) + mainSizer.Add(contentFGSizer, 1, wx.EXPAND, 0) + mainSizer.Add(helpSizer, 0, wx.EXPAND, 0) - self.SetSizer( mainSizer ) + self.SetSizer(mainSizer) self.SetAutoLayout(True) self.SetupScrolling() @@ -134,7 +132,7 @@ class GangView ( ScrolledPanel ): def buildBoostermenu(self): menu = wx.Menu() - for id, option in enumerate(self.options): + for option in self.options: item = menu.Append(-1, option) # We bind it to the mainFrame because it may be called from either this class or from FitItem via shipBrowser self.mainFrame.Bind(wx.EVT_MENU, self.OnPopupItemSelected, item) @@ -154,11 +152,12 @@ class GangView ( ScrolledPanel ): ''' Change booster character ''' chBooster = event.GetEventObject() - type = -1 - for id in self.fleet: - if chBooster == self.fleet[id]['chChar']: type = id + type_ = -1 + for id_ in self.fleet: + if chBooster == self.fleet[id_]['chChar']: + type_ = id_ - if type == -1: + if type_ == -1: event.Skip() return @@ -169,8 +168,8 @@ class GangView ( ScrolledPanel ): activeFitID = self.mainFrame.getActiveFit() fit = sFit.getFit(activeFitID) - sChar = Character.getInstance() - charList = sChar.getCharacterList() + # sChar = Character.getInstance() + # charList = sChar.getCharacterList() if activeFitID: commanders = fleetSrv.loadLinearFleet(fit) @@ -179,21 +178,21 @@ class GangView ( ScrolledPanel ): else: fleetCom, wingCom, squadCom = commanders - if type == 0: + if type_ == 0: if fleetCom: charID = chBooster.GetClientData(chBooster.GetSelection()) sFit.changeChar(fleetCom.ID, charID) else: chBooster.SetSelection(0) - if type == 1: + if type_ == 1: if wingCom: charID = chBooster.GetClientData(chBooster.GetSelection()) sFit.changeChar(wingCom.ID, charID) else: chBooster.SetSelection(0) - if type == 2: + if type_ == 2: if squadCom: charID = chBooster.GetClientData(chBooster.GetSelection()) sFit.changeChar(squadCom.ID, charID) @@ -208,13 +207,14 @@ class GangView ( ScrolledPanel ): def RemoveBooster(self, event): activeFitID = self.mainFrame.getActiveFit() - if not activeFitID: + if not activeFitID: return location = event.GetEventObject() - for id in self.fleet: - if location == self.fleet[id]['stText']: type = id + for id_ in self.fleet: + if location == self.fleet[id_]['stText']: + type_ = id_ sFit = Fit.getInstance() boostee = sFit.getFit(activeFitID) @@ -222,13 +222,16 @@ class GangView ( ScrolledPanel ): fleetSrv = Fleet.getInstance() - if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) - if type == 1: fleetSrv.setLinearWingCom(boostee, booster) - if type == 2: fleetSrv.setLinearSquadCom(boostee, booster) + if type_ == 0: + fleetSrv.setLinearFleetCom(boostee, booster) + elif type_ == 1: + fleetSrv.setLinearWingCom(boostee, booster) + elif type_ == 2: + fleetSrv.setLinearSquadCom(boostee, booster) # Hide stText and, default fit selection, and enable it location.Hide() - choice = self.fleet[type]['chFit'] + choice = self.fleet[type_]['chFit'] choice.SetSelection(0) choice.Show() @@ -236,7 +239,7 @@ class GangView ( ScrolledPanel ): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) def fitRenamed(self, event): - fleetSrv = Fleet.getInstance() + # fleetSrv = Fleet.getInstance() activeFitID = self.mainFrame.getActiveFit() if activeFitID: @@ -259,27 +262,27 @@ class GangView ( ScrolledPanel ): if activeFitID: commanders = fleetSrv.loadLinearFleet(fit) - for id in self.fleet: + for id_ in self.fleet: # try...except here as we're trying 2 different criteria and want to fall back on the same code try: - commander = commanders[id] + commander = commanders[id_] if not activeFitID or commander is None: raise Exception() - self.fleet[id]['stText'].SetLabel(commander.ship.item.name + ": " + commander.name) - self.fleet[id]['chChar'].SetStringSelection(commander.character.name if commander.character is not None else "All 0") - self.fleet[id]['chChar'].Enable() - self.fleet[id]['chFit'].Hide() - self.fleet[id]['stText'].Show() + self.fleet[id_]['stText'].SetLabel(commander.ship.item.name + ": " + commander.name) + self.fleet[id_]['chChar'].SetStringSelection(commander.character.name if commander.character is not None else "All 0") + self.fleet[id_]['chChar'].Enable() + self.fleet[id_]['chFit'].Hide() + self.fleet[id_]['stText'].Show() except: - #set defaults, disable char selection, and enable fit selection - self.fleet[id]['stText'].SetLabel("None") - self.fleet[id]['chChar'].SetStringSelection("All 0") - self.fleet[id]['chChar'].Disable() - self.fleet[id]['chFit'].SetSelection(0) - self.fleet[id]['chFit'].Show() - self.fleet[id]['stText'].Hide() + # set defaults, disable char selection, and enable fit selection + self.fleet[id_]['stText'].SetLabel("None") + self.fleet[id_]['chChar'].SetStringSelection("All 0") + self.fleet[id_]['chChar'].Disable() + self.fleet[id_]['chFit'].SetSelection(0) + self.fleet[id_]['chFit'].Show() + self.fleet[id_]['stText'].Hide() if activeFitID: self.Enable() @@ -289,7 +292,7 @@ class GangView ( ScrolledPanel ): self.Layout() self.SendSizeEvent() - def AddCommander(self, fitID, type = None): + def AddCommander(self, fitID, type=None): ''' Adds booster to a fit, then recalculates active fit ''' if type is None: return @@ -303,20 +306,23 @@ class GangView ( ScrolledPanel ): fleetSrv = Fleet.getInstance() - if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) - if type == 1: fleetSrv.setLinearWingCom(boostee, booster) - if type == 2: fleetSrv.setLinearSquadCom(boostee, booster) + if type == 0: + fleetSrv.setLinearFleetCom(boostee, booster) + elif type == 1: + fleetSrv.setLinearWingCom(boostee, booster) + elif type == 2: + fleetSrv.setLinearSquadCom(boostee, booster) sFit.recalc(boostee) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - def RefreshBoosterFits(self, event = None): + def RefreshBoosterFits(self, event=None): sFit = Fit.getInstance() sMkt = Market.getInstance() fitList = sFit.getBoosterFits() - for id in self.fleet: - choice = self.fleet[id]['chFit'] + for id_ in self.fleet: + choice = self.fleet[id_]['chFit'] chCurrSelection = choice.GetSelection() chCurrData = -1 if chCurrSelection != -1: @@ -326,10 +332,10 @@ class GangView ( ScrolledPanel ): currSelFound = False choice.Append("None", -1) for fit in fitList: - id,name,type = fit - ship = sMkt.getItem(type) - choice.Append(ship.name+': '+name, id) - if chCurrData == id: + fit_id, name, type_ = fit + ship = sMkt.getItem(type_) + choice.Append(ship.name + ': ' + name, fit_id) + if chCurrData == fit_id: currSelFound = True if chCurrSelection == -1: @@ -340,11 +346,11 @@ class GangView ( ScrolledPanel ): else: choice.SetSelection(0) - def RefreshCharacterList(self, event = None): + def RefreshCharacterList(self, event=None): sChar = Character.getInstance() charList = sChar.getCharacterList() - for id in self.fleet: - choice = self.fleet[id]['chChar'] + for id_ in self.fleet: + choice = self.fleet[id_]['chChar'] chCurrSelection = choice.GetSelection() chCurrData = -1 if chCurrSelection != -1: @@ -367,7 +373,7 @@ class GangView ( ScrolledPanel ): def handleDrag(self, type, fitID): ''' Handle dragging of fit to fleet interface ''' - #Those are drags coming from pyfa sources, NOT builtin wx drags + # Those are drags coming from pyfa sources, NOT builtin wx drags self.draggedFitID = None if type == "fit": sFit = Fit.getInstance() @@ -381,18 +387,17 @@ class GangView ( ScrolledPanel ): self.PopupMenu(self.FitDNDPopupMenu, pos) - def OnPopupItemSelected(self, event): ''' Fired when booster popup item is selected ''' # Get menu selection ID via self.options menuItem = event.EventObject.FindItemById(event.GetId()) - type = self.options.index(menuItem.GetText()) + type_ = self.options.index(menuItem.GetText()) if self.draggedFitID: sFit = Fit.getInstance() draggedFit = sFit.getFit(self.draggedFitID) - self.AddCommander(draggedFit.ID, type) + self.AddCommander(draggedFit.ID, type_) self.mainFrame.additionsPane.select("Fleet") def OnFitChoiceSelected(self, event): @@ -403,15 +408,16 @@ class GangView ( ScrolledPanel ): chFit = event.GetEventObject() fitID = chFit.GetClientData(chFit.GetSelection()) - type = -1 - for id in self.fleet: - if chFit == self.fleet[id]['chFit']: type = id + type_ = -1 + for id_ in self.fleet: + if chFit == self.fleet[id_]['chFit']: + type_ = id_ - if type == -1 or fitID == -1: + if type_ == -1 or fitID == -1: event.Skip() return fit = sFit.getFit(fitID) - self.AddCommander(fit.ID, type) + self.AddCommander(fit.ID, type_) self.mainFrame.additionsPane.select("Fleet") diff --git a/gui/globalEvents.py b/gui/globalEvents.py index 18f91d348..7b81b3218 100644 --- a/gui/globalEvents.py +++ b/gui/globalEvents.py @@ -1,5 +1,6 @@ import wx.lib.newevent + FitChanged, FIT_CHANGED = wx.lib.newevent.NewEvent() CharListUpdated, CHAR_LIST_UPDATED = wx.lib.newevent.NewEvent() CharChanged, CHAR_CHANGED = wx.lib.newevent.NewEvent() diff --git a/gui/graph.py b/gui/graph.py index 53d66f6a5..0b40902c2 100644 --- a/gui/graph.py +++ b/gui/graph.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,8 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + class Graph(object): views = [] @@ -33,4 +34,5 @@ class Graph(object): def getIcons(self): return None -from gui.builtinGraphs import * + +from gui.builtinGraphs import * # noqa diff --git a/gui/graphFrame.py b/gui/graphFrame.py index fb3480b33..dc286af1b 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,21 +15,24 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + +import os import wx -import os -from gui.bitmapLoader import BitmapLoader -import gui.display -import gui.globalEvents as GE -from gui.graph import Graph -import gui.mainFrame from service.fit import Fit +import gui.display +import gui.mainFrame +import gui.globalEvents as GE +from gui.graph import Graph +from gui.bitmapLoader import BitmapLoader + enabled = True mplImported = False + class GraphFrame(wx.Frame): def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): @@ -58,11 +61,11 @@ class GraphFrame(wx.Frame): from matplotlib.figure import Figure enabled = True if mpl.__version__[0] != "1": - print "pyfa: Found matplotlib version ",mpl.__version__, " - activating OVER9000 workarounds" - print "pyfa: Recommended minimum matplotlib version is 1.0.0" + print("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds") + print("pyfa: Recommended minimum matplotlib version is 1.0.0") self.legendFix = True except: - print "Problems importing matplotlib; continuing without graphs" + print("Problems importing matplotlib; continuing without graphs") enabled = False return @@ -91,19 +94,19 @@ class GraphFrame(wx.Frame): self.figure = Figure(figsize=(4, 3)) - rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() - clr = [c/255. for c in rgbtuple] - self.figure.set_facecolor( clr ) - self.figure.set_edgecolor( clr ) + rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() + clr = [c / 255. for c in rgbtuple] + self.figure.set_facecolor(clr) + self.figure.set_edgecolor(clr) self.canvas = Canvas(self, -1, self.figure) - self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) + self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) self.subplot = self.figure.add_subplot(111) self.subplot.grid(True) self.mainSizer.Add(self.canvas, 1, wx.EXPAND) - self.mainSizer.Add(wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0 , wx.EXPAND) + self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND) self.gridPanel = wx.Panel(self) self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) @@ -122,8 +125,8 @@ class GraphFrame(wx.Frame): self.graphSelection.SetSelection(0) self.fields = {} self.select(0) - self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - self.mainSizer.Add(self.sl1,0, wx.EXPAND) + self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + self.mainSizer.Add(self.sl1, 0, wx.EXPAND) self.mainSizer.Add(self.fitList, 0, wx.EXPAND) self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) @@ -160,13 +163,13 @@ class GraphFrame(wx.Frame): self.gridPanel.DestroyChildren() self.fields.clear() - #Setup textboxes + # Setup textboxes for field, defaultVal in view.getFields().iteritems(): textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) self.fields[field] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) - sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) + sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if defaultVal is not None: if not isinstance(defaultVal, basestring): defaultVal = ("%f" % defaultVal).rstrip("0") @@ -204,7 +207,7 @@ class GraphFrame(wx.Frame): try: success, status = view.getPoints(fit, values) if not success: - #TODO: Add a pwetty statys bar to report errors with + # TODO: Add a pwetty statys bar to report errors with self.SetStatusText(status) return @@ -218,15 +221,15 @@ class GraphFrame(wx.Frame): return if self.legendFix and len(legend) > 0: - leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False) + leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) - elif not self.legendFix and len(legend) >0: - leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False, frameon = False) + elif not self.legendFix and len(legend) > 0: + leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) for t in leg.get_texts(): t.set_fontsize('small') @@ -269,10 +272,10 @@ class FitList(wx.Panel): fitToolTip = wx.ToolTip("Drag a fit into this list to graph it") self.fitList.SetToolTip(fitToolTip) + class FitDisplay(gui.display.Display): DEFAULT_COLS = ["Base Icon", "Base Name"] def __init__(self, parent): gui.display.Display.__init__(self, parent) - diff --git a/gui/implantView.py b/gui/implantView.py index 85991cf24..3de27f1bc 100644 --- a/gui/implantView.py +++ b/gui/implantView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import gui.display as d @@ -26,20 +26,21 @@ from gui.contextMenu import ContextMenu import globalEvents as GE from eos.types import ImplantLocation from service.fit import Fit +from service.market import Market class ImplantView(wx.Panel): def __init__(self, parent): - wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL ) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL) self.mainFrame = gui.mainFrame.MainFrame.getInstance() mainSizer = wx.BoxSizer(wx.VERTICAL) self.implantDisplay = ImplantDisplay(self) - mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0 ) + mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0) radioSizer = wx.BoxSizer(wx.HORIZONTAL) - radioSizer.AddSpacer(( 0, 0), 1, wx.EXPAND, 5) + radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) self.rbFit = wx.RadioButton(self, id=wx.ID_ANY, label="Use Fit-specific Implants", style=wx.RB_GROUP) self.rbChar = wx.RadioButton(self, id=wx.ID_ANY, label="Use Character Implants") radioSizer.Add(self.rbFit, 0, wx.ALL, 5) @@ -48,7 +49,7 @@ class ImplantView(wx.Panel): mainSizer.Add(radioSizer, 0, wx.EXPAND, 5) - self.SetSizer( mainSizer ) + self.SetSizer(mainSizer) self.SetAutoLayout(True) self.Bind(wx.EVT_RADIOBUTTON, self.OnRadioSelect, self.rbFit) @@ -93,12 +94,12 @@ class ImplantDisplay(d.Display): self.Bind(wx.EVT_LEFT_DOWN, self.click) self.Bind(wx.EVT_KEY_UP, self.kbEvent) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() @@ -112,7 +113,7 @@ class ImplantDisplay(d.Display): self.Parent.Parent.Parent.DisablePage(self.Parent, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -121,7 +122,8 @@ class ImplantDisplay(d.Display): self.original = fit.implants if fit is not None else None self.implants = stuff = fit.appliedImplants if fit is not None else None - if stuff is not None: stuff.sort(key=lambda implant: implant.slot) + if stuff is not None: + stuff.sort(key=lambda implant: implant.slot) if event.fitID != self.lastFitId: self.lastFitId = event.fitID diff --git a/gui/itemStats.py b/gui/itemStats.py index 4942c67d6..fe56f3a09 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,39 +15,32 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + +import re +import os +import csv +import sys +import subprocess import wx -import re -import gui.mainFrame -from gui.bitmapLoader import BitmapLoader -import sys -import wx.lib.mixins.listctrl as listmix import wx.html -from eos.types import Fit, Ship, Citadel, Module, Skill, Booster, Implant, Drone, Mode, Fighter -from gui.utils.numberFormatter import formatAmount +import wx.lib.mixins.listctrl as listmix + import config -from gui.contextMenu import ContextMenu -from gui.utils.numberFormatter import formatAmount -import csv +from eos.types import Fit, Ship, Citadel, Module, Skill, Booster, Implant, Drone, Mode, Fighter from service.market import Market from service.attribute import Attribute +import gui.mainFrame +from gui.bitmapLoader import BitmapLoader +from gui.utils.numberFormatter import formatAmount +from gui.contextMenu import ContextMenu -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict class ItemStatsDialog(wx.Dialog): counter = 0 - def __init__( - self, - victim, - fullContext=None, - pos=wx.DefaultPosition, - size=wx.DefaultSize, - maximized = False - ): + + def __init__(self, victim, fullContext=None, pos=wx.DefaultPosition, size=wx.DefaultSize, maximized=False): wx.Dialog.__init__( self, @@ -56,7 +49,7 @@ class ItemStatsDialog(wx.Dialog): title="Item stats", pos=pos, size=size, - style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER| wx.SYSTEM_MENU + style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU ) empty = getattr(victim, "isEmpty", False) @@ -78,26 +71,26 @@ class ItemStatsDialog(wx.Dialog): victim = None self.context = itmContext if item.icon is not None: - before,sep,after = item.icon.iconFile.rpartition("_") - iconFile = "%s%s%s" % (before,sep,"0%s" % after if len(after) < 2 else after) + before, sep, after = item.icon.iconFile.rpartition("_") + iconFile = "%s%s%s" % (before, sep, "0%s" % after if len(after) < 2 else after) itemImg = BitmapLoader.getBitmap(iconFile, "icons") if itemImg is not None: self.SetIcon(wx.IconFromBitmap(itemImg)) - self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, " (%d)"%item.ID if config.debug else "")) + self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, " (%d)" % item.ID if config.debug else "")) self.SetMinSize((300, 200)) if "wxGTK" in wx.PlatformInfo: # GTK has huge tab widgets, give it a bit more room self.SetSize((580, 500)) else: self.SetSize((550, 500)) - #self.SetMaxSize((500, -1)) + # self.SetMaxSize((500, -1)) self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.container = ItemStatsContainer(self, victim, item, itmContext) self.mainSizer.Add(self.container, 1, wx.EXPAND) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + self.mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) self.SetSizer(self.mainSizer) @@ -113,13 +106,13 @@ class ItemStatsDialog(wx.Dialog): counter = ItemStatsDialog.counter dlgStep = 30 - if counter * dlgStep > ppos.x+psize.width-dlgsize.x or counter * dlgStep > ppos.y+psize.height-dlgsize.y: + if counter * dlgStep > ppos.x + psize.width - dlgsize.x or counter * dlgStep > ppos.y + psize.height - dlgsize.y: ItemStatsDialog.counter = 1 dlgx = ppos.x + counter * dlgStep dlgy = ppos.y + counter * dlgStep if pos == wx.DefaultPosition: - self.SetPosition((dlgx,dlgy)) + self.SetPosition((dlgx, dlgy)) else: self.SetPosition(pos) if maximized: @@ -139,26 +132,22 @@ class ItemStatsDialog(wx.Dialog): def closeEvent(self, event): - if self.dlgOrder==ItemStatsDialog.counter: + if self.dlgOrder == ItemStatsDialog.counter: ItemStatsDialog.counter -= 1 self.parentWnd.UnregisterStatsWindow(self) self.Destroy() -########################################################################### -## Class ItemStatsContainer -########################################################################### -class ItemStatsContainer ( wx.Panel ): - - def __init__( self, parent, stuff, item, context = None): - wx.Panel.__init__ ( self, parent ) +class ItemStatsContainer(wx.Panel): + def __init__(self, parent, stuff, item, context=None): + wx.Panel.__init__(self, parent) sMkt = Market.getInstance() - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.nbContainer = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.nbContainer, 1, wx.EXPAND |wx.ALL, 2 ) + self.nbContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.nbContainer, 1, wx.EXPAND | wx.ALL, 2) if item.traits is not None: self.traits = ItemTraits(self.nbContainer, stuff, item) @@ -189,7 +178,7 @@ class ItemStatsContainer ( wx.Panel ): self.SetSizer(mainSizer) self.Layout() - def __del__( self ): + def __del__(self): pass def mouseHit(self, event): @@ -197,54 +186,36 @@ class ItemStatsContainer ( wx.Panel ): if tab != -1: self.nbContainer.SetSelection(tab) -########################################################################### -## Class AutoListCtrl -########################################################################### class AutoListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter): - - def __init__(self, parent, ID, pos=wx.DefaultPosition, - size=wx.DefaultSize, style=0): + def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, ID, pos, size, style) listmix.ListCtrlAutoWidthMixin.__init__(self) listmix.ListRowHighlighter.__init__(self) -########################################################################### -## Class AutoListCtrl -########################################################################### class AutoListCtrlNoHighlight(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter): - - def __init__(self, parent, ID, pos=wx.DefaultPosition, - size=wx.DefaultSize, style=0): + def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, ID, pos, size, style) listmix.ListCtrlAutoWidthMixin.__init__(self) -########################################################################### -## Class ItemTraits -########################################################################### - -class ItemTraits ( wx.Panel ): +class ItemTraits(wx.Panel): def __init__(self, parent, stuff, item): - wx.Panel.__init__ (self, parent) + wx.Panel.__init__(self, parent) mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainSizer) self.traits = wx.html.HtmlWindow(self) self.traits.SetPage(item.traits.traitText) - mainSizer.Add(self.traits, 1, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0) self.Layout() -########################################################################### -## Class ItemDescription -########################################################################### - -class ItemDescription ( wx.Panel ): +class ItemDescription(wx.Panel): def __init__(self, parent, stuff, item): - wx.Panel.__init__ (self, parent) + wx.Panel.__init__(self, parent) mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainSizer) @@ -265,24 +236,18 @@ class ItemDescription ( wx.Panel ): self.description.SetPage(desc) - mainSizer.Add(self.description, 1, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(self.description, 1, wx.ALL | wx.EXPAND, 0) self.Layout() -########################################################################### -## Class ItemParams -########################################################################### class ItemParams (wx.Panel): - def __init__(self, parent, stuff, item, context = None): - wx.Panel.__init__ (self, parent) - mainSizer = wx.BoxSizer( wx.VERTICAL ) + def __init__(self, parent, stuff, item, context=None): + wx.Panel.__init__(self, parent) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.paramList = AutoListCtrl(self, wx.ID_ANY, - style = #wx.LC_HRULES | - #wx.LC_NO_HEADER | - wx.LC_REPORT |wx.LC_SINGLE_SEL |wx.LC_VRULES |wx.NO_BORDER) - mainSizer.Add( self.paramList, 1, wx.ALL|wx.EXPAND, 0 ) - self.SetSizer( mainSizer ) + self.paramList = AutoListCtrl(self, wx.ID_ANY, style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) + mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0) + self.SetSizer(mainSizer) self.toggleView = 1 self.stuff = stuff @@ -291,29 +256,29 @@ class ItemParams (wx.Panel): self.attrValues = {} self._fetchValues() - self.m_staticline = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline, 0, wx.EXPAND) - bSizer = wx.BoxSizer( wx.HORIZONTAL ) + self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline, 0, wx.EXPAND) + bSizer = wx.BoxSizer(wx.HORIZONTAL) - self.totalAttrsLabel = wx.StaticText( self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT) + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) - self.toggleViewBtn = wx.ToggleButton( self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.exportStatsBtn = wx.ToggleButton( self, wx.ID_ANY, u"Export Item Stats", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.exportStatsBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, u"Export Item Stats", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.exportStatsBtn, 0, wx.ALIGN_CENTER_VERTICAL) if stuff is not None: - self.refreshBtn = wx.Button( self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) - bSizer.Add( self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.refreshBtn.Bind( wx.EVT_BUTTON, self.RefreshValues ) + self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT) + bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshValues) - mainSizer.Add( bSizer, 0, wx.ALIGN_RIGHT) + mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT) self.PopulateList() - self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleViewMode) + self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode) self.exportStatsBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ExportItemStats) def _fetchValues(self): @@ -349,7 +314,7 @@ class ItemParams (wx.Panel): event.Skip() def ToggleViewMode(self, event): - self.toggleView *=-1 + self.toggleView *= -1 self.UpdateList() event.Skip() @@ -413,17 +378,17 @@ class ItemParams (wx.Panel): ) def PopulateList(self): - self.paramList.InsertColumn(0,"Attribute") - self.paramList.InsertColumn(1,"Current Value") + self.paramList.InsertColumn(0, "Attribute") + self.paramList.InsertColumn(1, "Current Value") if self.stuff is not None: - self.paramList.InsertColumn(2,"Base Value") - self.paramList.SetColumnWidth(0,110) - self.paramList.SetColumnWidth(1,90) + self.paramList.InsertColumn(2, "Base Value") + self.paramList.SetColumnWidth(0, 110) + self.paramList.SetColumnWidth(1, 90) if self.stuff is not None: - self.paramList.SetColumnWidth(2,90) + self.paramList.SetColumnWidth(2, 90) self.paramList.setResizeColumn(0) self.imageList = wx.ImageList(16, 16) - self.paramList.SetImageList(self.imageList,wx.IMAGE_LIST_SMALL) + self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) names = list(self.attrValues.iterkeys()) names.sort() @@ -462,7 +427,6 @@ class ItemParams (wx.Panel): else: attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) - index = self.paramList.InsertImageStringItem(sys.maxint, attrName, attrIcon) idNameMap[idCount] = attrName self.paramList.SetItemData(index, idCount) @@ -486,11 +450,9 @@ class ItemParams (wx.Panel): if self.stuff is not None: self.paramList.SetStringItem(index, 2, valueUnitDefault) - - self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2])) self.paramList.RefreshRows() - self.totalAttrsLabel.SetLabel("%d attributes. " %idCount) + self.totalAttrsLabel.SetLabel("%d attributes. " % idCount) self.Layout() def TranslateValueUnit(self, value, unitName, unitDisplayName): @@ -506,31 +468,30 @@ class ItemParams (wx.Panel): attribute = Attribute.getInstance().getAttributeInfo(value) return "%s (%d)" % (attribute.name.capitalize(), value) - trans = {"Inverse Absolute Percent": (lambda: (1-value)*100, unitName), - "Inversed Modifier Percent": (lambda: (1-value) * 100, unitName), + trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName), + "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), "Modifier Percent": (lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), "Volume": (lambda: value, u"m\u00B3"), "Sizeclass": (lambda: value, ""), - "Absolute Percent": (lambda: (value * 100) , unitName), + "Absolute Percent": (lambda: (value * 100), unitName), "Milliseconds": (lambda: value / 1000.0, unitName), "typeID": (itemIDCallback, ""), - "groupID": (groupIDCallback,""), + "groupID": (groupIDCallback, ""), "attributeID": (attributeIDCallback, "")} override = trans.get(unitDisplayName) if override is not None: - - if type(override[0]()) == type(str()): - fvalue = override[0]() + v = override[0]() + if isinstance(v, str): + fvalue = v + elif isinstance(v, (int, float, long)): + fvalue = formatAmount(v, 3, 0, 0) else: - v = override[0]() - if isinstance(v, (int, float, long)): - fvalue = formatAmount(v, 3, 0, 0) - else: - fvalue = v - return "%s %s" % (fvalue , override[1]) + fvalue = v + return "%s %s" % (fvalue, override[1]) else: - return "%s %s" % (formatAmount(value, 3, 0),unitName) + return "%s %s" % (formatAmount(value, 3, 0), unitName) + class ItemCompare(wx.Panel): def __init__(self, parent, stuff, item, items, context=None): @@ -538,9 +499,7 @@ class ItemCompare(wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) self.paramList = AutoListCtrl(self, wx.ID_ANY, - style= # wx.LC_HRULES | - # wx.LC_NO_HEADER | - wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) + style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0) self.SetSizer(mainSizer) @@ -604,7 +563,7 @@ class ItemCompare(wx.Panel): self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode) self.Bind(wx.EVT_LIST_COL_CLICK, self.SortCompareCols) - def SortCompareCols(self,event): + def SortCompareCols(self, event): self.Freeze() self.paramList.ClearAll() self.PopulateList(event.Column) @@ -628,7 +587,7 @@ class ItemCompare(wx.Panel): def processPrices(self, prices): for i, price in enumerate(prices): - self.paramList.SetStringItem(i, len(self.attrs)+1, formatAmount(price.price, 3, 3, 9, currency=True)) + self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(price.price, 3, 3, 9, currency=True)) def PopulateList(self, sort=None): @@ -659,11 +618,11 @@ class ItemCompare(wx.Panel): for i, attr in enumerate(self.attrs.keys()): name = self.attrs[attr].displayName if self.attrs[attr].displayName else attr - self.paramList.InsertColumn(i+1, name) - self.paramList.SetColumnWidth(i+1, 120) + self.paramList.InsertColumn(i + 1, name) + self.paramList.SetColumnWidth(i + 1, 120) - self.paramList.InsertColumn(len(self.attrs)+1, "Price") - self.paramList.SetColumnWidth(len(self.attrs)+1, 60) + self.paramList.InsertColumn(len(self.attrs) + 1, "Price") + self.paramList.SetColumnWidth(len(self.attrs) + 1, 60) sMkt = Market.getInstance() sMkt.getPrices([x.ID for x in self.items], self.processPrices) @@ -681,7 +640,7 @@ class ItemCompare(wx.Panel): else: valueUnit = formatAmount(value, 3, 0, 0) - self.paramList.SetStringItem(i, x+1, valueUnit) + self.paramList.SetStringItem(i, x + 1, valueUnit) self.paramList.RefreshRows() self.Layout() @@ -713,37 +672,30 @@ class ItemCompare(wx.Panel): override = trans.get(unitDisplayName) if override is not None: - - if type(override[0]()) == type(str()): - fvalue = override[0]() + v = override[0]() + if isinstance(v, str): + fvalue = v + elif isinstance(v, (int, float, long)): + fvalue = formatAmount(v, 3, 0, 0) else: - v = override[0]() - if isinstance(v, (int, float, long)): - fvalue = formatAmount(v, 3, 0, 0) - else: - fvalue = v + fvalue = v return "%s %s" % (fvalue, override[1]) else: return "%s %s" % (formatAmount(value, 3, 0), unitName) -########################################################################### -## Class ItemRequirements -########################################################################### - -class ItemRequirements ( wx.Panel ): - +class ItemRequirements(wx.Panel): def __init__(self, parent, stuff, item): - wx.Panel.__init__ (self, parent, style = wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) - #itemId is set by the parent. - self.romanNb = ["0","I","II","III","IV","V","VI","VII","VIII","IX","X"] - self.skillIdHistory=[] - mainSizer = wx.BoxSizer( wx.VERTICAL ) + # itemId is set by the parent. + self.romanNb = ["0", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"] + self.skillIdHistory = [] + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.reqTree = wx.TreeCtrl(self, style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER) + self.reqTree = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER) - mainSizer.Add(self.reqTree, 1, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(self.reqTree, 1, wx.ALL | wx.EXPAND, 0) self.SetSizer(mainSizer) self.root = self.reqTree.AddRoot("WINRARZOR") @@ -753,24 +705,20 @@ class ItemRequirements ( wx.Panel ): self.reqTree.SetImageList(self.imageList) skillBookId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) - self.getFullSkillTree(item,self.root,skillBookId) + self.getFullSkillTree(item, self.root, skillBookId) self.reqTree.ExpandAll() self.Layout() - def getFullSkillTree(self,parentSkill,parent,sbIconId): + def getFullSkillTree(self, parentSkill, parent, sbIconId): for skill, level in parentSkill.requiredSkills.iteritems(): - child = self.reqTree.AppendItem(parent,"%s %s" %(skill.name,self.romanNb[int(level)]), sbIconId) + child = self.reqTree.AppendItem(parent, "%s %s" % (skill.name, self.romanNb[int(level)]), sbIconId) if skill.ID not in self.skillIdHistory: - self.getFullSkillTree(skill,child,sbIconId) + self.getFullSkillTree(skill, child, sbIconId) self.skillIdHistory.append(skill.ID) -########################################################################### -## Class ItemEffects -########################################################################### - class ItemEffects (wx.Panel): def __init__(self, parent, stuff, item): wx.Panel.__init__(self, parent) @@ -778,11 +726,7 @@ class ItemEffects (wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) - self.effectList = AutoListCtrl(self, wx.ID_ANY, - style= - # wx.LC_HRULES | - # wx.LC_NO_HEADER | - wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) + self.effectList = AutoListCtrl(self, wx.ID_ANY, style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) mainSizer.Add(self.effectList, 1, wx.ALL | wx.EXPAND, 0) self.SetSizer(mainSizer) @@ -794,17 +738,17 @@ class ItemEffects (wx.Panel): def PopulateList(self): - self.effectList.InsertColumn(0,"Name") - self.effectList.InsertColumn(1,"Active") + self.effectList.InsertColumn(0, "Name") + self.effectList.InsertColumn(1, "Active") self.effectList.InsertColumn(2, "Type") if config.debug: self.effectList.InsertColumn(3, "Run Time") - self.effectList.InsertColumn(4,"ID") + self.effectList.InsertColumn(4, "ID") - #self.effectList.SetColumnWidth(0,385) + # self.effectList.SetColumnWidth(0,385) self.effectList.setResizeColumn(0) - self.effectList.SetColumnWidth(1,50) + self.effectList.SetColumnWidth(1, 50) self.effectList.SetColumnWidth(2, 80) if config.debug: self.effectList.SetColumnWidth(3, 65) @@ -872,19 +816,17 @@ class ItemEffects (wx.Panel): If effect file does not exist, create it """ - import os - file = os.path.join(config.pyfaPath, "eos", "effects", "%s.py"%event.GetText().lower()) + file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % event.GetText().lower()) - if not os.path.isfile(file): - open(file, 'a').close() + if not os.path.isfile(file_): + open(file_, 'a').close() if 'wxMSW' in wx.PlatformInfo: - os.startfile(file) + os.startfile(file_) elif 'wxMac' in wx.PlatformInfo: - os.system("open "+file) + os.system("open " + file_) else: - import subprocess - subprocess.call(["xdg-open", file]) + subprocess.call(["xdg-open", file_]) def RefreshValues(self, event): self.Freeze() @@ -895,13 +837,10 @@ class ItemEffects (wx.Panel): self.Thaw() event.Skip() -########################################################################### -## Class ItemAffectedBy -########################################################################### - -class ItemAffectedBy (wx.Panel): +class ItemAffectedBy(wx.Panel): ORDER = [Fit, Ship, Citadel, Mode, Module, Drone, Fighter, Implant, Booster, Skill] + def __init__(self, parent, stuff, item): wx.Panel.__init__(self, parent) self.stuff = stuff @@ -917,33 +856,33 @@ class ItemAffectedBy (wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) - self.affectedBy = wx.TreeCtrl(self, style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER) - mainSizer.Add(self.affectedBy, 1, wx.ALL|wx.EXPAND, 0) + self.affectedBy = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER) + mainSizer.Add(self.affectedBy, 1, wx.ALL | wx.EXPAND, 0) - self.m_staticline = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - mainSizer.Add( self.m_staticline, 0, wx.EXPAND) - bSizer = wx.BoxSizer( wx.HORIZONTAL ) + mainSizer.Add(self.m_staticline, 0, wx.EXPAND) + bSizer = wx.BoxSizer(wx.HORIZONTAL) - self.toggleExpandBtn = wx.ToggleButton( self, wx.ID_ANY, u"Expand All", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.toggleExpandBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.toggleExpandBtn = wx.ToggleButton(self, wx.ID_ANY, u"Expand All", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.toggleExpandBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.toggleNameBtn = wx.ToggleButton( self, wx.ID_ANY, u"Toggle Names", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.toggleNameBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.toggleNameBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle Names", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.toggleNameBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.toggleViewBtn = wx.ToggleButton( self, wx.ID_ANY, u"Toggle View", wx.DefaultPosition, wx.DefaultSize, 0 ) - bSizer.Add( self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle View", wx.DefaultPosition, wx.DefaultSize, 0) + bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) if stuff is not None: - self.refreshBtn = wx.Button( self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT ) - bSizer.Add( self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.refreshBtn.Bind( wx.EVT_BUTTON, self.RefreshTree ) + self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT) + bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) + self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshTree) - self.toggleNameBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleNameMode) - self.toggleExpandBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleExpand) - self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleViewMode) + self.toggleNameBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleNameMode) + self.toggleExpandBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleExpand) + self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode) - mainSizer.Add( bSizer, 0, wx.ALIGN_RIGHT) + mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT) self.SetSizer(mainSizer) self.PopulateTree() self.Layout() @@ -965,9 +904,9 @@ class ItemAffectedBy (wx.Panel): # Skills are different in that they don't have itemModifiedAttributes, # which is needed if we send the container to itemStats dialog. So # instead, we send the item. - type = stuff.__class__.__name__ - contexts.append(("itemStats", type)) - menu = ContextMenu.getMenu(stuff if type != "Skill" else stuff.item, *contexts) + type_ = stuff.__class__.__name__ + contexts.append(("itemStats", type_)) + menu = ContextMenu.getMenu(stuff if type_ != "Skill" else stuff.item, *contexts) self.PopupMenu(menu) def ExpandCollapseTree(self): @@ -983,7 +922,7 @@ class ItemAffectedBy (wx.Panel): self.Thaw() - def ToggleExpand(self,event): + def ToggleExpand(self, event): self.expand *= -1 self.ExpandCollapseTree() @@ -1043,20 +982,22 @@ class ItemAffectedBy (wx.Panel): return attr def buildAttributeView(self, root): - # We first build a usable dictionary of items. The key is either a fit - # if the afflictions stem from a projected fit, or self.stuff if they - # are local afflictions (everything else, even gang boosts at this time) - # The value of this is yet another dictionary in the following format: - # - # "attribute name": { - # "Module Name": [ - # class of affliction, - # affliction item (required due to GH issue #335) - # modifier type - # amount of modification - # whether this affliction was projected - # ] - # } + """ + We first build a usable dictionary of items. The key is either a fit + if the afflictions stem from a projected fit, or self.stuff if they + are local afflictions (everything else, even gang boosts at this time) + The value of this is yet another dictionary in the following format: + + "attribute name": { + "Module Name": [ + class of affliction, + affliction item (required due to GH issue #335) + modifier type + amount of modification + whether this affliction was projected + ] + } + """ attributes = self.stuff.itemModifiedAttributes if self.item == self.stuff.item else self.stuff.chargeModifiedAttributes container = {} @@ -1168,20 +1109,21 @@ class ItemAffectedBy (wx.Panel): treeItem = self.affectedBy.AppendItem(child, display, itemIcon) self.affectedBy.SetPyData(treeItem, afflictor) - def buildModuleView(self, root): - # We first build a usable dictionary of items. The key is either a fit - # if the afflictions stem from a projected fit, or self.stuff if they - # are local afflictions (everything else, even gang boosts at this time) - # The value of this is yet another dictionary in the following format: - # - # "Module Name": [ - # class of affliction, - # set of afflictors (such as 2 of the same module), - # info on affliction (attribute name, modifier, and modification amount), - # item that will be used to determine icon (required due to GH issue #335) - # whether this affliction is actually used (unlearned skills are not used) - # ] + """ + We first build a usable dictionary of items. The key is either a fit + if the afflictions stem from a projected fit, or self.stuff if they + are local afflictions (everything else, even gang boosts at this time) + The value of this is yet another dictionary in the following format: + + "Module Name": [ + class of affliction, + set of afflictors (such as 2 of the same module), + info on affliction (attribute name, modifier, and modification amount), + item that will be used to determine icon (required due to GH issue #335) + whether this affliction is actually used (unlearned skills are not used) + ] + """ attributes = self.stuff.itemModifiedAttributes if self.item == self.stuff.item else self.stuff.chargeModifiedAttributes container = {} @@ -1294,7 +1236,7 @@ class ItemAffectedBy (wx.Panel): attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized, attrIcon)) - attrSorted = sorted(attributes, key = lambda attribName: attribName[0]) + attrSorted = sorted(attributes, key=lambda attribName: attribName[0]) for attr in attrSorted: attrName, displayName, attrModifier, attrAmount, penalized, attrIcon = attr diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 4bad30a0a..f53f617c8 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import sys import os.path @@ -30,8 +30,6 @@ from wx._core import PyDeadObjectError from wx.lib.wordwrap import wordwrap import config -import threading -import webbrowser import gui.aboutData import gui.chromeTabs @@ -74,29 +72,32 @@ from service.settings import HTMLExportSettings from time import gmtime, strftime -if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): +if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): from service.crest import CrestModes from gui.crestFittings import CrestFittings, ExportToEve, CrestMgmt try: from gui.propertyEditor import AttributeEditor disableOverrideEditor = False - except ImportError, e: - print "Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled."%e.message + except ImportError as e: + print("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message) disableOverrideEditor = True -#dummy panel(no paint no erasebk) + +# dummy panel(no paint no erasebk) class PFPanel(wx.Panel): - def __init__(self,parent): - wx.Panel.__init__(self,parent) + def __init__(self, parent): + wx.Panel.__init__(self, parent) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnBkErase) def OnPaint(self, event): event.Skip() + def OnBkErase(self, event): pass + class OpenFitsThread(threading.Thread): def __init__(self, fits, callback): threading.Thread.__init__(self) @@ -121,34 +122,36 @@ class OpenFitsThread(threading.Thread): wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fits[-1], startup=2)) wx.CallAfter(self.callback) + class MainFrame(wx.Frame): __instance = None + @classmethod def getInstance(cls): return cls.__instance if cls.__instance is not None else MainFrame() def __init__(self, title): - self.title=title + self.title = title wx.Frame.__init__(self, None, wx.ID_ANY, self.title) MainFrame.__instance = self - #Load stored settings (width/height/maximized..) + # Load stored settings (width/height/maximized..) self.LoadMainFrameAttribs() - #Fix for msw (have the frame background color match panel color + # Fix for msw (have the frame background color match panel color if 'wxMSW' in wx.PlatformInfo: - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) - #Load and set the icon for pyfa main window + # Load and set the icon for pyfa main window i = wx.IconFromBitmap(BitmapLoader.getBitmap("pyfa", "gui")) self.SetIcon(i) - #Create the layout and windows + # Create the layout and windows mainSizer = wx.BoxSizer(wx.HORIZONTAL) - self.browser_fitting_split = wx.SplitterWindow(self, style = wx.SP_LIVE_UPDATE) - self.fitting_additions_split = wx.SplitterWindow(self.browser_fitting_split, style = wx.SP_LIVE_UPDATE) + self.browser_fitting_split = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) + self.fitting_additions_split = wx.SplitterWindow(self.browser_fitting_split, style=wx.SP_LIVE_UPDATE) mainSizer.Add(self.browser_fitting_split, 1, wx.EXPAND | wx.LEFT, 2) @@ -161,17 +164,17 @@ class MainFrame(wx.Frame): shipBrowserImg = BitmapLoader.getImage("ship_small", "gui") self.marketBrowser = MarketBrowser(self.notebookBrowsers) - self.notebookBrowsers.AddPage(self.marketBrowser, "Market", tabImage = marketImg, showClose = False) + self.notebookBrowsers.AddPage(self.marketBrowser, "Market", tabImage=marketImg, showClose=False) self.marketBrowser.splitter.SetSashPosition(self.marketHeight) self.shipBrowser = ShipBrowser(self.notebookBrowsers) - self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage = shipBrowserImg, showClose = False) + self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage=shipBrowserImg, showClose=False) - #======================================================================= + # ===================================================================== # DISABLED FOR RC2 RELEASE - #self.fleetBrowser = FleetBrowser(self.notebookBrowsers) - #self.notebookBrowsers.AddPage(self.fleetBrowser, "Fleets", showClose = False) - #======================================================================= + # self.fleetBrowser = FleetBrowser(self.notebookBrowsers) + # self.notebookBrowsers.AddPage(self.fleetBrowser, "Fleets", showClose = False) + # ===================================================================== self.notebookBrowsers.SetSelection(1) @@ -196,7 +199,7 @@ class MainFrame(wx.Frame): self.SetSizer(mainSizer) - #Add menu + # Add menu self.addPageId = wx.NewId() self.closePageId = wx.NewId() @@ -204,23 +207,23 @@ class MainFrame(wx.Frame): self.SetMenuBar(MainMenuBar()) self.registerMenu() - #Internal vars to keep track of other windows (graphing/stats) + # Internal vars to keep track of other windows (graphing/stats) self.graphFrame = None self.statsWnds = [] self.activeStatsWnd = None self.Bind(wx.EVT_CLOSE, self.OnClose) - #Show ourselves + # Show ourselves self.Show() self.LoadPreviousOpenFits() - #Check for updates + # Check for updates self.sUpdate = Update.getInstance() self.sUpdate.CheckUpdate(self.ShowUpdateBox) - if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): + if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin) self.Bind(GE.EVT_SSO_LOGOUT, self.onSSOLogout) @@ -274,7 +277,7 @@ class MainFrame(wx.Frame): def UpdateMainFrameAttribs(self): if self.IsIconized(): return - width,height = self.GetSize() + width, height = self.GetSize() self.mainFrameAttribs["wnd_width"] = width self.mainFrameAttribs["wnd_height"] = height @@ -308,7 +311,7 @@ class MainFrame(wx.Frame): return m() if m is not None else None def getActiveView(self): - sel = self.fitMultiSwitch.GetSelectedPage() + self.fitMultiSwitch.GetSelectedPage() def CloseCurrentPage(self, evt): ms = self.fitMultiSwitch @@ -321,11 +324,11 @@ class MainFrame(wx.Frame): self.UpdateMainFrameAttribs() # save open fits - self.prevOpenFits['pyfaOpenFits'] = [] # clear old list + self.prevOpenFits['pyfaOpenFits'] = [] # clear old list for page in self.fitMultiSwitch.pages: m = getattr(page, "getActiveFit", None) if m is not None: - self.prevOpenFits['pyfaOpenFits'].append(m()) + self.prevOpenFits['pyfaOpenFits'].append(m()) # save all teh settingz SettingsProvider.getInstance().saveAll() @@ -342,37 +345,36 @@ class MainFrame(wx.Frame): info.Name = "pyfa" info.Version = gui.aboutData.versionString info.Description = wordwrap(gui.aboutData.description + "\n\nDevelopers:\n\t" + - "\n\t".join(gui.aboutData.developers) + - "\n\nAdditional credits:\n\t" + - "\n\t".join(gui.aboutData.credits) + - "\n\nLicenses:\n\t" + - "\n\t".join(gui.aboutData.licenses) + - "\n\nEVE Data: \t" + eos.config.gamedata_version + - "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + - "\nwxPython: \t" + wx.__version__ + - "\nSQLAlchemy: \t" + sqlalchemy.__version__, - 500, wx.ClientDC(self)) - if "__WXGTK__" in wx.PlatformInfo: + "\n\t".join(gui.aboutData.developers) + + "\n\nAdditional credits:\n\t" + + "\n\t".join(gui.aboutData.credits) + + "\n\nLicenses:\n\t" + + "\n\t".join(gui.aboutData.licenses) + + "\n\nEVE Data: \t" + eos.config.gamedata_version + + "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + + "\nwxPython: \t" + wx.__version__ + + "\nSQLAlchemy: \t" + sqlalchemy.__version__, + 500, wx.ClientDC(self)) + if "__WXGTK__" in wx.PlatformInfo: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" else: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") wx.AboutBox(info) - def showCharacterEditor(self, event): - dlg=CharacterEditor(self) + dlg = CharacterEditor(self) dlg.Show() def showAttrEditor(self, event): - dlg=AttributeEditor(self) + dlg = AttributeEditor(self) dlg.Show() def showTargetResistsEditor(self, event): ResistsEditorDlg(self) def showDamagePatternEditor(self, event): - dlg=DmgPatternEditorDlg(self) + dlg = DmgPatternEditorDlg(self) dlg.ShowModal() dlg.Destroy() @@ -383,26 +385,28 @@ class MainFrame(wx.Frame): """ Export active fit """ sFit = Fit.getInstance() fit = sFit.getFit(self.getActiveFit()) - defaultFile = "%s - %s.xml"%(fit.ship.item.name, fit.name) if fit else None + defaultFile = "%s - %s.xml" % (fit.ship.item.name, fit.name) if fit else None dlg = wx.FileDialog(self, "Save Fitting As...", - wildcard = "EVE XML fitting files (*.xml)|*.xml", - style = wx.FD_SAVE, + wildcard="EVE XML fitting files (*.xml)|*.xml", + style=wx.FD_SAVE, defaultFile=defaultFile) if dlg.ShowModal() == wx.ID_OK: - format = dlg.GetFilterIndex() + format_ = dlg.GetFilterIndex() path = dlg.GetPath() - if format == 0: + if format_ == 0: output = sFit.exportXml(None, self.getActiveFit()) if '.' not in os.path.basename(path): path += ".xml" else: - print "oops, invalid fit format %d" % format + print("oops, invalid fit format %d" % format_) dlg.Destroy() return - file = open(path, "w", encoding="utf-8") - file.write(output) - file.close() + + with open(path, "w", encoding="utf-8") as openfile: + openfile.write(output) + openfile.close() + dlg.Destroy() def showPreferenceDialog(self, event): @@ -431,7 +435,7 @@ class MainFrame(wx.Frame): self.Bind(wx.EVT_MENU, self.loadDatabaseDefaults, id=menuBar.importDatabaseDefaultsId) # Widgets Inspector if config.debug: - self.Bind(wx.EVT_MENU, self.openWXInspectTool, id = self.widgetInspectMenuID) + self.Bind(wx.EVT_MENU, self.openWXInspectTool, id=self.widgetInspectMenuID) # About self.Bind(wx.EVT_MENU, self.ShowAboutBox, id=wx.ID_ABOUT) # Char editor @@ -459,32 +463,32 @@ class MainFrame(wx.Frame): # Preference dialog self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id=wx.ID_PREFERENCES) # User guide - self.Bind(wx.EVT_MENU, self.goWiki, id = menuBar.wikiId) + self.Bind(wx.EVT_MENU, self.goWiki, id=menuBar.wikiId) # EVE Forums - self.Bind(wx.EVT_MENU, self.goForums, id = menuBar.forumId) + self.Bind(wx.EVT_MENU, self.goForums, id=menuBar.forumId) # Save current character - self.Bind(wx.EVT_MENU, self.saveChar, id = menuBar.saveCharId) + self.Bind(wx.EVT_MENU, self.saveChar, id=menuBar.saveCharId) # Save current character as another character - self.Bind(wx.EVT_MENU, self.saveCharAs, id = menuBar.saveCharAsId) + self.Bind(wx.EVT_MENU, self.saveCharAs, id=menuBar.saveCharAsId) # Save current character - self.Bind(wx.EVT_MENU, self.revertChar, id = menuBar.revertCharId) + self.Bind(wx.EVT_MENU, self.revertChar, id=menuBar.revertCharId) # Browse fittings - self.Bind(wx.EVT_MENU, self.eveFittings, id = menuBar.eveFittingsId) + self.Bind(wx.EVT_MENU, self.eveFittings, id=menuBar.eveFittingsId) # Export to EVE - self.Bind(wx.EVT_MENU, self.exportToEve, id = menuBar.exportToEveId) + self.Bind(wx.EVT_MENU, self.exportToEve, id=menuBar.exportToEveId) # Handle SSO event (login/logout/manage characters, depending on mode and current state) - self.Bind(wx.EVT_MENU, self.ssoHandler, id = menuBar.ssoLoginId) + self.Bind(wx.EVT_MENU, self.ssoHandler, id=menuBar.ssoLoginId) # Open attribute editor - self.Bind(wx.EVT_MENU, self.showAttrEditor, id = menuBar.attrEditorId) + self.Bind(wx.EVT_MENU, self.showAttrEditor, id=menuBar.attrEditorId) # Toggle Overrides - self.Bind(wx.EVT_MENU, self.toggleOverrides, id = menuBar.toggleOverridesId) + self.Bind(wx.EVT_MENU, self.toggleOverrides, id=menuBar.toggleOverridesId) - #Clipboard exports + # Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) - #Graphs + # Graphs self.Bind(wx.EVT_MENU, self.openGraphFrame, id=menuBar.graphFrameId) toggleSearchBoxId = wx.NewId() @@ -494,11 +498,11 @@ class MainFrame(wx.Frame): # Close Page self.Bind(wx.EVT_MENU, self.CloseCurrentPage, id=self.closePageId) - self.Bind(wx.EVT_MENU, self.HAddPage, id = self.addPageId) - self.Bind(wx.EVT_MENU, self.toggleSearchBox, id = toggleSearchBoxId) - self.Bind(wx.EVT_MENU, self.toggleShipMarket, id = toggleShipMarketId) - self.Bind(wx.EVT_MENU, self.CTabNext, id = ctabnext) - self.Bind(wx.EVT_MENU, self.CTabPrev, id = ctabprev) + self.Bind(wx.EVT_MENU, self.HAddPage, id=self.addPageId) + self.Bind(wx.EVT_MENU, self.toggleSearchBox, id=toggleSearchBoxId) + self.Bind(wx.EVT_MENU, self.toggleShipMarket, id=toggleShipMarketId) + self.Bind(wx.EVT_MENU, self.CTabNext, id=ctabnext) + self.Bind(wx.EVT_MENU, self.CTabPrev, id=ctabprev) actb = [(wx.ACCEL_CTRL, ord('T'), self.addPageId), (wx.ACCEL_CMD, ord('T'), self.addPageId), @@ -531,30 +535,30 @@ class MainFrame(wx.Frame): for i in range(0, self.additionsPane.notebook.GetPageCount()): self.additionsSelect.append(wx.NewId()) self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionsSelect[i]) - actb.append((wx.ACCEL_CMD, i+49, self.additionsSelect[i])) - actb.append((wx.ACCEL_CTRL, i+49, self.additionsSelect[i])) + actb.append((wx.ACCEL_CMD, i + 49, self.additionsSelect[i])) + actb.append((wx.ACCEL_CTRL, i + 49, self.additionsSelect[i])) # Alt+1-9 for market item selection self.itemSelect = [] for i in range(0, 9): self.itemSelect.append(wx.NewId()) - self.Bind(wx.EVT_MENU, self.ItemSelect, id = self.itemSelect[i]) + self.Bind(wx.EVT_MENU, self.ItemSelect, id=self.itemSelect[i]) actb.append((wx.ACCEL_ALT, i + 49, self.itemSelect[i])) atable = wx.AcceleratorTable(actb) self.SetAcceleratorTable(atable) def eveFittings(self, event): - dlg=CrestFittings(self) + dlg = CrestFittings(self) dlg.Show() def updateTitle(self, event): sCrest = Crest.getInstance() char = sCrest.implicitCharacter if char: - t = time.gmtime(char.eve.expires-time.time()) + t = time.gmtime(char.eve.expires - time.time()) sTime = time.strftime("%H:%M:%S", t if t >= 0 else 0) - newTitle = "%s | %s - %s"%(self.title, char.name, sTime) + newTitle = "%s | %s - %s" % (self.title, char.name, sTime) self.SetTitle(newTitle) def onSSOLogin(self, event): @@ -605,11 +609,11 @@ class MainFrame(wx.Frame): uri = sCrest.startServer() webbrowser.open(uri) else: - dlg=CrestMgmt(self) + dlg = CrestMgmt(self) dlg.Show() def exportToEve(self, event): - dlg=ExportToEve(self) + dlg = ExportToEve(self) dlg.Show() def toggleOverrides(self, event): @@ -653,7 +657,7 @@ class MainFrame(wx.Frame): def CTabPrev(self, event): self.fitMultiSwitch.PrevPage() - def HAddPage(self,event): + def HAddPage(self, event): self.fitMultiSwitch.AddPage() def toggleShipMarket(self, event): @@ -714,17 +718,19 @@ class MainFrame(wx.Frame): CopySelectDict[selected]() - dlg.Destroy() def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ sCharacter = Character.getInstance() - saveDialog = wx.FileDialog(self, "Export Skills Needed As...", - wildcard = "EVEMon skills training file (*.emp)|*.emp|" \ - "EVEMon skills training XML file (*.xml)|*.xml|" \ - "Text skills training file (*.txt)|*.txt", - style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) + saveDialog = wx.FileDialog( + self, + "Export Skills Needed As...", + wildcard=("EVEMon skills training file (*.emp)|*.emp|" + "EVEMon skills training XML file (*.xml)|*.xml|" + "Text skills training file (*.txt)|*.txt"), + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, + ) if saveDialog.ShowModal() == wx.ID_OK: saveFmtInt = saveDialog.GetFilterIndex() @@ -748,16 +754,21 @@ class MainFrame(wx.Frame): def fileImportDialog(self, event): """Handles importing single/multiple EVE XML / EFT cfg fit files""" sFit = Fit.getInstance() - dlg = wx.FileDialog(self, "Open One Or More Fitting Files", - wildcard = "EVE XML fitting files (*.xml)|*.xml|" \ - "EFT text fitting files (*.cfg)|*.cfg|" \ - "All Files (*)|*", - style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) + dlg = wx.FileDialog( + self, + "Open One Or More Fitting Files", + wildcard=("EVE XML fitting files (*.xml)|*.xml|" + "EFT text fitting files (*.cfg)|*.cfg|" + "All Files (*)|*"), + style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE + ) if (dlg.ShowModal() == wx.ID_OK): self.progressDialog = wx.ProgressDialog( - "Importing fits", - " "*100, # set some arbitrary spacing to create width in window - parent=self, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) + "Importing fits", + " " * 100, # set some arbitrary spacing to create width in window + parent=self, + style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME + ) self.progressDialog.message = None sFit.importFitsThreaded(dlg.GetPaths(), self.fileImportCallback) self.progressDialog.ShowModal() @@ -765,12 +776,15 @@ class MainFrame(wx.Frame): def backupToXml(self, event): """ Back up all fits to EVE XML file """ - defaultFile = "pyfa-fits-%s.xml"%strftime("%Y%m%d_%H%M%S", gmtime()) + defaultFile = "pyfa-fits-%s.xml" % strftime("%Y%m%d_%H%M%S", gmtime()) - saveDialog = wx.FileDialog(self, "Save Backup As...", - wildcard = "EVE XML fitting file (*.xml)|*.xml", - style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, - defaultFile=defaultFile) + saveDialog = wx.FileDialog( + self, + "Save Backup As...", + wildcard="EVE XML fitting file (*.xml)|*.xml", + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, + defaultFile=defaultFile, + ) if saveDialog.ShowModal() == wx.ID_OK: filePath = saveDialog.GetPath() @@ -778,13 +792,15 @@ class MainFrame(wx.Frame): filePath += ".xml" sFit = Fit.getInstance() - max = sFit.countAllFits() - - self.progressDialog = wx.ProgressDialog("Backup fits", - "Backing up %d fits to: %s"%(max, filePath), - maximum=max, parent=self, - style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) + max_ = sFit.countAllFits() + self.progressDialog = wx.ProgressDialog( + "Backup fits", + "Backing up %d fits to: %s" % (max_, filePath), + maximum=max_, + parent=self, + style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME, + ) Port.backupFits(filePath, self.backupCallback) self.progressDialog.ShowModal() @@ -793,21 +809,26 @@ class MainFrame(wx.Frame): sFit = Fit.getInstance() settings = HTMLExportSettings.getInstance() - max = sFit.countAllFits() + max_ = sFit.countAllFits() path = settings.getPath() if not os.path.isdir(os.path.dirname(path)): - dlg = wx.MessageDialog(self, - "Invalid Path\n\nThe following path is invalid or does not exist: \n%s\n\nPlease verify path location pyfa's preferences."%path, - "Error", wx.OK | wx.ICON_ERROR) + dlg = wx.MessageDialog( + self, + "Invalid Path\n\nThe following path is invalid or does not exist: \n%s\n\nPlease verify path location pyfa's preferences." % path, + "Error", + wx.OK | wx.ICON_ERROR + ) if dlg.ShowModal() == wx.ID_OK: return - self.progressDialog = wx.ProgressDialog("Backup fits", - "Generating HTML file at: %s"%path, - maximum=max, parent=self, - style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) + self.progressDialog = wx.ProgressDialog( + "Backup fits", + "Generating HTML file at: %s" % path, + maximum=max_, parent=self, + style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME + ) exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback) self.progressDialog.ShowModal() @@ -844,7 +865,7 @@ class MainFrame(wx.Frame): self._openAfterImport(data) elif action == -2: dlg = wx.MessageDialog(self, - "The following error was generated\n\n%s\n\nBe aware that already processed fits were not saved"%data, + "The following error was generated\n\n%s\n\nBe aware that already processed fits were not saved" % data, "Import Error", wx.OK | wx.ICON_ERROR) if dlg.ShowModal() == wx.ID_OK: return @@ -869,10 +890,12 @@ class MainFrame(wx.Frame): def importCharacter(self, event): """ Imports character XML file from EVE API """ - dlg = wx.FileDialog(self, "Open One Or More Character Files", - wildcard="EVE API XML character files (*.xml)|*.xml|" \ - "All Files (*)|*", - style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) + dlg = wx.FileDialog( + self, + "Open One Or More Character Files", + wildcard="EVE API XML character files (*.xml)|*.xml|All Files (*)|*", + style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE + ) if dlg.ShowModal() == wx.ID_OK: self.waitDialog = wx.BusyInfo("Importing Character...") @@ -906,4 +929,3 @@ class MainFrame(wx.Frame): if not wnd: wnd = self InspectionTool().Show(wnd, True) - diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 88cc6de2e..2de7f4b80 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,20 +15,23 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx + import config -from gui.bitmapLoader import BitmapLoader +from service.crest import Crest +from service.character import Character import gui.mainFrame import gui.graphFrame import gui.globalEvents as GE -from service.crest import Crest -from service.character import Character +from gui.bitmapLoader import BitmapLoader -if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): + +if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): from service.crest import CrestModes + class MainMenuBar(wx.MenuBar): def __init__(self): self.characterEditorId = wx.NewId() @@ -52,7 +55,7 @@ class MainMenuBar(wx.MenuBar): self.toggleOverridesId = wx.NewId() self.importDatabaseDefaultsId = wx.NewId() - if 'wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0): + if 'wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0): wx.ID_COPY = wx.NewId() wx.ID_PASTE = wx.NewId() @@ -81,8 +84,8 @@ class MainMenuBar(wx.MenuBar): editMenu = wx.Menu() self.Append(editMenu, "&Edit") - #editMenu.Append(wx.ID_UNDO) - #editMenu.Append(wx.ID_REDO) + # editMenu.Append(wx.ID_UNDO) + # editMenu.Append(wx.ID_REDO) editMenu.Append(wx.ID_COPY, "To Clipboard\tCTRL+C", "Export a fit to the clipboard") editMenu.Append(wx.ID_PASTE, "From Clipboard\tCTRL+V", "Import a fit from the clipboard") @@ -116,11 +119,11 @@ class MainMenuBar(wx.MenuBar): windowMenu.AppendItem(graphFrameItem) preferencesShortCut = "CTRL+," if 'wxMac' in wx.PlatformInfo else "CTRL+P" - preferencesItem = wx.MenuItem(windowMenu, wx.ID_PREFERENCES, "Preferences\t"+preferencesShortCut) + preferencesItem = wx.MenuItem(windowMenu, wx.ID_PREFERENCES, "Preferences\t" + preferencesShortCut) preferencesItem.SetBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) windowMenu.AppendItem(preferencesItem) - if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): + if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): self.sCrest = Crest.getInstance() # CREST Menu @@ -155,7 +158,7 @@ class MainMenuBar(wx.MenuBar): helpMenu.Append(wx.ID_ABOUT) if config.debug: - helpMenu.Append( self.mainFrame.widgetInspectMenuID, "Open Widgets Inspect tool", "Open Widgets Inspect tool") + helpMenu.Append(self.mainFrame.widgetInspectMenuID, "Open Widgets Inspect tool", "Open Widgets Inspect tool") self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) @@ -175,5 +178,3 @@ class MainMenuBar(wx.MenuBar): self.Enable(self.revertCharId, char.isDirty) event.Skip() - - diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index a06b89d39..33db5d501 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,23 +15,24 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx -import gui.display as d -from gui.cachingImageList import CachingImageList -from gui.contextMenu import ContextMenu -import gui.PFSearchBox as SBox from service.market import Market from service.attribute import Attribute - +import gui.display as d +import gui.PFSearchBox as SBox +from gui.cachingImageList import CachingImageList +from gui.contextMenu import ContextMenu from gui.bitmapLoader import BitmapLoader + ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent() RECENTLY_USED_MODULES = -2 MAX_RECENTLY_USED_MODULES = 20 + class MarketBrowser(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) @@ -42,7 +43,7 @@ class MarketBrowser(wx.Panel): self.search = SearchBox(self) vbox.Add(self.search, 0, wx.EXPAND) - self.splitter = wx.SplitterWindow(self, style = wx.SP_LIVE_UPDATE) + self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) vbox.Add(self.splitter, 1, wx.EXPAND) # Grab market service instance and create child objects @@ -103,16 +104,18 @@ class MarketBrowser(wx.Panel): def jump(self, item): self.marketView.jump(item) + class SearchBox(SBox.PFSearchBox): def __init__(self, parent, **kwargs): SBox.PFSearchBox.__init__(self, parent, **kwargs) - cancelBitmap = BitmapLoader.getBitmap("fit_delete_small","gui") - searchBitmap = BitmapLoader.getBitmap("fsearch_small","gui") + cancelBitmap = BitmapLoader.getBitmap("fit_delete_small", "gui") + searchBitmap = BitmapLoader.getBitmap("fsearch_small", "gui") self.SetSearchBitmap(searchBitmap) self.SetCancelBitmap(cancelBitmap) self.ShowSearchButton() self.ShowCancelButton() + class MarketTree(wx.TreeCtrl): def __init__(self, parent, marketBrowser): wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) @@ -136,7 +139,7 @@ class MarketTree(wx.TreeCtrl): # Add recently used modules node rumIconId = self.addImage("market_small", "gui") - self.AppendItem(self.root, "Recently Used Modules", rumIconId, data = wx.TreeItemData(RECENTLY_USED_MODULES)) + self.AppendItem(self.root, "Recently Used Modules", rumIconId, data=wx.TreeItemData(RECENTLY_USED_MODULES)) # Bind our lookup method to when the tree gets expanded self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) @@ -185,10 +188,10 @@ class MarketTree(wx.TreeCtrl): for id in sMkt.ROOT_MARKET_GROUPS: if id in jumpList: - jumpList = jumpList[:jumpList.index(id)+1] + jumpList = jumpList[:jumpList.index(id) + 1] item = self.root - for i in range(len(jumpList) -1, -1, -1): + for i in range(len(jumpList) - 1, -1, -1): target = jumpList[i] child, cookie = self.GetFirstChild(item) while self.GetItemPyData(child) != target: @@ -200,6 +203,7 @@ class MarketTree(wx.TreeCtrl): self.SelectItem(item) self.marketBrowser.itemView.selectionMade(forcedMetaSelect=metaId) + class ItemView(d.Display): DEFAULT_COLS = ["Base Icon", "Base Name", @@ -242,12 +246,11 @@ class ItemView(d.Display): if row != -1: data = wx.PyTextDataObject() - data.SetText("market:"+str(self.active[row].ID)) + data.SetText("market:" + str(self.active[row].ID)) dropSource = wx.DropSource(self) dropSource.SetData(data) - res = dropSource.DoDragDrop() - + dropSource.DoDragDrop() def itemActivated(self, event=None): # Check if something is selected, if so, spawn the menu for it @@ -391,12 +394,12 @@ class ItemView(d.Display): mktgrpid = sMkt.getMarketGroupByItem(item).ID except AttributeError: mktgrpid = None - print "unable to find market group for", item.name + print("unable to find market group for", item.name) parentname = sMkt.getParentItemByItem(item).name # Get position of market group metagrpid = sMkt.getMetaGroupIdByItem(item) metatab = self.metaMap.get(metagrpid) - metalvl = self.metalvls.get(item.ID, 0) + metalvl = self.metalvls.get(item.ID, 0) return (catname, mktgrpid, parentname, metatab, metalvl, item.name) def contextMenu(self, event): @@ -442,7 +445,7 @@ class ItemView(d.Display): for i, item in enumerate(items[:9]): # set shortcut info for first 9 modules - item.marketShortcut = i+1 + item.marketShortcut = i + 1 d.Display.refresh(self, items) diff --git a/gui/multiSwitch.py b/gui/multiSwitch.py index 91532785d..766524a61 100644 --- a/gui/multiSwitch.py +++ b/gui/multiSwitch.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import gui.chromeTabs import gui.builtinViews.emptyView @@ -24,7 +24,7 @@ import gui.builtinViews.emptyView class MultiSwitch(gui.chromeTabs.PFNotebook): def __init__(self, parent): gui.chromeTabs.PFNotebook.__init__(self, parent) - #self.AddPage() # now handled by mainFrame + # self.AddPage() # now handled by mainFrame self.handlers = handlers = [] for type in TabSpawner.tabTypes: handlers.append(type(self)) diff --git a/gui/notesView.py b/gui/notesView.py index c8f648440..f607efb0b 100644 --- a/gui/notesView.py +++ b/gui/notesView.py @@ -1,9 +1,8 @@ import wx - +from service.fit import Fit import gui.globalEvents as GE import gui.mainFrame -from service.fit import Fit class NotesView(wx.Panel): @@ -36,7 +35,7 @@ class NotesView(wx.Panel): def onText(self, event): # delay the save so we're not writing to sqlite on every keystroke - self.saveTimer.Stop() # cancel the existing timer + self.saveTimer.Stop() # cancel the existing timer self.saveTimer.Start(1000, True) def delayedSave(self, event): diff --git a/gui/patternEditor.py b/gui/patternEditor.py index cbfb11207..8005d19aa 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,18 +15,15 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.bitmapLoader import BitmapLoader from wx.lib.intctrl import IntCtrl from gui.utils.clipboard import toClipboard, fromClipboard -from service.damagePattern import ImportError from gui.builtinViews.entityEditor import EntityEditor, BaseValidator from service.damagePattern import DamagePattern, ImportError -########################################################################### -## Class DmgPatternEditorDlg -########################################################################### + class DmgPatternTextValidor(BaseValidator): def __init__(self): @@ -47,7 +44,7 @@ class DmgPatternTextValidor(BaseValidator): raise ValueError("Damage Profile name already in use, please choose another.") return True - except ValueError, e: + except ValueError as e: wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -81,11 +78,12 @@ class DmgPatternEntityEditor(EntityEditor): sDP = DamagePattern.getInstance() sDP.deletePattern(entity) + class DmgPatternEditorDlg(wx.Dialog): DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") def __init__(self, parent): - wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Damage Pattern Editor", size = wx.Size( 400,240 )) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Damage Pattern Editor", size=wx.Size(400, 240)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -111,11 +109,11 @@ class DmgPatternEditorDlg(wx.Dialog): dmgeditSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) width = -1 - defSize = wx.Size(width,-1) + defSize = wx.Size(width, -1) - for i, type in enumerate(self.DAMAGE_TYPES): - bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big"%type, "gui")) - if i%2: + for i, type_ in enumerate(self.DAMAGE_TYPES): + bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big" % type_, "gui")) + if i % 2: style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT border = 20 else: @@ -123,13 +121,13 @@ class DmgPatternEditorDlg(wx.Dialog): border = 5 # set text edit - setattr(self, "%sEdit"%type, IntCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize)) - setattr(self, "%sPerc"%type, wx.StaticText(self, wx.ID_ANY, u"0%")) - editObj = getattr(self, "%sEdit"%type) + setattr(self, "%sEdit" % type_, IntCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize)) + setattr(self, "%sPerc" % type_, wx.StaticText(self, wx.ID_ANY, u"0%")) + editObj = getattr(self, "%sEdit" % type_) dmgeditSizer.Add(bmp, 0, style, border) dmgeditSizer.Add(editObj, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) - dmgeditSizer.Add(getattr(self, "%sPerc"%type), 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + dmgeditSizer.Add(getattr(self, "%sPerc" % type_), 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated) editObj.SetLimited(True) @@ -147,7 +145,7 @@ class DmgPatternEditorDlg(wx.Dialog): self.stNotice.Wrap(-1) perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) - footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) + footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) self.totSizer = wx.BoxSizer(wx.VERTICAL) @@ -156,8 +154,8 @@ class DmgPatternEditorDlg(wx.Dialog): mainSizer.Add(contentSizer, 1, wx.EXPAND, 0) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) self.SetSizer(mainSizer) @@ -169,13 +167,13 @@ class DmgPatternEditorDlg(wx.Dialog): bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - btn.SetMinSize( btn.GetSize() ) - btn.SetMaxSize( btn.GetSize() ) + btn.SetMinSize(btn.GetSize()) + btn.SetMaxSize(btn.GetSize()) btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction) ) + btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower()))) @@ -196,12 +194,12 @@ class DmgPatternEditorDlg(wx.Dialog): return p = self.entityEditor.getActiveEntity() - total = sum(map(lambda attr: getattr(self, "%sEdit"%attr).GetValue(), self.DAMAGE_TYPES)) - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - percObj = getattr(self, "%sPerc"%type) - setattr(p, "%sAmount"%type, editObj.GetValue()) - percObj.SetLabel("%.1f%%"%(float(editObj.GetValue())*100/total if total > 0 else 0)) + total = sum(map(lambda attr: getattr(self, "%sEdit" % attr).GetValue(), self.DAMAGE_TYPES)) + for type_ in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit" % type_) + percObj = getattr(self, "%sPerc" % type_) + setattr(p, "%sAmount" % type_, editObj.GetValue()) + percObj.SetLabel("%.1f%%" % (float(editObj.GetValue()) * 100 / total if total > 0 else 0)) self.totSizer.Layout() @@ -211,15 +209,15 @@ class DmgPatternEditorDlg(wx.Dialog): DamagePattern.getInstance().saveChanges(p) def restrict(self): - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) + for type_ in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit" % type_) editObj.Enable(False) self.entityEditor.btnRename.Enable(False) self.entityEditor.btnDelete.Enable(False) def unrestrict(self): - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) + for type_ in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit" % type_) editObj.Enable() self.entityEditor.btnRename.Enable() self.entityEditor.btnDelete.Enable() @@ -255,9 +253,9 @@ class DmgPatternEditorDlg(wx.Dialog): try: sDP.importPatterns(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") - except ImportError, e: + except ImportError as e: self.stNotice.SetLabel(str(e)) - except Exception, e: + except Exception: self.stNotice.SetLabel("Could not import from clipboard: unknown errors") finally: self.entityEditor.refreshEntityList() @@ -266,5 +264,5 @@ class DmgPatternEditorDlg(wx.Dialog): def exportPatterns(self, event): sDP = DamagePattern.getInstance() - toClipboard( sDP.exportPatterns() ) + toClipboard(sDP.exportPatterns()) self.stNotice.SetLabel("Patterns exported to clipboard") diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index ada936114..1d1146c85 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,14 +15,14 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.preferenceView import PreferenceView from gui.bitmapLoader import BitmapLoader -class PreferenceDialog(wx.Dialog): +class PreferenceDialog(wx.Dialog): def __init__(self, parent): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE) self.SetTitle("pyfa - Preferences") @@ -33,22 +33,22 @@ class PreferenceDialog(wx.Dialog): self.listbook = wx.Listbook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LB_DEFAULT) self.listview = self.listbook.GetListView() - #self.listview.SetMinSize((500, -1)) - #self.listview.SetSize((500, -1)) + # self.listview.SetMinSize((500, -1)) + # self.listview.SetSize((500, -1)) - self.imageList = wx.ImageList(32,32) + self.imageList = wx.ImageList(32, 32) self.listbook.SetImageList(self.imageList) - mainSizer.Add(self.listbook, 1, wx.EXPAND | wx.TOP|wx.BOTTOM|wx.LEFT, 5) + mainSizer.Add(self.listbook, 1, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.LEFT, 5) - self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_staticline2, 0, wx.EXPAND, 5 ) + self.m_staticline2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline2, 0, wx.EXPAND, 5) - btnSizer = wx.BoxSizer( wx.HORIZONTAL ) - btnSizer.AddSpacer( ( 0, 0), 1, wx.EXPAND, 5 ) - self.btnOK = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) - btnSizer.Add( self.btnOK, 0, wx.ALL, 5 ) - mainSizer.Add(btnSizer,0 , wx.EXPAND, 5) + btnSizer = wx.BoxSizer(wx.HORIZONTAL) + btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + self.btnOK = wx.Button(self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0) + btnSizer.Add(self.btnOK, 0, wx.ALL, 5) + mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) self.SetSizer(mainSizer) self.Centre(wx.BOTH) @@ -61,12 +61,12 @@ class PreferenceDialog(wx.Dialog): else: imgID = -1 prefView.populatePanel(page) - self.listbook.AddPage(page, prefView.title, imageId = imgID) + self.listbook.AddPage(page, prefView.title, imageId=imgID) # Set the height based on a condition. Can all the panels fit in the current height? # If not, use the .GetBestVirtualSize() to ensure that all content is available. minHeight = 360 - bestFit = self.GetBestVirtualSize() + bestFit = self.GetBestVirtualSize() if minHeight > bestFit[1]: self.SetSizeWH(450, minHeight) else: diff --git a/gui/preferenceView.py b/gui/preferenceView.py index bf1ba76ed..9dd58c9e9 100644 --- a/gui/preferenceView.py +++ b/gui/preferenceView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,14 +15,13 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx + class PreferenceView(object): views = [] - def __init__(self): - pass @classmethod def register(cls): @@ -37,5 +36,5 @@ class PreferenceView(object): def getImage(self): return wx.NullBitmap -from gui.builtinPreferenceViews import * +from gui.builtinPreferenceViews import * # noqa diff --git a/gui/projectedView.py b/gui/projectedView.py index 1e753bc77..8a3135121 100644 --- a/gui/projectedView.py +++ b/gui/projectedView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx import gui.display as d @@ -25,6 +25,7 @@ from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import eos.types from service.fit import Fit +from service.market import Market class DummyItem: @@ -32,23 +33,26 @@ class DummyItem: self.name = txt self.icon = None + class DummyEntry: def __init__(self, txt): self.item = DummyItem(txt) -class ProjectedViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t +class ProjectedViewDrop(wx.PyDropTarget): + def __init__(self, dropFn): + wx.PyDropTarget.__init__(self) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t + class ProjectedView(d.Display): DEFAULT_COLS = ["State", @@ -58,7 +62,7 @@ class ProjectedView(d.Display): "Ammo"] def __init__(self, parent): - d.Display.__init__(self, parent, style = wx.LC_SINGLE_SEL | wx.BORDER_NONE) + d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) self.lastFitId = None @@ -70,7 +74,7 @@ class ProjectedView(d.Display): self.droneView = gui.droneView.DroneView - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) @@ -96,7 +100,7 @@ class ProjectedView(d.Display): sFit.project(fitID, int(data[1])) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) - def kbEvent(self,event): + def kbEvent(self, event): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: fitID = self.mainFrame.getActiveFit() @@ -107,7 +111,7 @@ class ProjectedView(d.Display): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) def handleDrag(self, type, fitID): - #Those are drags coming from pyfa sources, NOT builtin wx drags + # Those are drags coming from pyfa sources, NOT builtin wx drags if type == "fit": activeFit = self.mainFrame.getActiveFit() if activeFit: @@ -120,14 +124,14 @@ class ProjectedView(d.Display): row = event.GetIndex() if row != -1 and isinstance(self.get(row), es_Drone): data = wx.PyTextDataObject() - data.SetText("projected:"+str(self.GetItemData(row))) + data.SetText("projected:" + str(self.GetItemData(row))) dropSource = wx.DropSource(self) dropSource.SetData(data) dropSource.DoDragDrop() def mergeDrones(self, x, y, itemID): - srcRow = self.FindItemData(-1,itemID) + srcRow = self.FindItemData(-1, itemID) dstRow, _ = self.HitTest((x, y)) if srcRow != -1 and dstRow != -1: self._merge(srcRow, dstRow) @@ -140,7 +144,6 @@ class ProjectedView(d.Display): if sFit.mergeDrones(fitID, self.get(src), dstDrone, True): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - def moduleSort(self, module): return module.item.name @@ -164,7 +167,7 @@ class ProjectedView(d.Display): self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -245,7 +248,8 @@ class ProjectedView(d.Display): menu = None if sel != -1: item = self.get(sel) - if item is None: return + if item is None: + return sMkt = Market.getInstance() if isinstance(item, es_Drone): srcContext = "projectedDrone" @@ -269,7 +273,7 @@ class ProjectedView(d.Display): else: fitSrcContext = "projectedFit" fitItemContext = item.name - context = ((fitSrcContext,fitItemContext),) + context = ((fitSrcContext, fitItemContext),) context = context + (("projected",),) menu = ContextMenu.getMenu((item,), *context) elif sel == -1: diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index 018edb579..dd257302d 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -1,3 +1,6 @@ +import csv +import logging + import wx try: @@ -8,25 +11,21 @@ except: else: raise -import gui.PFSearchBox as SBox -from gui.marketBrowser import SearchBox +import eos.db +from service.market import Market import gui.display as d import gui.globalEvents as GE +import gui.PFSearchBox as SBox +from gui.marketBrowser import SearchBox from gui.bitmapLoader import BitmapLoader -import csv -import eos.db - -import logging - -from service.market import Market logger = logging.getLogger(__name__) -class AttributeEditor( wx.Frame ): - def __init__( self, parent ): +class AttributeEditor(wx.Frame): + def __init__(self, parent): wx.Frame.__init__(self, parent, wx.ID_ANY, title="Attribute Editor", pos=wx.DefaultPosition, - size=wx.Size(650, 600), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.TAB_TRAVERSAL) + size=wx.Size(650, 600), style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.TAB_TRAVERSAL) i = wx.IconFromBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) self.SetIcon(i) @@ -46,7 +45,6 @@ class AttributeEditor( wx.Frame ): self.Bind(wx.EVT_MENU, self.OnExport, fileExport) self.Bind(wx.EVT_MENU, self.OnClear, fileClear) - i = wx.IconFromBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) self.SetIcon(i) @@ -68,10 +66,10 @@ class AttributeEditor( wx.Frame ): mainSizer.Add(leftPanel, 1, wx.ALL | wx.EXPAND, 5) rightSizer = wx.BoxSizer(wx.VERTICAL) - self.btnRemoveOverrides = wx.Button( panel, wx.ID_ANY, u"Remove Overides for Item", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.btnRemoveOverrides = wx.Button(panel, wx.ID_ANY, u"Remove Overides for Item", wx.DefaultPosition, wx.DefaultSize, 0) self.pg = AttributeGrid(panel) - rightSizer.Add(self.pg, 1, wx.ALL|wx.EXPAND, 5) - rightSizer.Add(self.btnRemoveOverrides, 0, wx.ALL | wx.EXPAND, 5 ) + rightSizer.Add(self.pg, 1, wx.ALL | wx.EXPAND, 5) + rightSizer.Add(self.btnRemoveOverrides, 0, wx.ALL | wx.EXPAND, 5) self.btnRemoveOverrides.Bind(wx.EVT_BUTTON, self.pg.removeOverrides) self.btnRemoveOverrides.Enable(False) @@ -95,8 +93,8 @@ class AttributeEditor( wx.Frame ): def OnImport(self, event): dlg = wx.FileDialog(self, "Import pyfa override file", - wildcard = "pyfa override file (*.csv)|*.csv", - style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) + wildcard="pyfa override file (*.csv)|*.csv", + style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) if (dlg.ShowModal() == wx.ID_OK): path = dlg.GetPath() with open(path, 'rb') as csvfile: @@ -114,8 +112,8 @@ class AttributeEditor( wx.Frame ): defaultFile = "pyfa_overrides.csv" dlg = wx.FileDialog(self, "Save Overrides As...", - wildcard = "pyfa overrides (*.csv)|*.csv", - style = wx.FD_SAVE, + wildcard="pyfa overrides (*.csv)|*.csv", + style=wx.FD_SAVE, defaultFile=defaultFile) if dlg.ShowModal() == wx.ID_OK: @@ -127,9 +125,12 @@ class AttributeEditor( wx.Frame ): writer.writerow([item.ID, override.attrID, override.value]) def OnClear(self, event): - dlg = wx.MessageDialog(self, - "Are you sure you want to delete all overrides?", - "Confirm Delete", wx.YES | wx.NO | wx.ICON_EXCLAMATION) + dlg = wx.MessageDialog( + self, + "Are you sure you want to delete all overrides?", + "Confirm Delete", + wx.YES | wx.NO | wx.ICON_EXCLAMATION + ) if dlg.ShowModal() == wx.ID_YES: sMkt = Market.getInstance() @@ -144,6 +145,7 @@ class AttributeEditor( wx.Frame ): self.itemView.updateItems(True) self.pg.Clear() + # This is literally a stripped down version of the market. class ItemView(d.Display): DEFAULT_COLS = ["Base Icon", @@ -199,9 +201,8 @@ class ItemView(d.Display): class AttributeGrid(wxpg.PropertyGrid): - def __init__(self, parent): - wxpg.PropertyGrid.__init__(self, parent, style=wxpg.PG_HIDE_MARGIN|wxpg.PG_HIDE_CATEGORIES|wxpg.PG_BOLD_MODIFIED|wxpg.PG_TOOLTIPS) + wxpg.PropertyGrid.__init__(self, parent, style=wxpg.PG_HIDE_MARGIN | wxpg.PG_HIDE_CATEGORIES | wxpg.PG_BOLD_MODIFIED | wxpg.PG_TOOLTIPS) self.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS) self.item = None @@ -210,9 +211,9 @@ class AttributeGrid(wxpg.PropertyGrid): self.btn = parent.Parent.btnRemoveOverrides - self.Bind( wxpg.EVT_PG_CHANGED, self.OnPropGridChange ) - self.Bind( wxpg.EVT_PG_SELECTED, self.OnPropGridSelect ) - self.Bind( wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick ) + self.Bind(wxpg.EVT_PG_CHANGED, self.OnPropGridChange) + self.Bind(wxpg.EVT_PG_SELECTED, self.OnPropGridSelect) + self.Bind(wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick) self.itemView.Bind(wx.EVT_LIST_ITEM_SELECTED, self.itemActivated) self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated) @@ -233,14 +234,14 @@ class AttributeGrid(wxpg.PropertyGrid): prop = wxpg.FloatProperty(key, value=default) prop.SetClientData(item.attributes[key]) # set this so that we may access it later - prop.SetHelpString("%s\n%s"%(item.attributes[key].displayName or key, "Default Value: %0.3f"%default)) + prop.SetHelpString("%s\n%s" % (item.attributes[key].displayName or key, "Default Value: %0.3f" % default)) self.Append(prop) def removeOverrides(self, event): if self.item is None: return - for _, x in self.item.overrides.items(): + for x in self.item.overrides.values(): self.item.deleteOverride(x.attr) self.itemView.updateItems(True) self.ClearModifiedStatus() diff --git a/gui/pyfatogglepanel.py b/gui/pyfatogglepanel.py index 765a07727..d34985115 100644 --- a/gui/pyfatogglepanel.py +++ b/gui/pyfatogglepanel.py @@ -1,65 +1,54 @@ -# -*- coding: utf-8 -*- - ########################################################################### -## pyfatogllepanel.py -## -## Author: Darriele - HomeWorld -## -## Project home: https://github.com/pyfa-org/Pyfa - pyfa project -## Some portions of code are based on -## AGW:pycollapsiblepane generic implementation of wx.CollapsiblePane -## AGW:pycollapsiblepane credits ( from the original source file used ): -## Andrea Gavana, @ 09 Aug 2007 -## Latest Revision: 12 Apr 2010, 12.00 GMT -## -## Module description: -## TogglePanel class is a wx.collipsablepane like implementation that uses -## some optimization from awg::pycollipsablepane to provide certain -## features tailored for PYFA needs. -## -## This module is part of PYFA (PYthon Fitting Assitant) and it shares the same -## licence ( read PYFA licence notice: gpl.txt ) -## -## Notes: leave the commented code as it is, those line will be removed someday +# pyfatogllepanel.py +# +# Author: Darriele - HomeWorld +# +# Project home: https://github.com/pyfa-org/Pyfa - pyfa project +# Some portions of code are based on +# AGW:pycollapsiblepane generic implementation of wx.CollapsiblePane +# AGW:pycollapsiblepane credits ( from the original source file used ): +# Andrea Gavana, @ 09 Aug 2007 +# Latest Revision: 12 Apr 2010, 12.00 GMT +# +# Module description: +# TogglePanel class is a wx.collipsablepane like implementation that uses +# some optimization from awg::pycollipsablepane to provide certain +# features tailored for PYFA needs. +# +# This module is part of PYFA (PYthon Fitting Assitant) and it shares the same +# licence ( read PYFA licence notice: gpl.txt ) +# +# Notes: leave the commented code as it is, those line will be removed someday ########################################################################### import wx from gui.bitmapLoader import BitmapLoader -########################################################################### -## Class TogglePanel -########################################################################### -class TogglePanel ( wx.Panel ): - - def __init__( self, parent , forceLayout = -1): - wx.Panel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.TAB_TRAVERSAL ) +class TogglePanel(wx.Panel): + def __init__(self, parent, forceLayout=-1): + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL) self._toggle = 1 self.parent = parent self.forceLayout = forceLayout self.bkColour = self.GetBackgroundColour() - # Create the main sizer of this panel - - self.mainSizer = wx.BoxSizer( wx.VERTICAL ) - self.SetSizer( self.mainSizer ) - parentSize = parent.GetMinSize() - - # Create the header panel + # Create the main sizer of this panel + self.mainSizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.mainSizer) + # parentSize = parent.GetMinSize() + # Create the header panel self.headerPanel = wx.Panel(self) + self.mainSizer.Add(self.headerPanel, 0, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.RIGHT, 1) - self.mainSizer.Add(self.headerPanel,0,wx.EXPAND | wx.TOP|wx.BOTTOM|wx.RIGHT, 1) + # Load expanded/collapsed bitmaps from the icons folder + self.bmpExpanded = BitmapLoader.getBitmap("down-arrow2", "gui") + self.bmpCollapsed = BitmapLoader.getBitmap("up-arrow2", "gui") - # Load expanded/collapsed bitmaps from the icons folder - - self.bmpExpanded = BitmapLoader.getBitmap("down-arrow2","gui") - self.bmpCollapsed = BitmapLoader.getBitmap("up-arrow2","gui") - - # Make the bitmaps have the same color as window text - - sysTextColour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOWTEXT ) + # Make the bitmaps have the same color as window text + sysTextColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) img = self.bmpExpanded.ConvertToImage() img.Replace(0, 0, 0, sysTextColour[0], sysTextColour[1], sysTextColour[2]) @@ -69,55 +58,53 @@ class TogglePanel ( wx.Panel ): img.Replace(0, 0, 0, sysTextColour[0], sysTextColour[1], sysTextColour[2]) self.bmpCollapsed = wx.BitmapFromImage(img) - self.headerBmp = wx.StaticBitmap(self.headerPanel ) - self.headerBmp.SetBitmap( self.bmpExpanded) + self.headerBmp = wx.StaticBitmap(self.headerPanel) + self.headerBmp.SetBitmap(self.bmpExpanded) - # Create the header sizer and add static bitmap and static text controls to it + # Create the header sizer and add static bitmap and static text controls to it - headerSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.headerPanel.SetSizer( headerSizer) + headerSizer = wx.BoxSizer(wx.HORIZONTAL) + self.headerPanel.SetSizer(headerSizer) - hbmpSizer = wx.BoxSizer( wx.HORIZONTAL ) - hlblSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.hcntSizer = wx.BoxSizer( wx.HORIZONTAL) + hbmpSizer = wx.BoxSizer(wx.HORIZONTAL) + hlblSizer = wx.BoxSizer(wx.HORIZONTAL) + self.hcntSizer = wx.BoxSizer(wx.HORIZONTAL) - hbmpSizer.Add( self.headerBmp, 0,0, 5 ) + hbmpSizer.Add(self.headerBmp, 0, 0, 5) - self.headerLabel = wx.StaticText( self.headerPanel, wx.ID_ANY, u"PYFA", wx.DefaultPosition, wx.DefaultSize, 0 ) - hlblSizer.Add( self.headerLabel, 0, wx.EXPAND , 5 ) + self.headerLabel = wx.StaticText(self.headerPanel, wx.ID_ANY, u"PYFA", wx.DefaultPosition, wx.DefaultSize, 0) + hlblSizer.Add(self.headerLabel, 0, wx.EXPAND, 5) - headerSizer.Add( hbmpSizer, 0, wx.RIGHT, 5 ) - headerSizer.Add( hlblSizer, 0, wx.RIGHT, 5 ) - headerSizer.Add( self.hcntSizer, 0, wx.RIGHT, 5) + headerSizer.Add(hbmpSizer, 0, wx.RIGHT, 5) + headerSizer.Add(hlblSizer, 0, wx.RIGHT, 5) + headerSizer.Add(self.hcntSizer, 0, wx.RIGHT, 5) - # Set the static text font weight to BOLD + # Set the static text font weight to BOLD - headerFont=parent.GetFont() + headerFont = parent.GetFont() headerFont.SetWeight(wx.BOLD) self.headerLabel.SetFont(headerFont) - # Create the content panel and its main sizer + # Create the content panel and its main sizer - self.contentSizer = wx.BoxSizer( wx.VERTICAL ) + self.contentSizer = wx.BoxSizer(wx.VERTICAL) self.contentPanel = wx.Panel(self) self.contentPanel.SetSizer(self.contentSizer) - self.mainSizer.Add( self.contentPanel, 0, wx.EXPAND | wx.RIGHT | wx.LEFT , 5) - + self.mainSizer.Add(self.contentPanel, 0, wx.EXPAND | wx.RIGHT | wx.LEFT, 5) self.Layout() - # Connect Events + # Connect Events + self.headerLabel.Bind(wx.EVT_LEFT_UP, self.toggleContent) + self.headerBmp.Bind(wx.EVT_LEFT_UP, self.toggleContent) + self.headerPanel.Bind(wx.EVT_LEFT_UP, self.toggleContent) - self.headerLabel.Bind( wx.EVT_LEFT_UP, self.toggleContent ) - self.headerBmp.Bind( wx.EVT_LEFT_UP, self.toggleContent ) - self.headerPanel.Bind( wx.EVT_LEFT_UP, self.toggleContent ) - - def __del__( self ): + def __del__(self): pass def AddToggleItem(self, hitem): - hitem.Bind( wx.EVT_LEFT_UP, self.toggleContent ) + hitem.Bind(wx.EVT_LEFT_UP, self.toggleContent) def GetHeaderContentSizer(self): return self.hcntSizer @@ -126,7 +113,7 @@ class TogglePanel ( wx.Panel ): return self.headerPanel def InsertItemInHeader(self, item): - self.hcntSizer.Add(item,0,0,0) + self.hcntSizer.Add(item, 0, 0, 0) self.Layout() def AddSizer(self, sizer): diff --git a/gui/pygauge.py b/gui/pygauge.py index b63f4fea8..0946e8727 100644 --- a/gui/pygauge.py +++ b/gui/pygauge.py @@ -13,7 +13,6 @@ It uses the easeOutQuad equation from caurina.transitions.Tweener to do the anim import wx import copy -import math from gui.utils import colorUtils import gui.utils.drawUtils as drawUtils @@ -22,6 +21,7 @@ import gui.utils.fonts as fonts from service import fit + class PyGauge(wx.PyWindow): """ This class provides a visual alternative for `wx.Gauge`. It currently @@ -29,7 +29,7 @@ class PyGauge(wx.PyWindow): """ def __init__(self, parent, id=wx.ID_ANY, range=100, pos=wx.DefaultPosition, - size=(-1,30), style=0): + size=(-1, 30), style=0): """ Default class constructor. @@ -46,8 +46,8 @@ class PyGauge(wx.PyWindow): self._size = size self._border_colour = wx.BLACK - self._barColour = self._barColourSorted = [wx.Colour(212,228,255)] - self._barGradient = self._barGradientSorted = None + self._barColour = self._barColourSorted = [wx.Colour(212, 228, 255)] + self._barGradient = self._barGradientSorted = None self._border_padding = 0 self._range = range @@ -67,10 +67,10 @@ class PyGauge(wx.PyWindow): self._animDirection = 0 self.animEffect = animEffects.OUT_QUAD - self.transitionsColors = [( wx.Colour(191, 191, 191, 255) , wx.Colour(96, 191, 0, 255) ), - ( wx.Colour(191, 167, 96, 255) , wx.Colour(255, 191, 0, 255) ), - ( wx.Colour(255, 191, 0, 255) , wx.Colour(255, 128, 0, 255) ), - ( wx.Colour(255, 128, 0, 255) , wx.Colour(255, 0, 0, 255) )] + self.transitionsColors = [(wx.Colour(191, 191, 191, 255), wx.Colour(96, 191, 0, 255)), + (wx.Colour(191, 167, 96, 255), wx.Colour(255, 191, 0, 255)), + (wx.Colour(255, 191, 0, 255), wx.Colour(255, 128, 0, 255)), + (wx.Colour(255, 128, 0, 255), wx.Colour(255, 0, 0, 255))] self.gradientEffect = -35 self._percentage = 0 @@ -79,8 +79,8 @@ class PyGauge(wx.PyWindow): self.font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) - self.SetBarGradient((wx.Colour(119,119,119),wx.Colour(153,153,153))) - self.SetBackgroundColour(wx.Colour(51,51,51)) + self.SetBarGradient((wx.Colour(119, 119, 119), wx.Colour(153, 153, 153))) + self.SetBackgroundColour(wx.Colour(51, 51, 51)) self._tooltip = wx.ToolTip("") self.SetToolTip(self._tooltip) self._tooltip.SetTip("0.00/100.00") @@ -107,7 +107,6 @@ class PyGauge(wx.PyWindow): return wx.Size(self._size[0], self._size[1]) - def GetBorderColour(self): return self._border_colour @@ -121,7 +120,7 @@ class PyGauge(wx.PyWindow): return self._barColour[0] def SetBarColour(self, colour): - if type(colour) != type([]): + if not isinstance(colour, list): self._barColour = [colour] else: self._barColour = list(colour) @@ -130,31 +129,30 @@ class PyGauge(wx.PyWindow): GetBarColor = GetBarColour def SetFractionDigits(self, digits): - self._fractionDigits=digits + self._fractionDigits = digits def GetBarGradient(self): """ Returns a tuple containing the gradient start and end colours. """ - if self._barGradient == None: + if self._barGradient is None: return None return self._barGradient[0] - def SetBarGradient(self, gradient = None): + def SetBarGradient(self, gradient=None): """ Sets the bar gradient. This overrides the BarColour. :param `gradient`: a tuple containing the gradient start and end colours. """ - if gradient == None: + if gradient is None: self._barGradient = None else: - if type(gradient) != type([]): + if not isinstance(gradient, list): self._barGradient = [gradient] else: self._barGradient = list(gradient) - def GetBorderPadding(self): """ Gets the border padding. """ @@ -169,7 +167,6 @@ class PyGauge(wx.PyWindow): self._border_padding = padding - def GetRange(self): """ Returns the maximum value of the gauge. """ @@ -186,7 +183,7 @@ class PyGauge(wx.PyWindow): self._animValue = self._percentage self.Refresh() - def SetRange(self, range, reinit = False): + def SetRange(self, range, reinit=False): """ Sets the range of the gauge. The gauge length is its value as a proportion of the range. @@ -197,16 +194,16 @@ class PyGauge(wx.PyWindow): if self._range == range: return - range = float(range) + range_ = float(range) - if range <= 0: + if range_ <= 0: self._range = 0.01 else: - self._range = range + self._range = range_ if reinit is False: self._oldPercentage = self._percentage - self._percentage = (self._value/self._range) * 100 + self._percentage = (self._value / self._range) * 100 else: self._oldPercentage = self._percentage self._percentage = 0 @@ -214,9 +211,7 @@ class PyGauge(wx.PyWindow): self.Animate() - - self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range >0.01 else 0)) - + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range > 0.01 else 0)) def GetValue(self): """ Returns the current position of the gauge. """ @@ -233,22 +228,22 @@ class PyGauge(wx.PyWindow): self._value = value if value < 0: self._value = 0 - self._percentage = (self._value/self._range) * 100 + self._percentage = (self._value / self._range) * 100 self.Animate() self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range)) - def SetValueRange(self, value, range, reinit = False): + def SetValueRange(self, value, range, reinit=False): if self._value == value and self._range == range: return - range = float(range) + range_ = float(range) - if range <= 0: + if range_ <= 0: self._range = 0.01 else: - self._range = range + self._range = range_ value = float(value) @@ -258,15 +253,14 @@ class PyGauge(wx.PyWindow): if reinit is False: self._oldPercentage = self._percentage - self._percentage = (self._value/self._range) * 100 + self._percentage = (self._value / self._range) * 100 else: self._oldPercentage = self._percentage self._percentage = 0 - self.Animate() - self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range >0.01 else 0)) + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range > 0.01 else 0)) def OnEraseBackground(self, event): """ @@ -308,7 +302,7 @@ class PyGauge(wx.PyWindow): dc.SetPen(wx.Pen(self.GetBorderColour())) dc.DrawRectangleRect(rect) pad = 1 + self.GetBorderPadding() - rect.Deflate(pad,pad) + rect.Deflate(pad, pad) if self.GetBarGradient(): @@ -326,48 +320,48 @@ class PyGauge(wx.PyWindow): # time on them if not needed. See GH issue #282 pv = value - xv=1 + xv = 1 transition = 0 if pv <= 100: - xv = pv/100 + xv = pv / 100 transition = 0 - elif pv <=101: - xv = pv -100 + elif pv <= 101: + xv = pv - 100 transition = 1 elif pv <= 103: - xv = (pv -101)/2 + xv = (pv - 101) / 2 transition = 2 elif pv <= 105: - xv = (pv -103)/2 + xv = (pv - 103) / 2 transition = 3 else: pv = 106 - xv = pv -100 + xv = pv - 100 transition = -1 if transition != -1: - colorS,colorE = self.transitionsColors[transition] + colorS, colorE = self.transitionsColors[transition] color = colorUtils.CalculateTransitionColor(colorS, colorE, xv) else: - color = wx.Colour(191,48,48) + color = wx.Colour(191, 48, 48) if self.gradientEffect > 0: - gcolor = colorUtils.BrightenColor(color, float(self.gradientEffect) / 100) - gMid = colorUtils.BrightenColor(color, float(self.gradientEffect/2) / 100) + gcolor = colorUtils.BrightenColor(color, float(self.gradientEffect) / 100) + gMid = colorUtils.BrightenColor(color, float(self.gradientEffect / 2) / 100) else: - gcolor = colorUtils.DarkenColor(color, float(-self.gradientEffect) / 100) - gMid = colorUtils.DarkenColor(color, float(-self.gradientEffect/2) / 100) + gcolor = colorUtils.DarkenColor(color, float(-self.gradientEffect) / 100) + gMid = colorUtils.DarkenColor(color, float(-self.gradientEffect / 2) / 100) gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) dc.DrawBitmap(gBmp, r.left, r.top) else: - colour=self.GetBarColour() + colour = self.GetBarColour() dc.SetBrush(wx.Brush(colour)) dc.SetPen(wx.Pen(colour)) if value > 100: @@ -381,14 +375,14 @@ class PyGauge(wx.PyWindow): dc.SetFont(self.font) r = copy.copy(rect) - r.left +=1 - r.top +=1 + r.left += 1 + r.top += 1 if self._range == 0.01 and self._value > 0: - formatStr = u'\u221e' - dc.SetTextForeground(wx.Colour(80,80,80)) + formatStr = u'\u221e' + dc.SetTextForeground(wx.Colour(80, 80, 80)) dc.DrawLabel(formatStr, r, wx.ALIGN_CENTER) - dc.SetTextForeground(wx.Colour(255,255,255)) + dc.SetTextForeground(wx.Colour(255, 255, 255)) dc.DrawLabel(formatStr, rect, wx.ALIGN_CENTER) else: if self.GetBarGradient() and self._showRemaining: @@ -404,20 +398,20 @@ class PyGauge(wx.PyWindow): else: formatStr = "{0:." + str(self._fractionDigits) + "f}%" - dc.SetTextForeground(wx.Colour(80,80,80)) + dc.SetTextForeground(wx.Colour(80, 80, 80)) dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) - dc.SetTextForeground(wx.Colour(255,255,255)) + dc.SetTextForeground(wx.Colour(255, 255, 255)) dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) - def OnTimer(self,event): + def OnTimer(self, event): """ Handles the ``wx.EVT_TIMER`` event for L{PyfaGauge}. :param `event`: a timer event """ - oldValue=self._oldPercentage - value=self._percentage + oldValue = self._oldPercentage + value = self._percentage start = 0 direction = 1 if oldValue < value else -1 @@ -436,13 +430,13 @@ class PyGauge(wx.PyWindow): stop_timer = True if direction == 1: - if (oldValue+step) < value: - self._animValue = oldValue+step + if (oldValue + step) < value: + self._animValue = oldValue + step else: stop_timer = True else: - if (oldValue-step) > value: - self._animValue = oldValue-step + if (oldValue - step) > value: + self._animValue = oldValue - step else: stop_timer = True @@ -451,4 +445,3 @@ class PyGauge(wx.PyWindow): self._timer.Stop() self.Refresh() - diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index f28d370b1..83fb4327d 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,14 +15,13 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx +from service.targetResists import TargetResists from gui.bitmapLoader import BitmapLoader from gui.utils.clipboard import toClipboard, fromClipboard -from service.targetResists import ImportError from gui.builtinViews.entityEditor import EntityEditor, BaseValidator -from service.targetResists import TargetResists class TargetResistsTextValidor(BaseValidator): @@ -44,7 +43,7 @@ class TargetResistsTextValidor(BaseValidator): raise ValueError("Target Resist Profile name already in use, please choose another.") return True - except ValueError, e: + except ValueError as e: wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -78,12 +77,13 @@ class TargetResistsEntityEditor(EntityEditor): sTR = TargetResists.getInstance() sTR.deletePattern(entity) + class ResistsEditorDlg(wx.Dialog): DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") def __init__(self, parent): - wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Target Resists Editor", size = wx.Size( 350,240 )) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Target Resists Editor", size=wx.Size(350, 240)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -104,24 +104,23 @@ class ResistsEditorDlg(wx.Dialog): resistEditSizer.SetFlexibleDirection(wx.BOTH) resistEditSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - width = -1 - defSize = wx.Size(50,-1) + defSize = wx.Size(50, -1) - for i, type in enumerate(self.DAMAGE_TYPES): - if i%2: + for i, type_ in enumerate(self.DAMAGE_TYPES): + if i % 2: style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT border = 25 else: style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT border = 5 - bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big"%type, "gui")) + bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big" % type_, "gui")) resistEditSizer.Add(bmp, 0, style, border) # set text edit - setattr(self, "%sEdit"%type, wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, defSize)) - editObj = getattr(self, "%sEdit"%type) + setattr(self, "%sEdit" % type_, wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, defSize)) + editObj = getattr(self, "%sEdit" % type_) resistEditSizer.Add(editObj, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) - resistEditSizer.Add(wx.StaticText( self, wx.ID_ANY, u"%", wx.DefaultPosition, wx.DefaultSize, 0 ), 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) + resistEditSizer.Add(wx.StaticText(self, wx.ID_ANY, u"%", wx.DefaultPosition, wx.DefaultSize, 0), 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated) # Color we use to reset invalid value color @@ -138,7 +137,7 @@ class ResistsEditorDlg(wx.Dialog): self.stNotice.Wrap(-1) perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) - footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) + footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) self.totSizer = wx.BoxSizer(wx.VERTICAL) @@ -147,8 +146,8 @@ class ResistsEditorDlg(wx.Dialog): mainSizer.Add(contentSizer, 1, wx.EXPAND, 0) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) self.SetSizer(mainSizer) @@ -160,13 +159,13 @@ class ResistsEditorDlg(wx.Dialog): bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - btn.SetMinSize( btn.GetSize() ) - btn.SetMaxSize( btn.GetSize() ) + btn.SetMinSize(btn.GetSize()) + btn.SetMaxSize(btn.GetSize()) btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction) ) + btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower()))) @@ -201,8 +200,8 @@ class ResistsEditorDlg(wx.Dialog): try: p = self.entityEditor.getActiveEntity() - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) + for type_ in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit" % type_) if editObj.GetValue() == "": # if we are blank, overwrite with 0 @@ -215,7 +214,7 @@ class ResistsEditorDlg(wx.Dialog): assert 0 <= value <= 100 # if everything checks out, set resist attribute - setattr(p, "%sAmount"%type, value/100) + setattr(p, "%sAmount" % type_, value / 100) editObj.SetForegroundColour(self.colorReset) self.stNotice.SetLabel("") @@ -250,13 +249,13 @@ class ResistsEditorDlg(wx.Dialog): # Set new values for field in self.DAMAGE_TYPES: edit = getattr(self, "%sEdit" % field) - amount = getattr(p, "%sAmount" % field)*100 + amount = getattr(p, "%sAmount" % field) * 100 edit.ChangeValue(str(amount)) self.block = False self.ValuesUpdated() - def __del__( self ): + def __del__(self): pass def importPatterns(self, event): @@ -268,9 +267,9 @@ class ResistsEditorDlg(wx.Dialog): try: sTR.importPatterns(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") - except service.targetResists.ImportError, e: + except ImportError as e: self.stNotice.SetLabel(str(e)) - except Exception, e: + except Exception: self.stNotice.SetLabel("Could not import from clipboard: unknown errors") finally: self.entityEditor.refreshEntityList() @@ -280,5 +279,5 @@ class ResistsEditorDlg(wx.Dialog): def exportPatterns(self, event): "Event fired when export to clipboard button is clicked" sTR = TargetResists.getInstance() - toClipboard( sTR.exportPatterns() ) + toClipboard(sTR.exportPatterns()) self.stNotice.SetLabel("Patterns exported to clipboard") diff --git a/gui/setEditor.py b/gui/setEditor.py index 5ddac5f4c..380c7986a 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2016 Ryan Holmes # # This file is part of pyfa. @@ -15,19 +15,19 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= +import logging import wx -from gui.bitmapLoader import BitmapLoader + +from service.implantSet import ImplantSets from gui.builtinViews.implantEditor import BaseImplantEditorView from gui.utils.clipboard import toClipboard, fromClipboard -from service.implantSet import ImportError -import logging from gui.builtinViews.entityEditor import EntityEditor, BaseValidator -from service.implantSet import ImplantSets logger = logging.getLogger(__name__) + class ImplantTextValidor(BaseValidator): def __init__(self): BaseValidator.__init__(self) @@ -47,7 +47,7 @@ class ImplantTextValidor(BaseValidator): raise ValueError("Imlplant Set name already in use, please choose another.") return True - except ValueError, e: + except ValueError as e: wx.MessageBox(u"{}".format(e), "Error") textCtrl.SetFocus() return False @@ -92,27 +92,27 @@ class ImplantSetEditor(BaseImplantEditorView): def getImplantsFromContext(self): sIS = ImplantSets.getInstance() - set = self.Parent.entityEditor.getActiveEntity() - if set: - return sIS.getImplants(set.ID) + set_ = self.Parent.entityEditor.getActiveEntity() + if set_: + return sIS.getImplants(set_.ID) return [] def addImplantToContext(self, item): sIS = ImplantSets.getInstance() - set = self.Parent.entityEditor.getActiveEntity() + set_ = self.Parent.entityEditor.getActiveEntity() - sIS.addImplant(set.ID, item.ID) + sIS.addImplant(set_.ID, item.ID) def removeImplantFromContext(self, implant): sIS = ImplantSets.getInstance() - set = self.Parent.entityEditor.getActiveEntity() + set_ = self.Parent.entityEditor.getActiveEntity() + + sIS.removeImplant(set_.ID, implant) - sIS.removeImplant(set.ID, implant) class ImplantSetEditorDlg(wx.Dialog): - def __init__(self, parent): - wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Implant Set Editor", size = wx.Size(640, 600)) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Implant Set Editor", size=wx.Size(640, 600)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -138,8 +138,8 @@ class ImplantSetEditorDlg(wx.Dialog): footerSizer.Add(self.stNotice, 1, wx.BOTTOM | wx.TOP | wx.LEFT, 5) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) importExport = (("Import", wx.ART_FILE_OPEN, "from"), @@ -149,13 +149,13 @@ class ImplantSetEditorDlg(wx.Dialog): bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - btn.SetMinSize( btn.GetSize() ) - btn.SetMaxSize( btn.GetSize() ) + btn.SetMinSize(btn.GetSize()) + btn.SetMaxSize(btn.GetSize()) btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction) ) + btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction)) footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5) @@ -183,7 +183,7 @@ class ImplantSetEditorDlg(wx.Dialog): def closeEvent(self, event): self.Destroy() - def __del__( self ): + def __del__(self): pass def importPatterns(self, event): @@ -196,9 +196,9 @@ class ImplantSetEditorDlg(wx.Dialog): sIS.importSets(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") self.showInput(False) - except ImportError, e: + except ImportError as e: self.stNotice.SetLabel(str(e)) - except Exception, e: + except Exception as e: logging.exception("Unhandled Exception") self.stNotice.SetLabel("Could not import from clipboard: unknown errors") finally: diff --git a/gui/sfBrowserItem.py b/gui/sfBrowserItem.py index 81294cba3..9b12fcf01 100644 --- a/gui/sfBrowserItem.py +++ b/gui/sfBrowserItem.py @@ -1,5 +1,4 @@ import wx -import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils SB_ITEM_NORMAL = 0 @@ -7,14 +6,14 @@ SB_ITEM_SELECTED = 1 SB_ITEM_HIGHLIGHTED = 2 SB_ITEM_DISABLED = 4 -BTN_NORMAL = 1 -BTN_PRESSED = 2 -BTN_HOVER = 4 +BTN_NORMAL = 1 +BTN_PRESSED = 2 +BTN_HOVER = 4 BTN_DISABLED = 8 class PFBaseButton(object): - def __init__(self, normalBitmap = wx.NullBitmap,label = "", callback = None, hoverBitmap = None, disabledBitmap = None, show = True): + def __init__(self, normalBitmap=wx.NullBitmap, label="", callback=None, hoverBitmap=None, disabledBitmap=None, show=True): self.normalBmp = normalBitmap self.dropShadowOpacity = 0.2 @@ -48,7 +47,7 @@ class PFBaseButton(object): if self.callback: self.callback() - def SetState(self, state = BTN_NORMAL): + def SetState(self, state=BTN_NORMAL): self.state = state def GetState(self): @@ -57,7 +56,7 @@ class PFBaseButton(object): def GetSize(self): w = self.normalBmp.GetWidth() h = self.normalBmp.GetHeight() - return (w,h) + return (w, h) def GetBitmap(self): return self.normalBmp @@ -70,22 +69,23 @@ class PFBaseButton(object): return self.label def GetHoverBitmap(self): - if self.hoverBmp == None: + if self.hoverBmp is None: return self.normalBmp return self.hoverBmp def GetDisabledBitmap(self): - if self.disabledBmp == None: + if self.disabledBmp is None: return self.normalBmp return self.disabledBmp def GetDropShadowBitmap(self): return self.dropShadowBmp + class PFToolbar(object): def __init__(self, parent): self.Parent = parent - self.buttons =[] + self.buttons = [] self.toolbarX = 0 self.toolbarY = 0 self.padding = 2 @@ -94,7 +94,7 @@ class PFToolbar(object): def SetPosition(self, pos): self.toolbarX, self.toolbarY = pos - def AddButton(self, btnBitmap, label = "", clickCallback = None, hoverBitmap = None, disabledBitmap = None, show = True): + def AddButton(self, btnBitmap, label="", clickCallback=None, hoverBitmap=None, disabledBitmap=None, show=True): btn = PFBaseButton(btnBitmap, label, clickCallback, hoverBitmap, disabledBitmap, show) self.buttons.append(btn) return btn @@ -115,7 +115,7 @@ class PFToolbar(object): continue state = button.GetState() - if self.HitTest( (bx, self.toolbarY), event.GetPosition(), button.GetSize()): + if self.HitTest((bx, self.toolbarY), event.GetPosition(), button.GetSize()): changeCursor = True if not state & BTN_HOVER: button.SetState(state | BTN_HOVER) @@ -135,7 +135,7 @@ class PFToolbar(object): return doRefresh def MouseClick(self, event): - mx,my = event.GetPosition() + mx, my = event.GetPosition() bx = self.toolbarX for button in self.buttons: if not button.IsVisible(): @@ -143,8 +143,8 @@ class PFToolbar(object): state = button.GetState() if state & BTN_PRESSED: - button.SetState(state ^ BTN_PRESSED ) - if self.HitTest( (bx, self.toolbarY), event.GetPosition(), button.GetSize()): + button.SetState(state ^ BTN_PRESSED) + if self.HitTest((bx, self.toolbarY), event.GetPosition(), button.GetSize()): return button else: return False @@ -158,7 +158,7 @@ class PFToolbar(object): state = button.GetState() - if self.HitTest( (bx, self.toolbarY), event.GetPosition(), button.GetSize()): + if self.HitTest((bx, self.toolbarY), event.GetPosition(), button.GetSize()): if event.LeftDown() or event.LeftDClick(): button.SetState(state | BTN_PRESSED) @@ -230,14 +230,14 @@ class PFToolbar(object): bmpWidth = bmp.GetWidth() - pdc.DrawBitmap(dropShadowBmp,bx + self.padding / 2, self.toolbarY + self.padding / 2) + pdc.DrawBitmap(dropShadowBmp, bx + self.padding / 2, self.toolbarY + self.padding / 2) pdc.DrawBitmap(bmp, tbx, by) bx += bmpWidth + self.padding class SFBrowserItem(wx.Window): - def __init__(self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = (0,16), style = 0): + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 16), style=0): wx.Window.__init__(self, parent, id, pos, size, style) self.highlighted = False @@ -248,7 +248,6 @@ class SFBrowserItem(wx.Window): self.toolbar = PFToolbar(self) - self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) @@ -256,8 +255,7 @@ class SFBrowserItem(wx.Window): if "wxMSW" in wx.PlatformInfo: self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown) - - self.Bind(wx.EVT_LEFT_DOWN,self.OnLeftDown) + self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) self.Bind(wx.EVT_MOTION, self.OnMotion) @@ -271,7 +269,7 @@ class SFBrowserItem(wx.Window): self.RenderBackground() - mdc.DrawBitmap(self.bkBitmap, 0,0) + mdc.DrawBitmap(self.bkBitmap, 0, 0) self.DrawItem(mdc) self.toolbar.Render(mdc) @@ -291,7 +289,7 @@ class SFBrowserItem(wx.Window): def MouseMove(self, event): pass - def SetDraggable(self, mode = True): + def SetDraggable(self, mode=True): self.canBeDragged = mode def OnLeftUp(self, event): @@ -302,14 +300,13 @@ class SFBrowserItem(wx.Window): mposx, mposy = wx.GetMousePosition() rect = self.GetRect() rect.top = rect.left = 0 - cx,cy = self.ScreenToClient((mposx,mposy)) - if not rect.Contains((cx,cy)): + cx, cy = self.ScreenToClient((mposx, mposy)) + if not rect.Contains((cx, cy)): self.SetHighlighted(False) self.toolbar.ClearState() self.Refresh() return - btn = self.toolbar.MouseClick(event) if btn is not None: @@ -323,7 +320,6 @@ class SFBrowserItem(wx.Window): self.MouseLeftUp(event) - def OnLeftDown(self, event): if not self.HasCapture(): self.CaptureMouse() @@ -360,10 +356,10 @@ class SFBrowserItem(wx.Window): def GetType(self): return -1 - def SetSelected(self, select = True): + def SetSelected(self, select=True): self.selected = select - def SetHighlighted(self, highlight = True): + def SetHighlighted(self, highlight=True): self.highlighted = highlight def GetState(self): @@ -373,7 +369,7 @@ class SFBrowserItem(wx.Window): elif self.selected: if self.highlighted: - state = SB_ITEM_SELECTED | SB_ITEM_HIGHLIGHTED + state = SB_ITEM_SELECTED | SB_ITEM_HIGHLIGHTED else: state = SB_ITEM_SELECTED else: @@ -396,7 +392,7 @@ class SFBrowserItem(wx.Window): mFactor = 0.45 eFactor = 0.30 - elif state == SB_ITEM_SELECTED | SB_ITEM_HIGHLIGHTED: + elif state == SB_ITEM_SELECTED | SB_ITEM_HIGHLIGHTED: eFactor = 0.3 elif state == SB_ITEM_SELECTED: eFactor = 0.15 @@ -405,7 +401,7 @@ class SFBrowserItem(wx.Window): if self.bkBitmap: if self.bkBitmap.eFactor == eFactor and self.bkBitmap.sFactor == sFactor and self.bkBitmap.mFactor == mFactor \ - and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight() : + and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight(): return else: del self.bkBitmap diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 95e84b86a..1a00089f9 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -1,27 +1,23 @@ -# -*- coding: utf-8 -*- -import wx import re -import copy -from gui.bitmapLoader import BitmapLoader -import gui.mainFrame -import gui.globalEvents as GE import time -from gui.PFListPane import PFListPane +import wx from wx.lib.buttons import GenBitmapButton +from service.fit import Fit +from service.market import Market +import gui.mainFrame +import gui.utils.fonts as fonts +import gui.globalEvents as GE +import gui.sfBrowserItem as SFItem import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.animUtils as animUtils import gui.utils.animEffects as animEffects - -import gui.sfBrowserItem as SFItem +from gui.PFListPane import PFListPane from gui.contextMenu import ContextMenu -import gui.utils.fonts as fonts +from gui.bitmapLoader import BitmapLoader -from service.fit import Fit -from service.market import Market -import gui.utils.fonts as fonts FitRenamed, EVT_FIT_RENAMED = wx.lib.newevent.NewEvent() FitSelected, EVT_FIT_SELECTED = wx.lib.newevent.NewEvent() @@ -35,21 +31,22 @@ Stage3Selected, EVT_SB_STAGE3_SEL = wx.lib.newevent.NewEvent() SearchSelected, EVT_SB_SEARCH_SEL = wx.lib.newevent.NewEvent() ImportSelected, EVT_SB_IMPORT_SEL = wx.lib.newevent.NewEvent() -class PFWidgetsContainer(PFListPane): - def __init__(self,parent): - PFListPane.__init__(self,parent) - self.anim = animUtils.LoadAnimation(self,label = "", size=(100,12)) +class PFWidgetsContainer(PFListPane): + def __init__(self, parent): + PFListPane.__init__(self, parent) + + self.anim = animUtils.LoadAnimation(self, label="", size=(100, 12)) self.anim.Stop() self.anim.Show(False) - def ShowLoading(self, mode = True): + def ShowLoading(self, mode=True): if mode: - aweight,aheight = self.anim.GetSize() - cweight,cheight = self.GetSize() - ax = (cweight - aweight)/2 - ay = (cheight - aheight)/2 - self.anim.SetPosition((ax,ay)) + aweight, aheight = self.anim.GetSize() + cweight, cheight = self.GetSize() + ax = (cweight - aweight) / 2 + ay = (cheight - aheight) / 2 + self.anim.SetPosition((ax, ay)) self.anim.Show() self.anim.Play() else: @@ -68,8 +65,8 @@ class PFWidgetsContainer(PFListPane): class RaceSelector(wx.Window): - def __init__ (self, parent, id = wx.ID_ANY, label = "", pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0, layout = wx.VERTICAL, animate = False): - wx.Window.__init__(self, parent, id, pos = pos, size = size, style = style) + def __init__(self, parent, id=wx.ID_ANY, label="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, layout=wx.VERTICAL, animate=False): + wx.Window.__init__(self, parent, id, pos=pos, size=size, style=style) self.animTimerID = wx.NewId() self.animTimer = wx.Timer(self, self.animTimerID) @@ -92,7 +89,6 @@ class RaceSelector(wx.Window): self.SetSize(wx.Size(-1, self.minHeight)) self.SetMinSize(wx.Size(-1, self.minHeight)) - self.checkTimerID = wx.NewId() self.checkTimer = wx.Timer(self, self.checkTimerID) self.checkPeriod = 250 @@ -103,20 +99,20 @@ class RaceSelector(wx.Window): self.hoveredItem = None if layout == wx.VERTICAL: - self.buttonsBarPos = (4,0) + self.buttonsBarPos = (4, 0) else: - self.buttonsBarPos = (0,4) + self.buttonsBarPos = (0, 4) self.buttonsPadding = 4 if layout == wx.VERTICAL: - self.bmpArrow = BitmapLoader.getBitmap("down-arrow2","gui") + self.bmpArrow = BitmapLoader.getBitmap("down-arrow2", "gui") else: - self.bmpArrow = BitmapLoader.getBitmap("up-arrow2","gui") + self.bmpArrow = BitmapLoader.getBitmap("up-arrow2", "gui") - # Make the bitmaps have the same color as window text + # Make the bitmaps have the same color as window text - sysTextColour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOWTEXT ) + sysTextColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) img = self.bmpArrow.ConvertToImage() if layout == wx.VERTICAL: @@ -141,9 +137,9 @@ class RaceSelector(wx.Window): self.Layout() def OnMouseMove(self, event): - mx,my = event.GetPosition() + mx, my = event.GetPosition() - location = self.HitTest(mx,my) + location = self.HitTest(mx, my) if location != self.hoveredItem: self.hoveredItem = location self.Refresh() @@ -152,7 +148,7 @@ class RaceSelector(wx.Window): else: self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) - def OnSizeUpdate(self,event): + def OnSizeUpdate(self, event): self.CalcButtonsBarPos() self.Refresh() @@ -176,9 +172,9 @@ class RaceSelector(wx.Window): def OnLeftUp(self, event): - mx,my = event.GetPosition() + mx, my = event.GetPosition() - toggle = self.HitTest(mx,my) + toggle = self.HitTest(mx, my) if toggle is not None: self.Refresh() @@ -189,11 +185,11 @@ class RaceSelector(wx.Window): if stage == 2: categoryID = self.shipBrowser.GetStageData(stage) - wx.PostEvent(self.shipBrowser,Stage2Selected(categoryID=categoryID, back = True)) + wx.PostEvent(self.shipBrowser, Stage2Selected(categoryID=categoryID, back=True)) event.Skip() - def HitTest(self, mx,my): - x,y = self.buttonsBarPos + def HitTest(self, mx, my): + x, y = self.buttonsBarPos padding = self.buttonsPadding for bmp in self.raceBmps: @@ -222,16 +218,15 @@ class RaceSelector(wx.Window): rect = self.GetRect() windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - bkColor = colorUtils.GetSuitableColor(windowColor, 0.1) + # bkColor = colorUtils.GetSuitableColor(windowColor, 0.1) sepColor = colorUtils.GetSuitableColor(windowColor, 0.2) mdc = wx.BufferedPaintDC(self) bkBitmap = drawUtils.RenderGradientBar(windowColor, rect.width, rect.height, 0.1, 0.1, 0.2, 2) - mdc.DrawBitmap(bkBitmap,0,0,True) + mdc.DrawBitmap(bkBitmap, 0, 0, True) - - x ,y = self.buttonsBarPos + x, y = self.buttonsBarPos if self.direction == 1: for raceBmp in self.raceBmps: @@ -250,27 +245,25 @@ class RaceSelector(wx.Window): if self.layout == wx.VERTICAL: mdc.DrawBitmap(dropShadow, rect.width - self.buttonsPadding - bmp.GetWidth() + 1, y + 1) mdc.DrawBitmap(bmp, rect.width - self.buttonsPadding - bmp.GetWidth(), y) - y+=raceBmp.GetHeight() + self.buttonsPadding - mdc.SetPen(wx.Pen(sepColor,1)) - mdc.DrawLine(rect.width - 1, 0, rect.width -1, rect.height) + y += raceBmp.GetHeight() + self.buttonsPadding + mdc.SetPen(wx.Pen(sepColor, 1)) + mdc.DrawLine(rect.width - 1, 0, rect.width - 1, rect.height) else: mdc.DrawBitmap(dropShadow, x + 1, self.buttonsPadding + 1) mdc.DrawBitmap(bmp, x, self.buttonsPadding) - x+=raceBmp.GetWidth() + self.buttonsPadding - mdc.SetPen(wx.Pen(sepColor,1)) + x += raceBmp.GetWidth() + self.buttonsPadding + mdc.SetPen(wx.Pen(sepColor, 1)) mdc.DrawLine(0, 0, rect.width, 0) if self.direction < 1: if self.layout == wx.VERTICAL: mdc.DrawBitmap(self.bmpArrow, -2, (rect.height - self.bmpArrow.GetHeight()) / 2) else: - mdc.SetPen(wx.Pen(sepColor,1)) + mdc.SetPen(wx.Pen(sepColor, 1)) mdc.DrawLine(0, 0, rect.width, 0) mdc.DrawBitmap(self.bmpArrow, (rect.width - self.bmpArrow.GetWidth()) / 2, -2) - - - def OnTimer(self,event): + def OnTimer(self, event): if event.GetId() == self.animTimerID: start = 0 if self.layout == wx.VERTICAL: @@ -298,7 +291,7 @@ class RaceSelector(wx.Window): self.animTimer.Start(self.animPeriod) def AdjustSize(self, delta): - self.SetMinSize(wx.Size(delta,-1) if self.layout == wx.VERTICAL else wx.Size(-1, delta)) + self.SetMinSize(wx.Size(delta, -1) if self.layout == wx.VERTICAL else wx.Size(-1, delta)) self.Parent.Layout() self.Refresh() @@ -327,19 +320,20 @@ class RaceSelector(wx.Window): event.Skip() + class NavigationPanel(SFItem.SFBrowserItem): - def __init__(self,parent, size = (-1, 24)): - SFItem.SFBrowserItem.__init__(self,parent,size = size) + def __init__(self, parent, size=(-1, 24)): + SFItem.SFBrowserItem.__init__(self, parent, size=size) - self.rewBmpH = BitmapLoader.getBitmap("frewind_small","gui") - self.forwBmp = BitmapLoader.getBitmap("fforward_small","gui") - self.searchBmpH = BitmapLoader.getBitmap("fsearch_small","gui") - self.newBmpH = BitmapLoader.getBitmap("fit_add_small","gui") - self.resetBmpH = BitmapLoader.getBitmap("freset_small","gui") - self.switchBmpH = BitmapLoader.getBitmap("fit_switch_view_mode_small","gui") + self.rewBmpH = BitmapLoader.getBitmap("frewind_small", "gui") + self.forwBmp = BitmapLoader.getBitmap("fforward_small", "gui") + self.searchBmpH = BitmapLoader.getBitmap("fsearch_small", "gui") + self.newBmpH = BitmapLoader.getBitmap("fit_add_small", "gui") + self.resetBmpH = BitmapLoader.getBitmap("freset_small", "gui") + self.switchBmpH = BitmapLoader.getBitmap("fit_switch_view_mode_small", "gui") - switchImg = BitmapLoader.getImage("fit_switch_view_mode_small","gui") - switchImg = switchImg.AdjustChannels(1,1,1,0.4) + switchImg = BitmapLoader.getImage("fit_switch_view_mode_small", "gui") + switchImg = switchImg.AdjustChannels(1, 1, 1, 0.4) self.switchBmpD = wx.BitmapFromImage(switchImg) self.resetBmp = self.AdjustChannels(self.resetBmpH) @@ -348,11 +342,11 @@ class NavigationPanel(SFItem.SFBrowserItem): self.switchBmp = self.AdjustChannels(self.switchBmpH) self.newBmp = self.AdjustChannels(self.newBmpH) - self.toolbar.AddButton(self.resetBmp, "Ship groups", clickCallback = self.OnHistoryReset, hoverBitmap = self.resetBmpH) - self.toolbar.AddButton(self.rewBmp, "Back", clickCallback = self.OnHistoryBack, hoverBitmap = self.rewBmpH) - self.btnNew = self.toolbar.AddButton(self.newBmp, "New fitting", clickCallback = self.OnNewFitting, hoverBitmap = self.newBmpH, show = False) - self.btnSwitch = self.toolbar.AddButton(self.switchBmpD, "Hide empty ship groups", clickCallback = self.ToggleEmptyGroupsView, hoverBitmap = self.switchBmpH, show = False) - self.toolbar.AddButton(self.searchBmp, "Search fittings", clickCallback = self.ToggleSearchBox, hoverBitmap = self.searchBmpH) + self.toolbar.AddButton(self.resetBmp, "Ship groups", clickCallback=self.OnHistoryReset, hoverBitmap=self.resetBmpH) + self.toolbar.AddButton(self.rewBmp, "Back", clickCallback=self.OnHistoryBack, hoverBitmap=self.rewBmpH) + self.btnNew = self.toolbar.AddButton(self.newBmp, "New fitting", clickCallback=self.OnNewFitting, hoverBitmap=self.newBmpH, show=False) + self.btnSwitch = self.toolbar.AddButton(self.switchBmpD, "Hide empty ship groups", clickCallback=self.ToggleEmptyGroupsView, hoverBitmap=self.switchBmpH, show=False) + self.toolbar.AddButton(self.searchBmp, "Search fittings", clickCallback=self.ToggleSearchBox, hoverBitmap=self.searchBmpH) self.padding = 4 self.lastSearch = "" @@ -360,8 +354,8 @@ class NavigationPanel(SFItem.SFBrowserItem): self.inSearch = False self.fontSmall = wx.Font(fonts.SMALL, wx.SWISS, wx.NORMAL, wx.NORMAL) - w,h = size - self.BrowserSearchBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1 ), wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) + w, h = size + self.BrowserSearchBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1), wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) self.BrowserSearchBox.Show(False) self.BrowserSearchBox.Bind(wx.EVT_TEXT_ENTER, self.OnBrowserSearchBoxEnter) @@ -381,8 +375,7 @@ class NavigationPanel(SFItem.SFBrowserItem): realsearch = search.replace("*", "") if len(realsearch) >= 3: self.lastSearch = search - wx.PostEvent(self.shipBrowser,SearchSelected(text=search, back = False)) - + wx.PostEvent(self.shipBrowser, SearchSelected(text=search, back=False)) def ToggleSearchBox(self): if self.BrowserSearchBox.IsShown(): @@ -420,10 +413,10 @@ class NavigationPanel(SFItem.SFBrowserItem): stage = self.shipBrowser.GetActiveStage() if stage == 1: - wx.PostEvent(self.shipBrowser,Stage1Selected()) + wx.PostEvent(self.shipBrowser, Stage1Selected()) elif stage == 2: categoryID = self.shipBrowser.GetStageData(stage) - wx.PostEvent(self.shipBrowser,Stage2Selected(categoryID=categoryID, back = True)) + wx.PostEvent(self.shipBrowser, Stage2Selected(categoryID=categoryID, back=True)) def ShowNewFitButton(self, show): self.btnNew.Show(show) @@ -439,19 +432,19 @@ class NavigationPanel(SFItem.SFBrowserItem): shipID = self.Parent.GetStageData(stage) shipName = self.Parent.GetStage3ShipName() sFit = Fit.getInstance() - fitID = sFit.newFit(shipID, "%s fit" %shipName) + fitID = sFit.newFit(shipID, "%s fit" % shipName) self.shipBrowser.fitIDMustEditName = fitID - wx.PostEvent(self.Parent,Stage3Selected(shipID=shipID)) + wx.PostEvent(self.Parent, Stage3Selected(shipID=shipID)) wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def OnHistoryReset(self): if self.shipBrowser.browseHist: self.shipBrowser.browseHist = [] - self.gotoStage(1,0) + self.gotoStage(1, 0) def OnHistoryBack(self): if len(self.shipBrowser.browseHist) > 0: - stage,data = self.shipBrowser.browseHist.pop() + stage, data = self.shipBrowser.browseHist.pop() self.gotoStage(stage, data) def AdjustChannels(self, bitmap): @@ -467,10 +460,10 @@ class NavigationPanel(SFItem.SFBrowserItem): mdc.SetFont(self.fontSmall) - wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) + wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) self.thoverx = self.toolbar.GetWidth() + self.padding - self.thovery = (rect.height - hlabel)/2 + self.thovery = (rect.height - hlabel) / 2 self.thoverw = wlabel self.browserBoxX = self.thoverx @@ -495,8 +488,8 @@ class NavigationPanel(SFItem.SFBrowserItem): self.toolbar.SetPosition((self.toolbarx, self.toolbary)) mdc.SetFont(self.fontSmall) mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) - mdc.SetPen(wx.Pen(sepColor,1)) - mdc.DrawLine(0,rect.height - 1, rect.width, rect.height - 1) + mdc.SetPen(wx.Pen(sepColor, 1)) + mdc.DrawLine(0, rect.height - 1, rect.width, rect.height - 1) def RenderBackground(self): rect = self.GetRect() @@ -505,7 +498,7 @@ class NavigationPanel(SFItem.SFBrowserItem): sFactor = 0.1 - shipGroupsFilter = getattr(self.shipBrowser,"filterShipsWithNoFits", None) + shipGroupsFilter = getattr(self.shipBrowser, "filterShipsWithNoFits", None) if shipGroupsFilter: sFactor = 0.15 mFactor = 0.25 @@ -516,7 +509,7 @@ class NavigationPanel(SFItem.SFBrowserItem): if self.bkBitmap: if self.bkBitmap.eFactor == eFactor and self.bkBitmap.sFactor == sFactor and self.bkBitmap.mFactor == mFactor \ - and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight() : + and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight(): return else: del self.bkBitmap @@ -527,7 +520,6 @@ class NavigationPanel(SFItem.SFBrowserItem): self.bkBitmap.eFactor = eFactor self.bkBitmap.mFactor = mFactor - def gotoStage(self, stage, data=None): if stage == 1: wx.PostEvent(self.Parent, Stage1Selected()) @@ -546,16 +538,16 @@ class NavigationPanel(SFItem.SFBrowserItem): class ShipBrowser(wx.Panel): def __init__(self, parent): - wx.Panel.__init__ (self, parent,style = 0) + wx.Panel.__init__(self, parent, style=0) self._lastWidth = 0 self._activeStage = 1 self._lastStage = 0 self.browseHist = [] - self.lastStage = (0,0) + self.lastStage = (0, 0) self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.categoryList=[] + self.categoryList = [] self.categoryFitCache = {} self._stage1Data = -1 @@ -581,16 +573,16 @@ class ShipBrowser(wx.Panel): layout = wx.HORIZONTAL self.navpanel = NavigationPanel(self) - mainSizer.Add(self.navpanel, 0 , wx.EXPAND) - self.raceselect = RaceSelector(self, layout = layout, animate = False) + mainSizer.Add(self.navpanel, 0, wx.EXPAND) + self.raceselect = RaceSelector(self, layout=layout, animate=False) container = wx.BoxSizer(wx.VERTICAL if layout == wx.HORIZONTAL else wx.HORIZONTAL) if layout == wx.HORIZONTAL: - container.Add(self.lpane,1,wx.EXPAND) - container.Add(self.raceselect,0,wx.EXPAND) + container.Add(self.lpane, 1, wx.EXPAND) + container.Add(self.raceselect, 0, wx.EXPAND) else: - container.Add(self.raceselect,0,wx.EXPAND) - container.Add(self.lpane,1,wx.EXPAND) + container.Add(self.raceselect, 0, wx.EXPAND) + container.Add(self.lpane, 1, wx.EXPAND) mainSizer.Add(container, 1, wx.EXPAND) self.SetSizer(mainSizer) @@ -667,7 +659,7 @@ class ShipBrowser(wx.Panel): self._lastStage = self._activeStage self._activeStage = 1 self.lastdata = 0 - self.browseHist = [(1,0)] + self.browseHist = [(1, 0)] self.navpanel.ShowNewFitButton(False) self.navpanel.ShowSwitchEmptyGroupsButton(False) @@ -731,7 +723,7 @@ class ShipBrowser(wx.Panel): if ship.race not in racesList: racesList.append(ship.race) - for race,state in self.racesFilter.iteritems(): + for race, state in self.racesFilter.iteritems(): if race in racesList: subRacesFilter[race] = self.racesFilter[race] @@ -744,18 +736,18 @@ class ShipBrowser(wx.Panel): for ship in ships: fits = sFit.countFitsWithShip(ship.ID) t_fits += fits - filter = subRacesFilter[ship.race] if ship.race else True + filter_ = subRacesFilter[ship.race] if ship.race else True if override: - filter = True + filter_ = True shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits - + if self.filterShipsWithNoFits: - if fits>0: - if filter: + if fits > 0: + if filter_: self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race)) else: - if filter: + if filter_: self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race)) self.raceselect.RebuildRaces(racesList) @@ -775,9 +767,8 @@ class ShipBrowser(wx.Panel): self.Layout() def stage2(self, event): - back = event.back - - #if not back: + # back = event.back + # if not back: # self.browseHist.append( (1,0) ) self._lastStage = self._activeStage @@ -789,7 +780,6 @@ class ShipBrowser(wx.Panel): self.lpane.RemoveAllChildren() - sMkt = Market.getInstance() sMkt.getShipListDelayed(categoryID, self.stage2Callback) @@ -831,9 +821,9 @@ class ShipBrowser(wx.Panel): fitList = sFit.getFitsWithShip(shipID) if len(fitList) == 0: - stage,data = self.browseHist.pop() + stage, data = self.browseHist.pop() self.lpane.Thaw() - self.navpanel.gotoStage(stage,data) + self.navpanel.gotoStage(stage, data) return self.categoryFitCache[categoryID] = True @@ -854,7 +844,7 @@ class ShipBrowser(wx.Panel): shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits for ID, name, booster, timestamp in fitList: - self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp),shipID)) + self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp), shipID)) self.lpane.RefreshList() self.lpane.Thaw() @@ -886,20 +876,20 @@ class ShipBrowser(wx.Panel): if query: ships = sMkt.searchShips(query) fitList = sFit.searchFits(query) - + for ship in ships: - shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits + shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, len(sFit.getFitsWithShip(ship.ID))), ship.race)) - + for ID, name, shipID, shipName, booster, timestamp in fitList: ship = sMkt.getItem(shipID) shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp), shipID)) - if len(ships) == 0 and len(fitList) == 0 : - self.lpane.AddWidget(PFStaticText(self.lpane, label = u"No matching results.")) - self.lpane.RefreshList(doFocus = False) + if len(ships) == 0 and len(fitList) == 0: + self.lpane.AddWidget(PFStaticText(self.lpane, label=u"No matching results.")) + self.lpane.RefreshList(doFocus=False) self.lpane.Thaw() self.raceselect.RebuildRaces(self.RACE_ORDER) @@ -935,13 +925,16 @@ class ShipBrowser(wx.Panel): self.lpane.AddWidget(FitItem( self.lpane, - fit.ID, ( + fit.ID, + ( fit.ship.item.name, shipTrait, fit.name, fit.booster, - fit.timestamp), - fit.ship.item.ID)) + fit.timestamp, + ), + fit.ship.item.ID, + )) self.lpane.RefreshList(doFocus=False) self.lpane.Thaw() @@ -951,20 +944,23 @@ class ShipBrowser(wx.Panel): self.raceselect.Show(False) self.Layout() + class PFStaticText(wx.Panel): def __init__(self, parent, label=wx.EmptyString): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size = parent.GetSize()) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=parent.GetSize()) self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) mainSizer = wx.BoxSizer(wx.VERTICAL) - text = wx.StaticText( self, wx.ID_ANY, label, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) - text.Wrap( -1 ) - mainSizer.Add( text, 1, wx.ALL, 10 ) + text = wx.StaticText(self, wx.ID_ANY, label, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE) + text.Wrap(-1) + mainSizer.Add(text, 1, wx.ALL, 10) self.SetSizer(mainSizer) self.Layout() + def GetType(self): return -1 + class PFGenBitmapButton(GenBitmapButton): def __init__(self, parent, id, bitmap, pos, size, style): GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style) @@ -976,14 +972,15 @@ class PFGenBitmapButton(GenBitmapButton): def GetBackgroundBrush(self, dc): return self.bgcolor + class CategoryItem(SFItem.SFBrowserItem): - def __init__(self,parent, categoryID, fittingInfo, size = (0,16)): - SFItem.SFBrowserItem.__init__(self,parent,size = size) + def __init__(self, parent, categoryID, fittingInfo, size=(0, 16)): + SFItem.SFBrowserItem.__init__(self, parent, size=size) if categoryID: - self.shipBmp = BitmapLoader.getBitmap("ship_small","gui") + self.shipBmp = BitmapLoader.getBitmap("ship_small", "gui") else: - self.shipBmp = wx.EmptyBitmap(16,16) + self.shipBmp = wx.EmptyBitmap(16, 16) self.dropShadowBitmap = drawUtils.CreateDropShadowBitmap(self.shipBmp, 0.2) @@ -1004,31 +1001,30 @@ class CategoryItem(SFItem.SFBrowserItem): self.Bind(wx.EVT_TIMER, self.OnTimer) - #======================================================================= + # ===================================================================== # Disabled - it will be added as an option to Preferences self.animCount = 0 # self.animTimer.Start(self.animPeriod) - #======================================================================= - + # ===================================================================== def OnTimer(self, event): step = self.OUT_QUAD(self.animStep, 0, 10, self.animDuration) self.animCount = 10 - step self.animStep += self.animPeriod - if self.animStep > self.animDuration or self.animCount < 0 : + if self.animStep > self.animDuration or self.animCount < 0: self.animCount = 0 self.animTimer.Stop() self.Refresh() - def OUT_QUAD (self, t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) + def OUT_QUAD(self, t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) - t/=d + t /= d - return -c *(t)*(t-2) + b + return -c * t * (t - 2) + b def GetType(self): return 1 @@ -1036,12 +1032,12 @@ class CategoryItem(SFItem.SFBrowserItem): def MouseLeftUp(self, event): categoryID = self.categoryID - wx.PostEvent(self.shipBrowser,Stage2Selected(categoryID=categoryID, back=False)) + wx.PostEvent(self.shipBrowser, Stage2Selected(categoryID=categoryID, back=False)) def UpdateElementsPos(self, mdc): rect = self.GetRect() self.shipBmpx = self.padding - self.shipBmpy = (rect.height-self.shipBmp.GetWidth())/2 + self.shipBmpy = (rect.height - self.shipBmp.GetWidth()) / 2 self.shipBmpx -= self.animCount @@ -1049,13 +1045,11 @@ class CategoryItem(SFItem.SFBrowserItem): categoryName, fittings = self.fittingInfo wtext, htext = mdc.GetTextExtent(categoryName) - self.catx = self.shipBmpx + self.shipBmp.GetWidth() + self.padding self.caty = (rect.height - htext) / 2 def DrawItem(self, mdc): - rect = self.GetRect() - + # rect = self.GetRect() self.UpdateElementsPos(mdc) windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) @@ -1063,7 +1057,7 @@ class CategoryItem(SFItem.SFBrowserItem): mdc.SetTextForeground(textColor) mdc.DrawBitmap(self.dropShadowBitmap, self.shipBmpx + 1, self.shipBmpy + 1) - mdc.DrawBitmap(self.shipBmp,self.shipBmpx,self.shipBmpy,0) + mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0) mdc.SetFont(self.fontBig) @@ -1071,7 +1065,7 @@ class CategoryItem(SFItem.SFBrowserItem): mdc.DrawText(categoryName, self.catx, self.caty) -#=============================================================================== +# ============================================================================= # Waiting for total #fits impl in eos/service # # mdc.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, False)) @@ -1090,14 +1084,14 @@ class CategoryItem(SFItem.SFBrowserItem): # else: # xtext, ytext = mdc.GetTextExtent(fformat) # ypos = (rect.height - ytext)/2 -#=============================================================================== +# ============================================================================= class ShipItem(SFItem.SFBrowserItem): - def __init__(self, parent, shipID=None, shipFittingInfo=("Test","TestTrait", 2), itemData=None, + def __init__(self, parent, shipID=None, shipFittingInfo=("Test", "TestTrait", 2), itemData=None, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 40), style=0): - SFItem.SFBrowserItem.__init__(self, parent, size = size) + SFItem.SFBrowserItem.__init__(self, parent, size=size) self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -1124,7 +1118,7 @@ class ShipItem(SFItem.SFBrowserItem): self.newBmp = BitmapLoader.getBitmap("fit_add_small", "gui") self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") - self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big","gui") + self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big", "gui") img = wx.ImageFromBitmap(self.shipEffBk) img = img.Mirror(False) @@ -1133,7 +1127,7 @@ class ShipItem(SFItem.SFBrowserItem): self.raceBmp = BitmapLoader.getBitmap("race_%s_small" % self.shipRace, "gui") if not self.raceBmp: - self.raceBmp = BitmapLoader.getBitmap("fit_delete_small","gui") + self.raceBmp = BitmapLoader.getBitmap("fit_delete_small", "gui") self.raceDropShadowBmp = drawUtils.CreateDropShadowBitmap(self.raceBmp, 0.2) @@ -1144,11 +1138,10 @@ class ShipItem(SFItem.SFBrowserItem): self.editWidth = 150 self.padding = 4 - self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s fit" % self.shipName, wx.DefaultPosition, (120,-1), wx.TE_PROCESS_ENTER) + self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s fit" % self.shipName, wx.DefaultPosition, (120, -1), wx.TE_PROCESS_ENTER) self.tcFitName.Show(False) - - self.newBtn = self.toolbar.AddButton(self.newBmp,"New", self.newBtnCB) + self.newBtn = self.toolbar.AddButton(self.newBmp, "New", self.newBtnCB) self.tcFitName.Bind(wx.EVT_TEXT_ENTER, self.createNewFit) self.tcFitName.Bind(wx.EVT_KILL_FOCUS, self.editLostFocus) @@ -1166,7 +1159,7 @@ class ShipItem(SFItem.SFBrowserItem): self.marketInstance = Market.getInstance() self.baseItem = self.marketInstance.getItem(self.shipID) - #=======================================================================\ + # ===================================================================== # DISABLED - it will be added as an option in PREFERENCES self.animCount = 0 @@ -1176,8 +1169,7 @@ class ShipItem(SFItem.SFBrowserItem): # self.animTimer.Start(self.animPeriod) # else: # self.animCount = 0 - #======================================================================= - + # ===================================================================== def OnShowPopup(self, event): pos = event.GetPosition() @@ -1191,20 +1183,20 @@ class ShipItem(SFItem.SFBrowserItem): step = self.OUT_QUAD(self.animStep, 0, 10, self.animDuration) self.animCount = 10 - step self.animStep += self.animPeriod - if self.animStep > self.animDuration or self.animCount < 0 : + if self.animStep > self.animDuration or self.animCount < 0: self.animCount = 0 self.animTimer.Stop() self.Refresh() - def OUT_QUAD (self, t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) + def OUT_QUAD(self, t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) - t/=d + t /= d - return -c *(t)*(t-2) + b + return -c * t * (t - 2) + b def GetType(self): return 2 @@ -1253,7 +1245,7 @@ class ShipItem(SFItem.SFBrowserItem): sFit = Fit.getInstance() fitID = sFit.newFit(self.shipID, self.tcFitName.GetValue()) - wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID, back=False)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID, back=False)) wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def UpdateElementsPos(self, mdc): @@ -1264,18 +1256,18 @@ class ShipItem(SFItem.SFBrowserItem): self.toolbarx = self.toolbarx + self.animCount - self.shipEffx = self.padding + (rect.height - self.shipEffBk.GetWidth())/2 - self.shipEffy = (rect.height - self.shipEffBk.GetHeight())/2 + self.shipEffx = self.padding + (rect.height - self.shipEffBk.GetWidth()) / 2 + self.shipEffy = (rect.height - self.shipEffBk.GetHeight()) / 2 self.shipEffx = self.shipEffx - self.animCount self.shipBmpx = self.padding + (rect.height - self.shipBmp.GetWidth()) / 2 self.shipBmpy = (rect.height - self.shipBmp.GetHeight()) / 2 - self.shipBmpx= self.shipBmpx - self.animCount + self.shipBmpx = self.shipBmpx - self.animCount self.raceBmpx = self.shipEffx + self.shipEffBk.GetWidth() + self.padding - self.raceBmpy = (rect.height - self.raceBmp.GetHeight())/2 + self.raceBmpy = (rect.height - self.raceBmp.GetHeight()) / 2 self.textStartx = self.raceBmpx + self.raceBmp.GetWidth() + self.padding @@ -1290,14 +1282,14 @@ class ShipItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontSmall) - wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) + wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) self.thoverx = self.toolbarx - self.padding - wlabel - self.thovery = (rect.height - hlabel)/2 + self.thovery = (rect.height - hlabel) / 2 self.thoverw = wlabel def DrawItem(self, mdc): - rect = self.GetRect() + # rect = self.GetRect() windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) textColor = colorUtils.GetSuitableColor(windowColor, 1) @@ -1318,20 +1310,19 @@ class ShipItem(SFItem.SFBrowserItem): mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0) mdc.DrawBitmap(self.raceDropShadowBmp, self.raceBmpx + 1, self.raceBmpy + 1) - mdc.DrawBitmap(self.raceBmp,self.raceBmpx, self.raceBmpy) + mdc.DrawBitmap(self.raceBmp, self.raceBmpx, self.raceBmpy) shipName, shipTrait, fittings = self.shipFittingInfo - if fittings <1: + if fittings < 1: fformat = "No fits" + elif fittings == 1: + fformat = "%d fit" else: - if fittings == 1: - fformat = "%d fit" - else: - fformat = "%d fits" + fformat = "%d fits" mdc.SetFont(self.fontNormal) - mdc.DrawText(fformat %fittings if fittings >0 else fformat, self.textStartx, self.fittingsy) + mdc.DrawText(fformat % fittings if fittings > 0 else fformat, self.textStartx, self.fittingsy) mdc.SetFont(self.fontSmall) mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) @@ -1349,51 +1340,49 @@ class ShipItem(SFItem.SFBrowserItem): fnEditSize = editCtl.GetSize() wSize = self.GetSize() fnEditPosX = end - fnEditPosY = (wSize.height - fnEditSize.height)/2 + fnEditPosY = (wSize.height - fnEditSize.height) / 2 if fnEditPosX < start: - editCtl.SetSize((self.editWidth + fnEditPosX - start,-1)) - editCtl.SetPosition((start,fnEditPosY)) + editCtl.SetSize((self.editWidth + fnEditPosX - start, -1)) + editCtl.SetPosition((start, fnEditPosY)) else: - editCtl.SetSize((self.editWidth,-1)) - editCtl.SetPosition((fnEditPosX,fnEditPosY)) + editCtl.SetSize((self.editWidth, -1)) + editCtl.SetPosition((fnEditPosX, fnEditPosY)) + class PFBitmapFrame(wx.Frame): - def __init__ (self, parent, pos, bitmap): - wx.Frame.__init__(self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = pos, size = wx.DefaultSize, style = - wx.NO_BORDER - | wx.FRAME_NO_TASKBAR - | wx.STAY_ON_TOP) + def __init__(self, parent, pos, bitmap): + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=pos, size=wx.DefaultSize, style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) img = bitmap.ConvertToImage() img = img.ConvertToGreyscale() bitmap = wx.BitmapFromImage(img) self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) - self.Bind(wx.EVT_PAINT,self.OnWindowPaint) + self.Bind(wx.EVT_PAINT, self.OnWindowPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnWindowEraseBk) self.Bind(wx.EVT_TIMER, self.OnTimer) - self.timer = wx.Timer(self,wx.ID_ANY) + self.timer = wx.Timer(self, wx.ID_ANY) self.direction = 1 self.transp = 0 - self.SetSize((bitmap.GetWidth(),bitmap.GetHeight())) + self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) self.SetTransparent(0) self.Refresh() def OnTimer(self, event): - self.transp += 20*self.direction + self.transp += 20 * self.direction if self.transp > 200: self.transp = 200 self.timer.Stop() if self.transp < 0: self.transp = 0 self.timer.Stop() - wx.Frame.Show(self,False) + wx.Frame.Show(self, False) self.Destroy() return self.SetTransparent(self.transp) - def Show(self, showWnd = True): + def Show(self, showWnd=True): if showWnd: wx.Frame.Show(self, showWnd) self.Parent.SetFocus() @@ -1403,33 +1392,33 @@ class PFBitmapFrame(wx.Frame): self.direction = -1 self.timer.Start(5) - def OnWindowEraseBk(self,event): + def OnWindowEraseBk(self, event): pass - def OnWindowPaint(self,event): + def OnWindowPaint(self, event): rect = self.GetRect() canvas = wx.EmptyBitmap(rect.width, rect.height) mdc = wx.BufferedPaintDC(self) mdc.SelectObject(canvas) mdc.DrawBitmap(self.bitmap, 0, 0) - mdc.SetPen( wx.Pen("#000000", width = 1 ) ) - mdc.SetBrush( wx.TRANSPARENT_BRUSH ) - mdc.DrawRectangle( 0,0,rect.width,rect.height) + mdc.SetPen(wx.Pen("#000000", width=1)) + mdc.SetBrush(wx.TRANSPARENT_BRUSH) + mdc.DrawRectangle(0, 0, rect.width, rect.height) class FitItem(SFItem.SFBrowserItem): - def __init__(self, parent, fitID=None, shipFittingInfo=("Test", "TestTrait", "cnc's avatar", 0, 0 ), shipID = None, itemData=None, + def __init__(self, parent, fitID=None, shipFittingInfo=("Test", "TestTrait", "cnc's avatar", 0, 0), shipID=None, itemData=None, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 40), style=0): - #=============================================================================== + # ===================================================================== # animCount should be 10 if we enable animation in Preferences - #=============================================================================== + # ===================================================================== self.animCount = 0 self.selectedDelta = 0 - SFItem.SFBrowserItem.__init__(self,parent,size = size) + SFItem.SFBrowserItem.__init__(self, parent, size=size) self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -1446,10 +1435,10 @@ class FitItem(SFItem.SFBrowserItem): self.deleted = False if shipID: - self.shipBmp = BitmapLoader.getBitmap(str(shipID),"renders") + self.shipBmp = BitmapLoader.getBitmap(str(shipID), "renders") if not self.shipBmp: - self.shipBmp = BitmapLoader.getBitmap("ship_no_image_big","gui") + self.shipBmp = BitmapLoader.getBitmap("ship_no_image_big", "gui") self.shipFittingInfo = shipFittingInfo self.shipName, self.shipTrait, self.fitName, self.fitBooster, self.timestamp = shipFittingInfo @@ -1457,16 +1446,15 @@ class FitItem(SFItem.SFBrowserItem): # see GH issue #62 # Disabling this due to change in gang boosts Nov 2016 - #if self.fitBooster is None: self.fitBooster = False + # if self.fitBooster is None: self.fitBooster = False self.fitBooster = False self.boosterBmp = BitmapLoader.getBitmap("fleet_fc_small", "gui") - self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") - self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") - self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small","gui") - self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") - - self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big","gui") + self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") + self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") + self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small", "gui") + self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") + self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big", "gui") img = wx.ImageFromBitmap(self.shipEffBk) img = img.Mirror(False) @@ -1475,8 +1463,8 @@ class FitItem(SFItem.SFBrowserItem): self.dragTLFBmp = None self.bkBitmap = None - if self.shipTrait != "": # show no tooltip if no trait available - self.SetToolTip(wx.ToolTip(u'{}\n{}\n{}'.format(self.shipName, u'─'*20, self.shipTrait))) + if self.shipTrait != "": # show no tooltip if no trait available + self.SetToolTip(wx.ToolTip(u'{}\n{}\n{}'.format(self.shipName, u'─' * 20, self.shipTrait))) self.padding = 4 self.editWidth = 150 @@ -1492,12 +1480,12 @@ class FitItem(SFItem.SFBrowserItem): self.SetDraggable() - self.boosterBtn = self.toolbar.AddButton(self.boosterBmp,"Booster", show=self.fitBooster) - self.toolbar.AddButton(self.copyBmp,"Copy", self.copyBtnCB) - self.renameBtn = self.toolbar.AddButton(self.renameBmp,"Rename", self.renameBtnCB) + self.boosterBtn = self.toolbar.AddButton(self.boosterBmp, "Booster", show=self.fitBooster) + self.toolbar.AddButton(self.copyBmp, "Copy", self.copyBtnCB) + self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.renameBtnCB) self.toolbar.AddButton(self.deleteBmp, "Delete", self.deleteBtnCB) - self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fitName, wx.DefaultPosition, (self.editWidth,-1), wx.TE_PROCESS_ENTER) + self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fitName, wx.DefaultPosition, (self.editWidth, -1), wx.TE_PROCESS_ENTER) if self.shipBrowser.fitIDMustEditName != self.fitID: self.tcFitName.Show(False) @@ -1522,18 +1510,18 @@ class FitItem(SFItem.SFBrowserItem): self.Bind(wx.EVT_TIMER, self.OnTimer) - #======================================================================= + # ===================================================================== # DISABLED - it will be added as an option in PREFERENCES # if self.shipBrowser.GetActiveStage() != 4 and self.shipBrowser.GetLastStage() !=3: # self.animTimer.Start(self.animPeriod) # else: # self.animCount = 0 - #======================================================================= + # ===================================================================== self.selTimerID = wx.NewId() - self.selTimer = wx.Timer(self,self.selTimerID) + self.selTimer = wx.Timer(self, self.selTimerID) self.selTimer.Start(100) self.Bind(wx.EVT_RIGHT_UP, self.OnContextMenu) @@ -1605,7 +1593,7 @@ class FitItem(SFItem.SFBrowserItem): self.Bind(wx.EVT_MENU, self.OnProjectToFit, projectedItem) commandItem = menu.Append(wx.ID_ANY, "Add Command Booster") - self.Bind(wx.EVT_MENU, self.OnAddCommandFit, commandItem ) + self.Bind(wx.EVT_MENU, self.OnAddCommandFit, commandItem) self.PopupMenu(menu, pos) @@ -1621,7 +1609,7 @@ class FitItem(SFItem.SFBrowserItem): interval = 5 if ctimestamp < self.timestamp + interval: delta = (ctimestamp - self.timestamp) / interval - self.selectedDelta = self.CalculateDelta(0x0,self.maxDelta,delta) + self.selectedDelta = self.CalculateDelta(0x0, self.maxDelta, delta) self.Refresh() else: self.selectedDelta = self.maxDelta @@ -1631,23 +1619,23 @@ class FitItem(SFItem.SFBrowserItem): step = self.OUT_QUAD(self.animStep, 0, 10, self.animDuration) self.animCount = 10 - step self.animStep += self.animPeriod - if self.animStep > self.animDuration or self.animCount < 0 : + if self.animStep > self.animDuration or self.animCount < 0: self.animCount = 0 self.animTimer.Stop() self.Refresh() def CalculateDelta(self, start, end, delta): - return start + (end-start)*delta + return start + (end - start) * delta - def OUT_QUAD (self, t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) + def OUT_QUAD(self, t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) - t/=d + t /= d - return -c *(t)*(t-2) + b + return -c * t * (t - 2) + b def editLostFocus(self, event): self.RestoreEditButton() @@ -1670,7 +1658,7 @@ class FitItem(SFItem.SFBrowserItem): sFit = Fit.getInstance() fitID = sFit.copyFit(self.fitID) self.shipBrowser.fitIDMustEditName = fitID - wx.PostEvent(self.shipBrowser,Stage3Selected(shipID=self.shipID)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID)) wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def renameBtnCB(self): @@ -1708,9 +1696,12 @@ class FitItem(SFItem.SFBrowserItem): if wx.GetMouseState().ShiftDown() or wx.GetMouseState().MiddleDown(): self.deleteFit() else: - dlg = wx.MessageDialog(self, - "Do you really want to delete this fit?", - "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + dlg = wx.MessageDialog( + self, + "Do you really want to delete this fit?", + "Confirm Delete", + wx.YES | wx.NO | wx.ICON_QUESTION + ) if dlg.ShowModal() == wx.ID_YES: self.deleteFit() @@ -1727,8 +1718,8 @@ class FitItem(SFItem.SFBrowserItem): sFit.deleteFit(self.fitID) if self.shipBrowser.GetActiveStage() == 5: - if fit in self.shipBrowser.lastdata: # remove fit from import cache - self.shipBrowser.lastdata.remove(fit) + if fit in self.shipBrowser.lastdata: # remove fit from import cache + self.shipBrowser.lastdata.remove(fit) wx.PostEvent(self.shipBrowser, ImportSelected(fits=self.shipBrowser.lastdata)) elif self.shipBrowser.GetActiveStage() == 4: wx.PostEvent(self.shipBrowser, SearchSelected(text=self.shipBrowser.navpanel.lastSearch, back=True)) @@ -1793,9 +1784,9 @@ class FitItem(SFItem.SFBrowserItem): wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fitID)) def RestoreEditButton(self): - self.tcFitName.Show(False) - self.renameBtn.SetBitmap(self.renameBmp) - self.Refresh() + self.tcFitName.Show(False) + self.renameBtn.SetBitmap(self.renameBmp) + self.Refresh() def UpdateElementsPos(self, mdc): rect = self.GetRect() @@ -1805,15 +1796,15 @@ class FitItem(SFItem.SFBrowserItem): self.toolbarx = self.toolbarx + self.animCount - self.shipEffx = self.padding + (rect.height - self.shipEffBk.GetWidth())/2 - self.shipEffy = (rect.height - self.shipEffBk.GetHeight())/2 + self.shipEffx = self.padding + (rect.height - self.shipEffBk.GetWidth()) / 2 + self.shipEffy = (rect.height - self.shipEffBk.GetHeight()) / 2 self.shipEffx = self.shipEffx - self.animCount self.shipBmpx = self.padding + (rect.height - self.shipBmp.GetWidth()) / 2 self.shipBmpy = (rect.height - self.shipBmp.GetHeight()) / 2 - self.shipBmpx= self.shipBmpx - self.animCount + self.shipBmpx = self.shipBmpx - self.animCount self.textStartx = self.shipEffx + self.shipEffBk.GetWidth() + self.padding @@ -1826,10 +1817,10 @@ class FitItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontSmall) - wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) + wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) self.thoverx = self.toolbarx - self.padding - wlabel - self.thovery = (rect.height - hlabel)/2 + self.thovery = (rect.height - hlabel) / 2 self.thoverw = wlabel def DrawItem(self, mdc): @@ -1858,7 +1849,7 @@ class FitItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontNormal) fitDate = time.localtime(self.timestamp) - fitLocalDate = "%d/%02d/%02d %02d:%02d" % ( fitDate[0], fitDate[1], fitDate[2], fitDate[3], fitDate[4]) + fitLocalDate = "%d/%02d/%02d %02d:%02d" % (fitDate[0], fitDate[1], fitDate[2], fitDate[3], fitDate[4]) pfdate = drawUtils.GetPartialText(mdc, fitLocalDate, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) mdc.DrawText(pfdate, self.textStartx, self.timestampy) @@ -1885,13 +1876,13 @@ class FitItem(SFItem.SFBrowserItem): fnEditSize = editCtl.GetSize() wSize = self.GetSize() fnEditPosX = end - fnEditPosY = (wSize.height - fnEditSize.height)/2 + fnEditPosY = (wSize.height - fnEditSize.height) / 2 if fnEditPosX < start: - editCtl.SetSize((self.editWidth + fnEditPosX - start,-1)) - editCtl.SetPosition((start,fnEditPosY)) + editCtl.SetSize((self.editWidth + fnEditPosX - start, -1)) + editCtl.SetPosition((start, fnEditPosY)) else: - editCtl.SetSize((self.editWidth,-1)) - editCtl.SetPosition((fnEditPosX,fnEditPosY)) + editCtl.SetSize((self.editWidth, -1)) + editCtl.SetPosition((fnEditPosX, fnEditPosY)) def GetState(self): activeFitID = self.mainFrame.getActiveFit() @@ -1902,7 +1893,7 @@ class FitItem(SFItem.SFBrowserItem): else: if activeFitID == self.fitID: if self.highlighted: - state = SFItem.SB_ITEM_SELECTED | SFItem.SB_ITEM_HIGHLIGHTED + state = SFItem.SB_ITEM_SELECTED | SFItem.SB_ITEM_HIGHLIGHTED else: state = SFItem.SB_ITEM_SELECTED else: @@ -1914,8 +1905,7 @@ class FitItem(SFItem.SFBrowserItem): windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - activeFitID = self.mainFrame.getActiveFit() - + # activeFitID = self.mainFrame.getActiveFit() state = self.GetState() sFactor = 0.2 @@ -1926,18 +1916,18 @@ class FitItem(SFItem.SFBrowserItem): mFactor = 0.45 eFactor = 0.30 - elif state == SFItem.SB_ITEM_SELECTED | SFItem.SB_ITEM_HIGHLIGHTED: + elif state == SFItem.SB_ITEM_SELECTED | SFItem.SB_ITEM_HIGHLIGHTED: eFactor = 0.3 mFactor = 0.4 elif state == SFItem.SB_ITEM_SELECTED: - eFactor = (self.maxDelta - self.selectedDelta)/100 + 0.25 + eFactor = (self.maxDelta - self.selectedDelta) / 100 + 0.25 else: sFactor = 0 if self.bkBitmap: if self.bkBitmap.eFactor == eFactor and self.bkBitmap.sFactor == sFactor and self.bkBitmap.mFactor == mFactor \ - and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight() : + and rect.width == self.bkBitmap.GetWidth() and rect.height == self.bkBitmap.GetHeight(): return else: del self.bkBitmap @@ -1947,4 +1937,3 @@ class FitItem(SFItem.SFBrowserItem): self.bkBitmap.sFactor = sFactor self.bkBitmap.eFactor = eFactor self.bkBitmap.mFactor = mFactor - diff --git a/gui/statsPane.py b/gui/statsPane.py index 0b0b27413..9645f06dd 100644 --- a/gui/statsPane.py +++ b/gui/statsPane.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,22 +15,24 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx -from gui.statsView import StatsView + from service.fit import Fit -from gui.pyfatogglepanel import TogglePanel -import gui.builtinStatsViews -from gui.contextMenu import ContextMenu -#import gui.builtinViews.fittingView as fv import gui.mainFrame +import gui.builtinStatsViews import gui.globalEvents as GE +# import gui.builtinViews.fittingView as fv +from gui.statsView import StatsView +from gui.contextMenu import ContextMenu +from gui.pyfatogglepanel import TogglePanel + class StatsPane(wx.Panel): - DEFAULT_VIEWS = ["resourcesViewFull", "resistancesViewFull" ,"rechargeViewFull", "firepowerViewFull", + DEFAULT_VIEWS = ["resourcesViewFull", "resistancesViewFull", "rechargeViewFull", "firepowerViewFull", "capacitorViewFull", "targetingmiscViewFull", - "priceViewFull",] + "priceViewFull"] def fitChanged(self, event): sFit = Fit.getInstance() @@ -44,7 +46,7 @@ class StatsPane(wx.Panel): # Use 25% smaller fonts if MAC or force font size to 8 for msw/linux - if "__WXMAC__" in wx.PlatformInfo : + if "__WXMAC__" in wx.PlatformInfo: self.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) else: standardFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) @@ -57,7 +59,7 @@ class StatsPane(wx.Panel): self.views = [] self.nameViewMap = {} maxviews = len(self.DEFAULT_VIEWS) - i=0 + i = 0 for viewName in self.DEFAULT_VIEWS: tp = TogglePanel(self) contentPanel = tp.GetContentPane() @@ -80,18 +82,18 @@ class StatsPane(wx.Panel): mainSizer.Add(tp, 0, wx.EXPAND | wx.LEFT, 3) if i < maxviews - 1: mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, style=wx.HORIZONTAL), 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 2) - i+=1 + i += 1 tp.OnStateChange(tp.GetBestSize()) - width,height = self.GetSize() - self.SetMinSize((width+9,-1)) - + width, height = self.GetSize() + self.SetMinSize((width + 9, -1)) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) def contextHandler(self, contentPanel): viewName = contentPanel.viewName + def handler(event): menu = ContextMenu.getMenu(None, (viewName,)) if menu is not None: diff --git a/gui/statsView.py b/gui/statsView.py index 33e2a74e9..2cd8db548 100644 --- a/gui/statsView.py +++ b/gui/statsView.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,10 +15,12 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= + class StatsView(object): views = {} + def __init__(self): pass @@ -39,4 +41,5 @@ class StatsView(object): def refreshPanel(self, fit): raise NotImplementedError() -from gui.builtinStatsViews import * \ No newline at end of file + +from gui.builtinStatsViews import * # noqa diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 21ccb0197..29381558e 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx from gui.bitmapLoader import BitmapLoader diff --git a/gui/utils/animEffects.py b/gui/utils/animEffects.py index 519fe60c4..67605919a 100644 --- a/gui/utils/animEffects.py +++ b/gui/utils/animEffects.py @@ -1,83 +1,98 @@ import math -def OUT_CIRC (t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - t = t/d -1 - return c * math.sqrt(1 - t*t) + b; + +def OUT_CIRC(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t = t / d - 1 + + return c * math.sqrt(1 - t * t) + b + def OUT_QUART(t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - t = t/d -1 - return -c * ((t)*t*t*t - 1) + b; + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t = t / d - 1 + + return -c * (t * t * t * t - 1) + b + def INOUT_CIRC(t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) + t = float(t) + b = float(b) + c = float(c) + d = float(d) t1 = t / (d / 2) - if ((t / (d/2)) < 1): - return -c/2 * (math.sqrt(1 - (t/(d/2))**2) - 1) + b - return c/2 * (math.sqrt(1 - (t1-2)**2) + 1) + b; - -def IN_CUBIC (t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - t = t/d - return c*t*t*t + b - -def OUT_QUAD (t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - - t/=d - - return -c *(t)*(t-2) + b - -def OUT_BOUNCE (t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - - t/=d - - if ((t) < (1/2.75)): - return c*(7.5625*t*t) + b + if (t / (d / 2)) < 1: + return -c / 2 * (math.sqrt(1 - (t / (d / 2)) ** 2) - 1) + b else: - if (t < (2/2.75)): - t-=(1.5/2.75) - return c*(7.5625*t*t + .75) + b - else: - if (t < (2.5/2.75)): - t-=(2.25/2.75) - return c*(7.5625*(t)*t + .9375) + b - else: - t-=(2.625/2.75) - return c*(7.5625*(t)*t + .984375) + b + return c / 2 * (math.sqrt(1 - (t1 - 2) ** 2) + 1) + b + + +def IN_CUBIC(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t = t / d + + return c * t * t * t + b + + +def OUT_QUAD(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t /= d + + return -c * t * (t - 2) + b + + +def OUT_BOUNCE(t, b, c, d): + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t /= d + + if t < (1 / 2.75): + return c * (7.5625 * t * t) + b + elif t < (2 / 2.75): + t -= (1.5 / 2.75) + return c * (7.5625 * t * t + .75) + b + elif t < (2.5 / 2.75): + t -= (2.25 / 2.75) + return c * (7.5625 * t * t + .9375) + b + else: + t -= (2.625 / 2.75) + return c * (7.5625 * t * t + .984375) + b + def INOUT_EXP(t, b, c, d): - t=float(t) - b=float(b) - c=float(c) - d=float(d) - t1 = t / (d/2) - if t==0: - return b - if t==d: - return b+c - if (t1) < 1: - return c/2 * math.pow(2, 10 * (t1 - 1)) + b - c * 0.0005 - return c/2 * 1.0005 * (-math.pow(2, -10 * (t1-1)) + 2) + b + t = float(t) + b = float(b) + c = float(c) + d = float(d) + + t1 = t / (d / 2) + + if t == 0: + return b + elif t == d: + return b + c + elif t1 < 1: + return c / 2 * math.pow(2, 10 * (t1 - 1)) + b - c * 0.0005 + else: + return c / 2 * 1.0005 * (-math.pow(2, -10 * (t1 - 1)) + 2) + b diff --git a/gui/utils/animUtils.py b/gui/utils/animUtils.py index 9ee81b03a..cefd3938b 100644 --- a/gui/utils/animUtils.py +++ b/gui/utils/animUtils.py @@ -1,9 +1,10 @@ import wx import gui.utils.colorUtils as colorUtils + class LoadAnimation(wx.Window): - def __init__ (self, parent, id = wx.ID_ANY, label = "", pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0): - wx.Window.__init__(self, parent, id, pos = pos, size = size, style = style) + def __init__(self, parent, id=wx.ID_ANY, label="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): + wx.Window.__init__(self, parent, id, pos=pos, size=size, style=style) self.label = label @@ -16,7 +17,6 @@ class LoadAnimation(wx.Window): self.bars = 10 self.padding = 2 - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_TIMER, self.OnTimer) self.Bind(wx.EVT_PAINT, self.OnPaint) @@ -64,14 +64,14 @@ class LoadAnimation(wx.Window): x = self.padding - for bar in xrange(self.bars): + for bar in range(self.bars): if bar != self.animCount: dc.SetPen(wx.Pen(shadeColor)) dc.SetBrush(wx.Brush(shadeColor)) bh = barHeight y = self.padding else: - barColor = colorUtils.GetSuitableColor(barColor,float(self.animCount/2)/10) + barColor = colorUtils.GetSuitableColor(barColor, float(self.animCount / 2) / 10) dc.SetPen(wx.Pen(barColor)) dc.SetBrush(wx.Brush(barColor)) bh = rect.height @@ -82,17 +82,17 @@ class LoadAnimation(wx.Window): textColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) dc.SetTextForeground(textColor) - dc.DrawLabel(self.label,rect,wx.ALIGN_CENTER) + dc.DrawLabel(self.label, rect, wx.ALIGN_CENTER) + class WaitDialog(wx.Dialog): - def __init__(self, parent, title = "Processing"): - wx.Dialog.__init__ (self, parent, id=wx.ID_ANY, title = title, size=(300,30), + def __init__(self, parent, title="Processing"): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=title, size=(300, 30), style=wx.NO_BORDER) - mainSizer = wx.BoxSizer( wx.HORIZONTAL ) + mainSizer = wx.BoxSizer(wx.HORIZONTAL) - self.progress = LoadAnimation(self,label = title, size=(300,30)) - mainSizer.Add( self.progress, 1, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 0 ) - self.SetSizer( mainSizer ) + self.progress = LoadAnimation(self, label=title, size=(300, 30)) + mainSizer.Add(self.progress, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 0) + self.SetSizer(mainSizer) self.Layout() self.CenterOnParent() - diff --git a/gui/utils/clipboard.py b/gui/utils/clipboard.py index 17427c6e3..4fa05e1f3 100644 --- a/gui/utils/clipboard.py +++ b/gui/utils/clipboard.py @@ -1,5 +1,6 @@ import wx + def toClipboard(text): clip = wx.TheClipboard clip.Open() @@ -7,6 +8,7 @@ def toClipboard(text): clip.SetData(data) clip.Close() + def fromClipboard(): clip = wx.TheClipboard clip.Open() @@ -16,4 +18,4 @@ def fromClipboard(): return data.GetText() else: clip.Close() - return None \ No newline at end of file + return None diff --git a/gui/utils/colorUtils.py b/gui/utils/colorUtils.py index ccf42ec18..a8d560159 100644 --- a/gui/utils/colorUtils.py +++ b/gui/utils/colorUtils.py @@ -1,25 +1,26 @@ import wx import math -#Brightens a color (wx.Colour), factor = [0,1] def BrightenColor(color, factor): + # Brightens a color (wx.Colour), factor = [0,1] - r,g,b = color + r, g, b = color a = color.Alpha() factor = min(max(factor, 0), 1) - r+=(255-r)*factor - b+=(255-b)*factor - g+=(255-g)*factor + r += (255 - r) * factor + b += (255 - b) * factor + g += (255 - g) * factor - return wx.Colour(r,g,b,a) + return wx.Colour(r, g, b, a) -#Darkens a color (wx.Colour), factor = [0, 1] def DarkenColor(color, factor): - bkR ,bkG , bkB = color + # Darkens a color (wx.Colour), factor = [0, 1] + + bkR, bkG, bkB = color alpha = color.Alpha() @@ -30,50 +31,50 @@ def DarkenColor(color, factor): g = float(bkG * factor) b = float(bkB * factor) - r = min(max(r,0),255) - b = min(max(b,0),255) - g = min(max(g,0),255) + r = min(max(r, 0), 255) + b = min(max(b, 0), 255) + g = min(max(g, 0), 255) return wx.Colour(r, g, b, alpha) -#Calculates the brightness of a color, different options - def GetBrightnessO1(color): - r,g,b = color - return (0.299*r + 0.587*g + 0.114*b) + # Calculates the brightness of a color, different options + + r, g, b = color + return (0.299 * r + 0.587 * g + 0.114 * b) + def GetBrightnessO2(color): - r,g,b = color - return math.sqrt( 0.241 * r * r + 0.691 * g * g + 0.068 * b * b ) + r, g, b = color + return math.sqrt(0.241 * r * r + 0.691 * g * g + 0.068 * b * b) - -#Calculates a suitable color based on original color (wx.Colour), its brightness, a factor=[0,1] (darken/brighten by factor depending on calculated brightness) - def GetSuitableColor(color, factor): + # Calculates a suitable color based on original color (wx.Colour), its brightness, a factor=[0,1] (darken/brighten by factor depending on calculated brightness) brightness = GetBrightnessO1(color) - if brightness >129: + if brightness > 129: return DarkenColor(color, factor) else: return BrightenColor(color, factor) - -#Calculates the color between a given start and end colors, delta = [0,1] -#Colors are wx.Colour objects - def CalculateTransitionColor(startColor, endColor, delta): - sR,sG,sB = startColor - eR,eG,eB = endColor + """ + Calculates the color between a given start and end colors, delta = [0,1] + Colors are wx.Colour objects + """ + + sR, sG, sB = startColor + eR, eG, eB = endColor alphaS = startColor.Alpha() alphaE = endColor.Alpha() - tR = sR + (eR - sR) * delta - tG = sG + (eG - sG) * delta - tB = sB + (eB - sB) * delta + tR = sR + (eR - sR) * delta + tG = sG + (eG - sG) * delta + tB = sB + (eB - sB) * delta - return wx.Colour(tR, tG, tB, (alphaS + alphaE)/2) + return wx.Colour(tR, tG, tB, (alphaS + alphaE) / 2) diff --git a/gui/utils/compat.py b/gui/utils/compat.py index d8177973b..bf49eb4aa 100644 --- a/gui/utils/compat.py +++ b/gui/utils/compat.py @@ -237,7 +237,7 @@ class OrderedDict(dict): ''' if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() + return len(self) == len(other) and self.items() == other.items() return dict.__eq__(self, other) def __ne__(self, other): diff --git a/gui/utils/drawUtils.py b/gui/utils/drawUtils.py index 91e60eb98..91af8444e 100644 --- a/gui/utils/drawUtils.py +++ b/gui/utils/drawUtils.py @@ -2,17 +2,17 @@ import wx import gui.utils.colorUtils as colorUtils -def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor = None , fillRatio = 2): +def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor=None, fillRatio=2): - if sFactor == 0 and eFactor == 0 and mFactor == None: - return DrawFilledBitmap(width,height, windowColor) + if sFactor == 0 and eFactor == 0 and mFactor is None: + return DrawFilledBitmap(width, height, windowColor) gStart = colorUtils.GetSuitableColor(windowColor, sFactor) if mFactor: gMid = colorUtils.GetSuitableColor(windowColor, mFactor) else: - gMid = colorUtils.GetSuitableColor(windowColor, sFactor + (eFactor - sFactor) / 2) + gMid = colorUtils.GetSuitableColor(windowColor, sFactor + (eFactor - sFactor) / 2) gEnd = colorUtils.GetSuitableColor(windowColor, eFactor) @@ -20,7 +20,7 @@ def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor = No def DrawFilledBitmap(width, height, color): - canvas = wx.EmptyBitmap(width,height) + canvas = wx.EmptyBitmap(width, height) mdc = wx.MemoryDC() mdc.SelectObject(canvas) @@ -32,10 +32,11 @@ def DrawFilledBitmap(width, height, color): return canvas -def DrawGradientBar(width, height, gStart, gEnd, gMid = None, fillRatio = 4): + +def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4): # we need to have dimensions to draw - #assert width > 0 and height > 0 - canvas = wx.EmptyBitmap(width,height) + # assert width > 0 and height > 0 + canvas = wx.EmptyBitmap(width, height) mdc = wx.MemoryDC() mdc.SelectObject(canvas) @@ -48,7 +49,7 @@ def DrawGradientBar(width, height, gStart, gEnd, gMid = None, fillRatio = 4): mdc.GradientFillLinear(r, gStart, gMid, wx.SOUTH) r.top = r.height - r.height = height * (fillRatio - 1)/fillRatio + (1 if height % fillRatio != 0 else 0) + r.height = height * (fillRatio - 1) / fillRatio + (1 if height % fillRatio != 0 else 0) mdc.GradientFillLinear(r, gMid, gEnd, wx.SOUTH) @@ -57,48 +58,51 @@ def DrawGradientBar(width, height, gStart, gEnd, gMid = None, fillRatio = 4): return canvas -def GetPartialText(dc, text , maxWidth, defEllipsis = "..."): - ellipsis = defEllipsis +def GetPartialText(dc, text, maxWidth, defEllipsis="..."): + ellipsis = defEllipsis + base_w, h = dc.GetTextExtent(ellipsis) + + lenText = len(text) + drawntext = text + w, dummy = dc.GetTextExtent(text) + + while lenText > 0: + + if w + base_w <= maxWidth: + break + + w_c, h_c = dc.GetTextExtent(drawntext[-1]) + drawntext = drawntext[0:-1] + lenText -= 1 + w -= w_c + + while len(ellipsis) > 0 and w + base_w > maxWidth: + ellipsis = ellipsis[0:-1] base_w, h = dc.GetTextExtent(ellipsis) + if len(text) > lenText: + return drawntext + ellipsis + else: + return text - lenText = len(text) - drawntext = text - w, dummy = dc.GetTextExtent(text) - while lenText > 0: - - if w + base_w <= maxWidth: - break - - w_c, h_c = dc.GetTextExtent(drawntext[-1]) - drawntext = drawntext[0:-1] - lenText -= 1 - w -= w_c - - while len(ellipsis) > 0 and w + base_w > maxWidth: - ellipsis = ellipsis[0:-1] - base_w, h = dc.GetTextExtent(ellipsis) - if len(text) > lenText: - return drawntext + ellipsis - else: - return text - -def GetRoundBitmap( w, h, r ): - maskColor = wx.Color(0,0,0) - shownColor = wx.Color(5,5,5) - b = wx.EmptyBitmap(w,h) +def GetRoundBitmap(w, h, r): + maskColor = wx.Color(0, 0, 0) + shownColor = wx.Color(5, 5, 5) + b = wx.EmptyBitmap(w, h) dc = wx.MemoryDC(b) dc.SetBrush(wx.Brush(maskColor)) - dc.DrawRectangle(0,0,w,h) + dc.DrawRectangle(0, 0, w, h) dc.SetBrush(wx.Brush(shownColor)) dc.SetPen(wx.Pen(shownColor)) - dc.DrawRoundedRectangle(0,0,w,h,r) + dc.DrawRoundedRectangle(0, 0, w, h, r) dc.SelectObject(wx.NullBitmap) b.SetMaskColour(maskColor) return b -def GetRoundShape( w, h, r ): - return wx.RegionFromBitmap( GetRoundBitmap(w,h,r) ) + +def GetRoundShape(w, h, r): + return wx.RegionFromBitmap(GetRoundBitmap(w, h, r)) + def CreateDropShadowBitmap(bitmap, opacity): img = wx.ImageFromBitmap(bitmap) diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index 432dae740..ed9e8bc2b 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -5,8 +5,10 @@ from service.settings import HTMLExportSettings from service.fit import Fit from service.market import Market + class exportHtml(): _instance = None + @classmethod def getInstance(cls): if cls._instance is None: @@ -25,6 +27,7 @@ class exportHtml(): self.thread = exportHtmlThread(callback) self.thread.start() + class exportHtmlThread(threading.Thread): def __init__(self, callback=False): @@ -45,35 +48,30 @@ class exportHtmlThread(threading.Thread): sFit = Fit.getInstance() settings = HTMLExportSettings.getInstance() - timestamp = time.localtime(time.time()) - localDate = "%d/%02d/%02d %02d:%02d" % (timestamp[0], timestamp[1], timestamp[2], timestamp[3], timestamp[4]) - - minimal = settings.getMinimalEnabled(); + minimal = settings.getMinimalEnabled() dnaUrl = "https://o.smium.org/loadout/dna/" if minimal: - HTML = self.generateMinimalHTML(sMkt,sFit, dnaUrl) + HTML = self.generateMinimalHTML(sMkt, sFit, dnaUrl) else: - HTML = self.generateFullHTML(sMkt,sFit, dnaUrl) + HTML = self.generateFullHTML(sMkt, sFit, dnaUrl) try: FILE = open(settings.getPath(), "w") FILE.write(HTML.encode('utf-8')) FILE.close() except IOError: - print "Failed to write to " + settings.getPath() + print("Failed to write to " + settings.getPath()) pass if self.callback: wx.CallAfter(self.callback, -1) - - - def generateFullHTML(self,sMkt,sFit,dnaUrl): + def generateFullHTML(self, sMkt, sFit, dnaUrl): """ Generate the complete HTML with styling and javascript """ timestamp = time.localtime(time.time()) localDate = "%d/%02d/%02d %02d:%02d" % (timestamp[0], timestamp[1], timestamp[2], timestamp[3], timestamp[4]) - + HTML = """ @@ -152,7 +150,7 @@ class exportHtmlThread(threading.Thread): $('a[data-dna]').each(function( index ) { var dna = $(this).data('dna'); if (typeof CCPEVE !== 'undefined') { // inside IGB - $(this).attr('href', 'javascript:CCPEVE.showFitting("'+dna+'");');} + $(this).attr('href', 'javascript:CCPEVE.showFitting("'+dna+'");');} else { // outside IGB $(this).attr('href', '%s'+dna); } }); @@ -194,8 +192,7 @@ class exportHtmlThread(threading.Thread): fit = fits[0] try: dnaFit = sFit.exportDna(fit[0]) - HTMLgroup += ( - '
  • ' + ship.name + ": " + fit[1] + '
  • \n') + HTMLgroup += '
  • ' + ship.name + ": " + fit[1] + '
  • \n' except: pass finally: @@ -205,9 +202,10 @@ class exportHtmlThread(threading.Thread): else: # Ship group header HTMLship = ( - '
  • \n' - '

    ' + ship.name + ' '+str(len(fits))+'

    \n' - '
      \n') + '
    • \n' + '

      ' + ship.name + ' ' + str(len(fits)) + '

      \n' + '
        \n' + ) for fit in fits: if self.stopRunning: @@ -227,12 +225,12 @@ class exportHtmlThread(threading.Thread): if groupFits > 0: # Market group header HTML += ( - '
      • \n' - '

        ' + group.groupName + ' '+str(groupFits)+'

        \n' - '
          \n' - + HTMLgroup + - '
        \n' - '
      • ') + '
      • \n' + '

        ' + group.groupName + ' ' + str(groupFits) + '

        \n' + '
          \n' + HTMLgroup + + '
        \n' + '
      • ' + ) HTML += """
      @@ -243,7 +241,7 @@ class exportHtmlThread(threading.Thread): return HTML - def generateMinimalHTML(self,sMkt,sFit,dnaUrl): + def generateMinimalHTML(self, sMkt, sFit, dnaUrl): """ Generate a minimal HTML version of the fittings, without any javascript or styling""" categoryList = list(sMkt.getShipRoot()) categoryList.sort(key=lambda ship: ship.name) @@ -252,7 +250,6 @@ class exportHtmlThread(threading.Thread): HTML = '' for group in categoryList: # init market group string to give ships something to attach to - ships = list(sMkt.getShipList(group.ID)) ships.sort(key=lambda ship: ship.name) @@ -265,8 +262,8 @@ class exportHtmlThread(threading.Thread): if self.stopRunning: return try: - dnaFit = sFit.exportDna(fit[0]) - HTML += ''+ship.name +': '+ fit[1]+ '
      \n' + dnaFit = sFit.exportDna(fit[0]) + HTML += '' + ship.name + ': ' + fit[1] + '
      \n' except: continue finally: diff --git a/gui/utils/fonts.py b/gui/utils/fonts.py index eb2dd95f5..0ee52fb7f 100644 --- a/gui/utils/fonts.py +++ b/gui/utils/fonts.py @@ -5,9 +5,11 @@ different wxPython versions import wx + if 'wxMac' in wx.PlatformInfo: sizes = (10, 11, 12) else: sizes = (7, 8, 9) + SMALL, NORMAL, BIG = sizes diff --git a/gui/utils/numberFormatter.py b/gui/utils/numberFormatter.py index 8b64d6147..bb1ca210a 100644 --- a/gui/utils/numberFormatter.py +++ b/gui/utils/numberFormatter.py @@ -1,5 +1,6 @@ import math + def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=False): """ Add suffix to value, transform value to match new suffix and round it. @@ -34,7 +35,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal for key in posOrders: # Find first suitable suffix and check if it's not above highest order if abs(val) >= 10**key and key <= highest: - mantissa, suffix = val/float(10**key), posSuffixMap[key] + mantissa, suffix = val / float(10 ** key), posSuffixMap[key] # Do additional step to eliminate results like 999999 => 1000k # If we're already using our greatest order, we can't do anything useful if posOrders.index(key) == 0: @@ -53,7 +54,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal # or equal to multiplier if roundToPrec(mantissa, prec) >= orderDiff: # Divide mantissa and use suffix of greater order - mantissa, suffix = mantissa/orderDiff, posSuffixMap[prevKey] + mantissa, suffix = mantissa / orderDiff, posSuffixMap[prevKey] # Otherwise consider current results as acceptable break # Take numbers between 0 and 1, and matching/below highest possible negative suffix @@ -67,7 +68,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal nextKey = 0 # Check if mantissa with next suffix is in range [1, 1000) if abs(val) < 10**(nextKey) and key >= lowest: - mantissa, suffix = val/float(10**key), negSuffixMap[key] + mantissa, suffix = val / float(10**key), negSuffixMap[key] # Do additional step to eliminate results like 0.9999 => 1000m # Check if the key we're potentially switching to is greater than our # upper boundary @@ -81,7 +82,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal if roundToPrec(mantissa, prec) >= orderDiff: # Divide mantissa and use suffix of greater order # Use special handling of zero key as it's not on the map - mantissa, suffix = mantissa/orderDiff, posSuffixMap[nextKey] if nextKey != 0 else "" + mantissa, suffix = mantissa / orderDiff, posSuffixMap[nextKey] if nextKey != 0 else "" # Otherwise consider current results as acceptable break # Round mantissa according to our prec variable @@ -91,6 +92,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal result = u"{0}{1}{2}".format(sign, mantissa, suffix) return result + def roundToPrec(val, prec): # We're not rounding integers anyway # Also make sure that we do not ask to calculate logarithm of zero diff --git a/gui/viewColumn.py b/gui/viewColumn.py index c2ffff612..9afca36e7 100644 --- a/gui/viewColumn.py +++ b/gui/viewColumn.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,10 +15,11 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import wx + class ViewColumn(object): ''' Abstract class that columns can inherit from. @@ -26,6 +27,7 @@ class ViewColumn(object): they can be used as columns in a view. ''' columns = {} + def __init__(self, fittingView): self.fittingView = fittingView self.columnText = "" @@ -62,4 +64,5 @@ class ViewColumn(object): def delayedText(self, display, colItem): raise NotImplementedError() -from gui.builtinViewColumns import * + +from gui.builtinViewColumns import * # noqa diff --git a/service/attribute.py b/service/attribute.py index 535bb23a2..12b9b264b 100644 --- a/service/attribute.py +++ b/service/attribute.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import eos.db diff --git a/service/character.py b/service/character.py index f99b79692..db7bba12a 100644 --- a/service/character.py +++ b/service/character.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import copy import itertools @@ -37,6 +37,7 @@ from eos.saveddata.implant import Implant as es_Implant from eos.saveddata.character import Character as es_Character from eos.saveddata.module import Slot as es_Slot, Module as es_Module from eos.saveddata.fighter import Fighter as es_Fighter +from service.fit import Fit as es_Fit logger = logging.getLogger(__name__) @@ -54,7 +55,7 @@ class CharacterImportThread(threading.Thread): # we try to parse api XML data first with open(path, mode='r') as charFile: sheet = ParseXML(charFile) - char = sCharacter.new(sheet.name+" (imported)") + char = sCharacter.new(sheet.name + " (imported)") sCharacter.apiUpdateCharSheet(char.ID, sheet.skills) except: # if it's not api XML data, try this @@ -72,7 +73,7 @@ class CharacterImportThread(threading.Thread): "typeID": int(skill.getAttribute("typeID")), "level": int(skill.getAttribute("level")), }) - char = sCharacter.new(name+" (EVEMon)") + char = sCharacter.new(name + " (EVEMon)") sCharacter.apiUpdateCharSheet(char.ID, skills) except Exception, e: print e.message @@ -91,6 +92,8 @@ class SkillBackupThread(threading.Thread): def run(self): path = self.path sCharacter = Character.getInstance() + sFit = es_Fit.getInstance() + fit = sFit.getFit(self.activeFit) backupData = "" if self.saveFmt == "xml" or self.saveFmt == "emp": backupData = sCharacter.exportXml() @@ -123,7 +126,7 @@ class Character(object): self.all5() def exportText(self): - data = "Pyfa exported plan for \""+self.skillReqsDict['charname']+"\"\n" + data = "Pyfa exported plan for \"" + self.skillReqsDict['charname'] + "\"\n" data += "=" * 79 + "\n" data += "\n" item = "" @@ -139,7 +142,7 @@ class Character(object): def exportXml(self): root = ElementTree.Element("plan") - root.attrib["name"] = "Pyfa exported plan for "+self.skillReqsDict['charname'] + root.attrib["name"] = "Pyfa exported plan for " + self.skillReqsDict['charname'] root.attrib["revision"] = config.evemonMinVersion sorts = ElementTree.SubElement(root, "sorting") @@ -150,7 +153,7 @@ class Character(object): skillsSeen = set() for s in self.skillReqsDict['skills']: - skillKey = str(s["skillID"])+"::"+s["skill"]+"::"+str(int(s["level"])) + skillKey = str(s["skillID"]) + "::" + s["skill"] + "::" + str(int(s["level"])) if skillKey in skillsSeen: pass # Duplicate skills confuse EVEMon else: @@ -164,7 +167,7 @@ class Character(object): notes = ElementTree.SubElement(entry, "notes") notes.text = entry.attrib["skill"] - tree = ElementTree.ElementTree(root) + # tree = ElementTree.ElementTree(root) data = ElementTree.tostring(root, 'utf-8') prettydata = minidom.parseString(data).toprettyxml(indent=" ") @@ -362,7 +365,7 @@ class Character(object): return char.implants def checkRequirements(self, fit): - toCheck = [] + # toCheck = [] reqs = {} for thing in itertools.chain(fit.modules, fit.drones, fit.fighters, (fit.ship,)): if isinstance(thing, es_Module) and thing.slot == es_Slot.RIG: diff --git a/service/conversions/releaseApril2016.py b/service/conversions/releaseApril2016.py index a20914179..bd14944ee 100644 --- a/service/conversions/releaseApril2016.py +++ b/service/conversions/releaseApril2016.py @@ -42,4 +42,4 @@ CONVERSIONS = { "Unit W-634's Modified Drone Control Unit": "Unit W-634's Modified Fighter Support Unit", "Heavy Shadow Serpentis Stasis Grappler": "Shadow Serpentis Heavy Stasis Grappler", "Heavy Domination Stasis Grappler": "Domination Heavy Stasis Grappler", -} \ No newline at end of file +} diff --git a/service/conversions/releaseDecember15.py b/service/conversions/releaseDecember15.py index adcd9e552..456dedb59 100644 --- a/service/conversions/releaseDecember15.py +++ b/service/conversions/releaseDecember15.py @@ -96,4 +96,4 @@ CONVERSIONS = { "Micro S95a Remote Shield Booster": "'Micro' Remote Shield Booster", "Large 'Atonement' Remote Shield Booster": "Large Murky Compact Remote Shield Booster", "E50 Prototype Energy Vampire": "Medium Knave Scoped Energy Nosferatu", -} \ No newline at end of file +} diff --git a/service/conversions/releaseFeb2016.py b/service/conversions/releaseFeb2016.py index 4314ef173..9b2e8d838 100644 --- a/service/conversions/releaseFeb2016.py +++ b/service/conversions/releaseFeb2016.py @@ -7,4 +7,3 @@ CONVERSIONS = { "Capital Coaxial Remote Armor Repairer Blueprint": "CONCORD Capital Remote Armor Repairer Blueprint", "Capital Murky Remote Shield Booster Blueprint": "CONCORD Capital Remote Shield Booster Blueprint", } - diff --git a/service/conversions/releaseJan2016.py b/service/conversions/releaseJan2016.py index e5f9beba3..eb0763d6b 100644 --- a/service/conversions/releaseJan2016.py +++ b/service/conversions/releaseJan2016.py @@ -10,4 +10,4 @@ CONVERSIONS = { "'Distributor' Guidance Disruptor I Blueprint": "'Distributor' Guidance Disruptor Blueprint", "Highstroke Scoped Guidance Disruptor I": "Highstroke Scoped Guidance Disruptor", "A-211 Enduring Guidance Disruptor I": "A-211 Enduring Guidance Disruptor", -} \ No newline at end of file +} diff --git a/service/conversions/releaseMar2016.py b/service/conversions/releaseMar2016.py index 2af04e1af..da7591da5 100644 --- a/service/conversions/releaseMar2016.py +++ b/service/conversions/releaseMar2016.py @@ -358,4 +358,4 @@ CONVERSIONS = { "Wavelength Signal Enhancer I": "F-89 Compact Signal Amplifier", "Type-D Attenuation Signal Augmentation": "F-89 Compact Signal Amplifier", "Indirect Scanning Dampening Unit I": "Phased Muon Scoped Sensor Dampener", -} \ No newline at end of file +} diff --git a/service/damagePattern.py b/service/damagePattern.py index 2d1132d5a..ac5fc8b44 100644 --- a/service/damagePattern.py +++ b/service/damagePattern.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import copy @@ -24,6 +24,8 @@ from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern class ImportError(Exception): pass + + class DamagePattern(): instance = None @classmethod @@ -79,7 +81,7 @@ class DamagePattern(): if lenImports == 0: raise ImportError("No patterns found for import") if lenImports != num: - raise ImportError("%d patterns imported from clipboard; %d had errors"%(num, num-lenImports)) + raise ImportError("%d patterns imported from clipboard; %d had errors" % (num, num - lenImports)) def exportPatterns(self): patterns = self.getDamagePatternList() diff --git a/service/eveapi.py b/service/eveapi.py index 66c31f625..0b02f2159 100644 --- a/service/eveapi.py +++ b/service/eveapi.py @@ -1,4 +1,4 @@ -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # eveapi - EVE Online API access # # Copyright (c)2007-2014 Jamie "Entity" van den Berge @@ -24,7 +24,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE # -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # # Version: 1.3.0 - 27 May 2014 # - Added set_user_agent() module-level function to set the User-Agent header @@ -145,10 +145,10 @@ # Requirements: # Python 2.4+ # -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # This eveapi has been modified for pyfa. # # Specifically, the entire network request/response has been substituted for @@ -156,7 +156,7 @@ # # Additionally, various other parts have been changed to support urllib2 # responses instead of httplib -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- import urlparse @@ -174,7 +174,8 @@ proxySSL = False _default_useragent = "eveapi.py/1.3" _useragent = None # use set_user_agent() to set this. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + def set_cast_func(func): """Sets an alternative value casting function for the XML parser. @@ -185,25 +186,30 @@ def set_cast_func(func): global _castfunc _castfunc = _autocast if func is None else func + def set_user_agent(user_agent_string): """Sets a User-Agent for any requests sent by the library.""" global _useragent _useragent = user_agent_string -class Error(StandardError): +class Error(Exception): def __init__(self, code, message): self.code = code self.args = (message.rstrip("."),) + def __unicode__(self): return u'%s [code=%s]' % (self.args[0], self.code) + class RequestError(Error): pass + class AuthenticationError(Error): pass + class ServerError(Error): pass @@ -303,19 +309,16 @@ def _ParseXML(response, fromContext, storeFunc): return result - - - - -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # API Classes -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + _listtypes = (list, tuple, dict) _unspecified = [] -class _Context(object): +class _Context(object): def __init__(self, root, path, parentDict, newKeywords=None): self._root = root or self self._path = path @@ -356,20 +359,18 @@ class _Context(object): class _AuthContext(_Context): - def character(self, characterID): # returns a copy of this connection object but for every call made # through it, it will add the folder "/char" to the url, and the # characterID to the parameters passed. - return _Context(self._root, self._path + "/char", self.parameters, {"characterID":characterID}) + return _Context(self._root, self._path + "/char", self.parameters, {"characterID": characterID}) def corporation(self, characterID): # same as character except for the folder "/corp" - return _Context(self._root, self._path + "/corp", self.parameters, {"characterID":characterID}) + return _Context(self._root, self._path + "/corp", self.parameters, {"characterID": characterID}) class _RootContext(_Context): - def auth(self, **kw): if len(kw) == 2 and (("keyID" in kw and "vCode" in kw) or ("userID" in kw and "apiKey" in kw)): return _AuthContext(self._root, self._path, self.parameters, kw) @@ -397,7 +398,7 @@ class _RootContext(_Context): if response is None: network = Network.getInstance() - req = self._scheme+'://'+self._host+path + req = self._scheme + '://' + self._host + path response = network.request(req, network.EVE, kw) @@ -423,9 +424,11 @@ class _RootContext(_Context): # implementor is not handling fallbacks... return _ParseXML(response, True, store and (lambda obj: cache.store(self._host, path, kw, response, obj))) -#----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- # XML Parser -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + def _autocast(key, value): # attempts to cast an XML string to the most probable type. @@ -452,11 +455,11 @@ def _autocast(key, value): # couldn't cast. return string unchanged. return value + _castfunc = _autocast class _Parser(object): - def Parse(self, data, isStream=False): self.container = self.root = None self._cdata = False @@ -475,7 +478,6 @@ class _Parser(object): p.Parse(data, True) return self.root - def tag_cdatasection_enter(self): # encountered an explicit CDATA tag. self._cdata = True @@ -501,21 +503,20 @@ class _Parser(object): if name == "rowset": # for rowsets, use the given name try: - columns = attributes[attributes.index('columns')+1].replace(" ", "").split(",") + columns = attributes[attributes.index('columns') + 1].replace(" ", "").split(",") except ValueError: # rowset did not have columns tag set (this is a bug in API) # columns will be extracted from first row instead. columns = [] try: - priKey = attributes[attributes.index('key')+1] + priKey = attributes[attributes.index('key') + 1] this = IndexRowset(cols=columns, key=priKey) except ValueError: this = Rowset(cols=columns) - - this._name = attributes[attributes.index('name')+1] - this.__catch = "row" # tag to auto-add to rowset. + this._name = attributes[attributes.index('name') + 1] + this.__catch = "row" # tag to auto-add to rowset. else: this = Element() this._name = name @@ -528,7 +529,7 @@ class _Parser(object): if name != "eveapi": raise RuntimeError("Invalid API response") try: - this.version = attributes[attributes.index("version")+1] + this.version = attributes[attributes.index("version") + 1] except KeyError: raise RuntimeError("Invalid API response") self.root = this @@ -541,16 +542,18 @@ class _Parser(object): # such as rawQuantity in the assets lists. # In either case the tag is assumed to be correct and the rowset's # columns are overwritten with the tag's version, if required. - numAttr = len(attributes)/2 + numAttr = len(attributes) / 2 numCols = len(self.container._cols) if numAttr < numCols and (attributes[-2] == self.container._cols[-1]): # the row data is missing attributes that were defined in the rowset. # missing attributes' values will be set to None. fixed = [] - row_idx = 0; hdr_idx = 0; numAttr*=2 + row_idx = 0 + hdr_idx = 0 + numAttr *= 2 for col in self.container._cols: if col == attributes[row_idx]: - fixed.append(_castfunc(col, attributes[row_idx+1])) + fixed.append(_castfunc(col, attributes[row_idx + 1])) row_idx += 2 else: fixed.append(None) @@ -560,7 +563,7 @@ class _Parser(object): if not self.container._cols or (numAttr > numCols): # the row data contains more attributes than were defined. self.container._cols = attributes[0::2] - self.container.append([_castfunc(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]) + self.container.append([_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)]) # this._isrow = True @@ -611,7 +614,7 @@ class _Parser(object): if this is self.root: del this._attributes - #this.__dict__.pop("_attributes", None) + # this.__dict__.pop("_attributes", None) return # we're done with current tag, so we can pop it off. This means that @@ -651,7 +654,7 @@ class _Parser(object): e._name = this._name setattr(self.container, this._name, e) for i in xrange(0, len(attributes), 2): - setattr(e, attributes[i], attributes[i+1]) + setattr(e, attributes[i], attributes[i + 1]) else: # tag of the form: , treat as empty string. setattr(self.container, this._name, "") @@ -663,7 +666,7 @@ class _Parser(object): # multiples of some tag or attribute. Code below handles this case. elif isinstance(sibling, Rowset): # its doppelganger is a rowset, append this as a row to that. - row = [_castfunc(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)] + row = [_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)] row.extend([getattr(this, col) for col in attributes2]) sibling.append(row) elif isinstance(sibling, Element): @@ -672,11 +675,11 @@ class _Parser(object): # into a Rowset, adding the sibling element and this one. rs = Rowset() rs.__catch = rs._name = this._name - row = [_castfunc(attributes[i], attributes[i+1]) for i in xrange(0, len(attributes), 2)]+[getattr(this, col) for col in attributes2] + row = [_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)] + [getattr(this, col) for col in attributes2] rs.append(row) - row = [getattr(sibling, attributes[i]) for i in xrange(0, len(attributes), 2)]+[getattr(sibling, col) for col in attributes2] + row = [getattr(sibling, attributes[i]) for i in xrange(0, len(attributes), 2)] + [getattr(sibling, col) for col in attributes2] rs.append(row) - rs._cols = [attributes[i] for i in xrange(0, len(attributes), 2)]+[col for col in attributes2] + rs._cols = [attributes[i] for i in xrange(0, len(attributes), 2)] + [col for col in attributes2] setattr(self.container, this._name, rs) else: # something else must have set this attribute already. @@ -685,29 +688,31 @@ class _Parser(object): # Now fix up the attributes and be done with it. for i in xrange(0, len(attributes), 2): - this.__dict__[attributes[i]] = _castfunc(attributes[i], attributes[i+1]) + this.__dict__[attributes[i]] = _castfunc(attributes[i], attributes[i + 1]) return - - -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # XML Data Containers -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- # The following classes are the various container types the XML data is # unpacked into. # # Note that objects returned by API calls are to be treated as read-only. This # is not enforced, but you have been warned. -#----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + class Element(object): # Element is a namespace for attributes and nested tags def __str__(self): return "" % self._name + _fmt = u"%s:%s".__mod__ + + class Row(object): # A Row is a single database record associated with a Rowset. # The fields in the record are accessed as attributes by their respective @@ -750,7 +755,7 @@ class Row(object): try: return self._row[self._cols.index(this)] except: - raise AttributeError, this + raise AttributeError(this) def __getitem__(self, this): return self._row[self._cols.index(this)] @@ -823,7 +828,6 @@ class Rowset(object): for line in self._rows: yield [line[x] for x in i] - # ------------- def __init__(self, cols=None, rows=None): @@ -871,7 +875,6 @@ class Rowset(object): self._cols, self._rows = state - class IndexRowset(Rowset): # An IndexRowset is a Rowset that keeps an index on a column. # @@ -888,7 +891,7 @@ class IndexRowset(Rowset): if row is None: if default: return default[0] - raise KeyError, key + raise KeyError(key) return Row(self._cols, row) # ------------- @@ -960,7 +963,7 @@ class FilterRowset(object): if id in items: items[id][row[idfield2]] = row else: - items[id] = {row[idfield2]:row} + items[id] = {row[idfield2]: row} self._cols = cols self.key = key diff --git a/service/fit.py b/service/fit.py index aed843cfa..f4727dc3c 100644 --- a/service/fit.py +++ b/service/fit.py @@ -17,10 +17,14 @@ # along with pyfa. If not, see . # =============================================================================== - +import locale import copy +import threading import logging import wx +from codecs import open + +import xml.parsers.expat import eos.db from eos.types import State, Slot, Module, Drone, Fighter, Fit as FitType @@ -80,8 +84,8 @@ class Fit(object): "showMarketShortcuts": False, "enableGaugeAnimation": True, "exportCharges": True, - "openFitInNew": False - } + "openFitInNew": False, + } self.serviceFittingOptions = SettingsProvider.getInstance().getSettings( "pyfaServiceFittingOptions", serviceFittingDefaultOptions) @@ -532,7 +536,7 @@ class Fit(object): # Gather modules and convert Cargo item to Module, silently return if not a module try: - cargoP = Module(cargo.item) + cargoP = es_Module(cargo.item) cargoP.owner = fit if cargoP.isValidState(State.ACTIVE): cargoP.state = State.ACTIVE @@ -665,7 +669,7 @@ class Fit(object): break ''' if fighter is None: - fighter = Fighter(item) + fighter = es_Fighter(item) used = fit.getSlotsUsed(fighter.slot) total = fit.getNumSlots(fighter.slot) standardAttackActive = False @@ -756,7 +760,7 @@ class Fit(object): d.amount = amount d.amountActive = amount if active else 0 - newD = Drone(d.item) + newD = es_Drone(d.item) newD.amount = total - amount newD.amountActive = newD.amount if active else 0 l.append(newD) diff --git a/service/fleet.py b/service/fleet.py index 68c344189..a3f255336 100644 --- a/service/fleet.py +++ b/service/fleet.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import copy import eos.db diff --git a/service/implantSet.py b/service/implantSet.py index dcf954be2..add0808cc 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2016 Ryan Holmes # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import copy @@ -27,8 +27,9 @@ from eos.saveddata.implantSet import ImplantSet as es_ImplantSet class ImportError(Exception): pass -class ImplantSets(): +class ImplantSets(object): instance = None + @classmethod def getInstance(cls): if cls.instance is None: @@ -43,8 +44,7 @@ class ImplantSets(): return eos.db.getImplantSet(name) def getImplants(self, setID): - set = eos.db.getImplantSet(setID) - return set.implants + return eos.db.getImplantSet(setID).implants def addImplant(self, setID, itemID): set = eos.db.getImplantSet(setID) @@ -53,30 +53,29 @@ class ImplantSets(): eos.db.commit() def removeImplant(self, setID, implant): - set = eos.db.getImplantSet(setID) - set.implants.remove(implant) + eos.db.getImplantSet(setID).implants.remove(implant) eos.db.commit() def newSet(self, name): - s = es_ImplantSet() - s.name = name - eos.db.save(s) - return s + implant_set = es_ImplantSet() + implant_set.name = name + eos.db.save(implant_set) + return implant_set - def renameSet(self, s, newName): - s.name = newName - eos.db.save(s) + def renameSet(self, implant_set, newName): + implant_set.name = newName + eos.db.save(implant_set) - def deleteSet(self, s): - eos.db.remove(s) + def deleteSet(self, implant_set): + eos.db.remove(implant_set) - def copySet(self, s): - newS = copy.deepcopy(s) + def copySet(self, implant_set): + newS = copy.deepcopy(implant_set) eos.db.save(newS) return newS - def saveChanges(self, s): - eos.db.save(s) + def saveChanges(self, implant_set): + eos.db.save(implant_set) def importSets(self, text): sMkt = Market.getInstance() @@ -101,16 +100,16 @@ class ImplantSets(): errors += 1 continue - for set in self.getImplantSetList(): - lookup[set.name] = set + for implant_set in self.getImplantSetList(): + lookup[implant_set.name] = implant_set - for set in newSets: - if set.name in lookup: - match = lookup[set.name] - for implant in set.implants: + for implant_set in newSets: + if implant_set.name in lookup: + match = lookup[implant_set.name] + for implant in implant_set.implants: match.implants.append(es_Implant(implant.item)) else: - eos.db.save(set) + eos.db.save(implant_set) eos.db.commit() @@ -118,7 +117,8 @@ class ImplantSets(): if lenImports == 0: raise ImportError("No patterns found for import") if errors > 0: - raise ImportError("%d sets imported from clipboard; %d errors"%(lenImports, errors)) + raise ImportError("%d sets imported from clipboard; %d errors" % + (lenImports, errors)) def exportSets(self): patterns = self.getImplantSetList() diff --git a/service/network.py b/service/network.py index 8923f8b57..48769a48b 100644 --- a/service/network.py +++ b/service/network.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import urllib2 @@ -54,6 +54,7 @@ class Network(): UPDATE = 8 _instance = None + @classmethod def getInstance(cls): if cls._instance is None: @@ -63,7 +64,7 @@ class Network(): def request(self, url, type, data=None): # URL is required to be https as of right now - #print "Starting request: %s\n\tType: %s\n\tPost Data: %s"%(url,type,data) + # print "Starting request: %s\n\tType: %s\n\tPost Data: %s"%(url,type,data) # Make sure request is enabled access = NetworkSettings.getInstance().getAccess() @@ -73,7 +74,7 @@ class Network(): # Set up some things for the request versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion) - headers = {"User-Agent" : "pyfa {0} (Python-urllib2)".format(versionString)} + headers = {"User-Agent": "pyfa {0} (Python-urllib2)".format(versionString)} proxy = NetworkSettings.getInstance().getProxySettings() if proxy is not None: diff --git a/service/port.py b/service/port.py index 38257fda8..3f2751b4f 100644 --- a/service/port.py +++ b/service/port.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import re import os @@ -31,13 +31,13 @@ from codecs import open import xml.parsers.expat from eos import db +from service.fit import Fit import wx -from eos.types import Fit, State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel +from eos.types import State, Slot, Module, Cargo, Fit, Ship, Drone, Implant, Booster, Citadel from service.crest import Crest from service.market import Market -from service.fit import Fit logger = logging.getLogger("pyfa.service.port") @@ -48,15 +48,17 @@ except ImportError: EFT_SLOT_ORDER = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM] INV_FLAGS = { - Slot.LOW: 11, - Slot.MED: 19, - Slot.HIGH: 27, - Slot.RIG: 92, - Slot.SUBSYSTEM: 125} + Slot.LOW: 11, + Slot.MED: 19, + Slot.HIGH: 27, + Slot.RIG: 92, + Slot.SUBSYSTEM: 125 +} INV_FLAG_CARGOBAY = 5 INV_FLAG_DRONEBAY = 87 + class Port(object): def backupFits(self, path, callback): thread = FitBackupThread(path, callback) @@ -185,7 +187,7 @@ class Port(object): # max length is 50 characters name = ofit.name[:47] + '...' if len(ofit.name) > 50 else ofit.name fit['name'] = name - fit['ship']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, ofit.ship.item.ID) + fit['ship']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, ofit.ship.item.ID) fit['ship']['id'] = ofit.ship.item.ID fit['ship']['name'] = '' @@ -213,7 +215,7 @@ class Port(object): slotNum[slot] += 1 item['quantity'] = 1 - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, module.item.ID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, module.item.ID) item['type']['id'] = module.item.ID item['type']['name'] = '' fit['items'].append(item) @@ -228,7 +230,7 @@ class Port(object): item = nested_dict() item['flag'] = INV_FLAG_CARGOBAY item['quantity'] = cargo.amount - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, cargo.item.ID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, cargo.item.ID) item['type']['id'] = cargo.item.ID item['type']['name'] = '' fit['items'].append(item) @@ -237,7 +239,7 @@ class Port(object): item = nested_dict() item['flag'] = INV_FLAG_CARGOBAY item['quantity'] = amount - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, chargeID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, chargeID) item['type']['id'] = chargeID item['type']['name'] = '' fit['items'].append(item) @@ -246,7 +248,7 @@ class Port(object): item = nested_dict() item['flag'] = INV_FLAG_DRONEBAY item['quantity'] = drone.amount - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, drone.item.ID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, drone.item.ID) item['type']['id'] = drone.item.ID item['type']['name'] = '' fit['items'].append(item) @@ -372,12 +374,12 @@ class Port(object): except ValueError: f.ship = Citadel(sMkt.getItem(int(info[0]))) f.name = "{0} - DNA Imported".format(f.ship.item.name) - except UnicodeEncodeError as e: + except UnicodeEncodeError: def logtransform(s): if len(s) > 10: return s[:10] + "..." return s - logger.exception("Couldn't import ship data %r", [ logtransform(s) for s in info ]) + logger.exception("Couldn't import ship data %r", [logtransform(s) for s in info]) return None moduleList = [] diff --git a/service/prefetch.py b/service/prefetch.py index 30cec80a1..c3705dd54 100644 --- a/service/prefetch.py +++ b/service/prefetch.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import threading import os @@ -35,16 +35,15 @@ class PrefetchThread(threading.Thread): except: pass + prefetch = PrefetchThread() prefetch.daemon = True prefetch.start() -######## # The following code does not belong here, however until we rebuild skeletons # to include modified pyfa.py, this is the best place to put it. See GH issue # #176 # @ todo: move this to pyfa.py -######## # Make sure the saveddata db exists if config.savePath and not os.path.exists(config.savePath): diff --git a/service/price.py b/service/price.py index fc2bf9bbf..e3d39d734 100644 --- a/service/price.py +++ b/service/price.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import time @@ -24,12 +24,12 @@ from xml.dom import minidom from eos import db from service.network import Network, TimeoutError -VALIDITY = 24*60*60 # Price validity period, 24 hours -REREQUEST = 4*60*60 # Re-request delay for failed fetches, 4 hours -TIMEOUT = 15*60 # Network timeout delay for connection issues, 15 minutes +VALIDITY = 24 * 60 * 60 # Price validity period, 24 hours +REREQUEST = 4 * 60 * 60 # Re-request delay for failed fetches, 4 hours +TIMEOUT = 15 * 60 # Network timeout delay for connection issues, 15 minutes -class Price(): +class Price(object): @classmethod def fetchPrices(cls, prices): """Fetch all prices passed to this method""" @@ -65,7 +65,7 @@ class Price(): # Base request URL baseurl = "https://eve-central.com/api/marketstat" - data.append(("usesystem", 30000142)) # Use Jita for market + data.append(("usesystem", 30000142)) # Use Jita for market for typeID in toRequest: # Add all typeID arguments data.append(("typeid", typeID)) diff --git a/service/pycrest/__init__.py b/service/pycrest/__init__.py index f51a5440e..f083e1f84 100644 --- a/service/pycrest/__init__.py +++ b/service/pycrest/__init__.py @@ -5,6 +5,7 @@ class NullHandler(logging.Handler): def emit(self, record): pass + logger = logging.getLogger('pycrest') logger.addHandler(NullHandler()) diff --git a/service/pycrest/compat.py b/service/pycrest/compat.py index 06320069b..7484b41dd 100644 --- a/service/pycrest/compat.py +++ b/service/pycrest/compat.py @@ -21,4 +21,4 @@ def text_(s, encoding='latin-1', errors='strict'): # pragma: no cover def bytes_(s, encoding='latin-1', errors='strict'): # pragma: no cover if isinstance(s, text_type): return s.encode(encoding, errors) - return s \ No newline at end of file + return s diff --git a/service/pycrest/errors.py b/service/pycrest/errors.py index 33b9ca9ae..4216deabb 100644 --- a/service/pycrest/errors.py +++ b/service/pycrest/errors.py @@ -1,2 +1,2 @@ class APIException(Exception): - pass \ No newline at end of file + pass diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index 221389b79..a46ad6c91 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -243,19 +243,18 @@ class EVE(APIConnection): def temptoken_authorize(self, access_token=None, expires_in=0, refresh_token=None): self.set_auth_values({'access_token': access_token, - 'refresh_token': refresh_token, - 'expires_in': expires_in}) + 'refresh_token': refresh_token, + 'expires_in': expires_in}) class AuthedConnection(EVE): - def __call__(self): if not self._data: self._data = APIObject(self.get(self._endpoint), self) return self._data def whoami(self): - #if 'whoami' not in self._cache: + # if 'whoami' not in self._cache: # print "Setting this whoami cache" # self._cache['whoami'] = self.get("%s/verify" % self._oauth_endpoint) return self.get("%s/verify" % self._oauth_endpoint) @@ -275,6 +274,7 @@ class AuthedConnection(EVE): self.refr_authorize(self.refresh_token) return self._session.delete(resource, params=params) + class APIObject(object): def __init__(self, parent, connection): self._dict = {} diff --git a/service/pycrest/weak_ciphers.py b/service/pycrest/weak_ciphers.py index 13a09abba..ad0adec03 100644 --- a/service/pycrest/weak_ciphers.py +++ b/service/pycrest/weak_ciphers.py @@ -20,8 +20,7 @@ except: from urllib3.packages.ssl_match_hostname import match_hostname -class WeakCiphersHTTPSConnection( - urllib3.connection.VerifiedHTTPSConnection): # pragma: no cover +class WeakCiphersHTTPSConnection(urllib3.connection.VerifiedHTTPSConnection): # pragma: no cover # Python versions >=2.7.9 and >=3.4.1 do not (by default) allow ciphers # with MD5. Unfortunately, the CREST public server _only_ supports @@ -74,16 +73,20 @@ class WeakCiphersHTTPSConnection( # Wrap socket using verification with the root certs in # trusted_root_certs - self.sock = ssl_.ssl_wrap_socket(conn, self.key_file, self.cert_file, - cert_reqs=resolved_cert_reqs, - ca_certs=self.ca_certs, - server_hostname=hostname, - ssl_version=resolved_ssl_version, - ciphers=self.ciphers) + self.sock = ssl_.ssl_wrap_socket( + conn, + self.key_file, + self.cert_file, + cert_reqs=resolved_cert_reqs, + ca_certs=self.ca_certs, + server_hostname=hostname, + ssl_version=resolved_ssl_version, + ciphers=self.ciphers, + ) if self.assert_fingerprint: ssl_.assert_fingerprint(self.sock.getpeercert(binary_form=True), - self.assert_fingerprint) + self.assert_fingerprint) elif resolved_cert_reqs != ssl.CERT_NONE \ and self.assert_hostname is not False: cert = self.sock.getpeercert() @@ -96,8 +99,7 @@ class WeakCiphersHTTPSConnection( ) match_hostname(cert, self.assert_hostname or hostname) - self.is_verified = (resolved_cert_reqs == ssl.CERT_REQUIRED - or self.assert_fingerprint is not None) + self.is_verified = (resolved_cert_reqs == ssl.CERT_REQUIRED or self.assert_fingerprint is not None) class WeakCiphersHTTPSConnectionPool( @@ -110,22 +112,24 @@ class WeakCiphersPoolManager(urllib3.poolmanager.PoolManager): def _new_pool(self, scheme, host, port): if scheme == 'https': - return WeakCiphersHTTPSConnectionPool(host, port, - **(self.connection_pool_kw)) - return super(WeakCiphersPoolManager, self)._new_pool(scheme, host, - port) + return WeakCiphersHTTPSConnectionPool(host, port, **(self.connection_pool_kw)) + return super(WeakCiphersPoolManager, self)._new_pool(scheme, host, port) class WeakCiphersAdapter(HTTPAdapter): """"Transport adapter" that allows us to use TLS_RSA_WITH_RC4_128_MD5.""" - def init_poolmanager(self, connections, maxsize, block=False, - **pool_kwargs): + def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs): # Rewrite of the requests.adapters.HTTPAdapter.init_poolmanager method # to use WeakCiphersPoolManager instead of urllib3's PoolManager self._pool_connections = connections self._pool_maxsize = maxsize self._pool_block = block - self.poolmanager = WeakCiphersPoolManager(num_pools=connections, - maxsize=maxsize, block=block, strict=True, **pool_kwargs) + self.poolmanager = WeakCiphersPoolManager( + num_pools=connections, + maxsize=maxsize, + block=block, + strict=True, + **pool_kwargs + ) diff --git a/service/server.py b/service/server.py index f65729a89..49e193cee 100644 --- a/service/server.py +++ b/service/server.py @@ -57,6 +57,7 @@ else { ''' + # https://github.com/fuzzysteve/CREST-Market-Downloader/ class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): @@ -73,6 +74,7 @@ class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler): def log_message(self, format, *args): return + # http://code.activestate.com/recipes/425210-simple-stoppable-server-using-socket-timeout/ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): @@ -102,7 +104,7 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): self.run = False def handle_timeout(self): - #logger.debug("Number of tries: %d"%self.tries) + # logger.debug("Number of tries: %d"%self.tries) self.tries += 1 if self.tries == self.max_tries: logger.debug("Server timed out waiting for connection") @@ -117,6 +119,7 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): pass self.server_close() + if __name__ == "__main__": httpd = StoppableHTTPServer(('', 6461), AuthHandler) thread.start_new_thread(httpd.serve, ()) diff --git a/service/settings.py b/service/settings.py index 186193033..579d484c2 100644 --- a/service/settings.py +++ b/service/settings.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import cPickle import os.path @@ -23,13 +23,15 @@ import urllib2 import config -class SettingsProvider(): + +class SettingsProvider(object): BASE_PATH = os.path.join(config.savePath or ".", "settings") settings = {} _instance = None + @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = SettingsProvider() return cls._instance @@ -72,7 +74,8 @@ class SettingsProvider(): for settings in self.settings.itervalues(): settings.save() -class Settings(): + +class Settings(object): def __init__(self, location, info): self.location = location self.info = info @@ -112,7 +115,7 @@ class Settings(): return self.info.items() -class NetworkSettings(): +class NetworkSettings(object): _instance = None # constants for serviceNetworkDefaultSettings["mode"] parameter @@ -122,7 +125,7 @@ class NetworkSettings(): @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = NetworkSettings() return cls._instance @@ -190,12 +193,11 @@ class NetworkSettings(): proxy = None proxAddr = proxPort = "" proxydict = urllib2.ProxyHandler().proxies - txt = "Auto-detected: " validPrefixes = ("http", "https") for prefix in validPrefixes: - if not prefix in proxydict: + if prefix not in proxydict: continue proxyline = proxydict[prefix] proto = "{0}://".format(prefix) @@ -238,22 +240,21 @@ class NetworkSettings(): self.serviceNetworkSettings["password"] = password - -""" -Settings used by the HTML export feature. -""" -class HTMLExportSettings(): +class HTMLExportSettings(object): + """ + Settings used by the HTML export feature. + """ _instance = None @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = HTMLExportSettings() return cls._instance def __init__(self): - serviceHTMLExportDefaultSettings = {"enabled": False, "path": config.pyfaPath + os.sep + 'pyfaFits.html', "minimal": False } + serviceHTMLExportDefaultSettings = {"enabled": False, "path": config.pyfaPath + os.sep + 'pyfaFits.html', "minimal": False} self.serviceHTMLExportSettings = SettingsProvider.getInstance().getSettings("pyfaServiceHTMLExportSettings", serviceHTMLExportDefaultSettings) def getEnabled(self): @@ -262,29 +263,28 @@ class HTMLExportSettings(): def setEnabled(self, enabled): self.serviceHTMLExportSettings["enabled"] = enabled - def getMinimalEnabled(self): return self.serviceHTMLExportSettings["minimal"] def setMinimalEnabled(self, minimal): self.serviceHTMLExportSettings["minimal"] = minimal - def getPath(self): return self.serviceHTMLExportSettings["path"] def setPath(self, path): self.serviceHTMLExportSettings["path"] = path -""" -Settings used by update notification -""" -class UpdateSettings(): + +class UpdateSettings(object): + """ + Settings used by update notification + """ _instance = None @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = UpdateSettings() return cls._instance @@ -294,7 +294,7 @@ class UpdateSettings(): # Updates are completely suppressed via network settings # prerelease - If True, suppress prerelease notifications # version - Set to release tag that user does not want notifications for - serviceUpdateDefaultSettings = {"prerelease": True, 'version': None } + serviceUpdateDefaultSettings = {"prerelease": True, 'version': None} self.serviceUpdateSettings = SettingsProvider.getInstance().getSettings("pyfaServiceUpdateSettings", serviceUpdateDefaultSettings) def get(self, type): @@ -303,7 +303,8 @@ class UpdateSettings(): def set(self, type, value): self.serviceUpdateSettings[type] = value -class CRESTSettings(): + +class CRESTSettings(object): _instance = None @classmethod diff --git a/service/targetResists.py b/service/targetResists.py index b98ec8b16..dc208e78a 100644 --- a/service/targetResists.py +++ b/service/targetResists.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import copy @@ -81,7 +81,7 @@ class TargetResists(object): if lenImports == 0: raise ImportError("No patterns found for import") if lenImports != num: - raise ImportError("%d patterns imported from clipboard; %d had errors"%(num, num-lenImports)) + raise ImportError("%d patterns imported from clipboard; %d had errors" % (num, num - lenImports)) def exportPatterns(self): patterns = self.getTargetResistsList() diff --git a/service/update.py b/service/update.py index 8f498e724..2b594bbf8 100644 --- a/service/update.py +++ b/service/update.py @@ -1,4 +1,4 @@ -#=============================================================================== +# ============================================================================= # Copyright (C) 2014 Ryan Holmes # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================= import threading import json @@ -28,6 +28,7 @@ import config from service.network import Network from service.settings import UpdateSettings + class CheckUpdateThread(threading.Thread): def __init__(self, callback): threading.Thread.__init__(self) @@ -49,7 +50,7 @@ class CheckUpdateThread(threading.Thread): continue # Handle use-case of updating to suppressed version - if self.settings.get('version') == 'v'+config.version: + if self.settings.get('version') == 'v' + config.version: self.settings.set('version', None) # Suppress version @@ -63,15 +64,15 @@ class CheckUpdateThread(threading.Thread): rVersion = release['tag_name'].replace('v', '', 1) if config.tag is 'git' and not release['prerelease'] and self.versiontuple(rVersion) >= self.versiontuple(config.version): - wx.CallAfter(self.callback, release) # git (dev/Singularity) -> Stable + wx.CallAfter(self.callback, release) # git (dev/Singularity) -> Stable elif config.expansionName is not "Singularity": if release['prerelease']: - wx.CallAfter(self.callback, release) # Stable -> Singularity + wx.CallAfter(self.callback, release) # Stable -> Singularity elif self.versiontuple(rVersion) > self.versiontuple(config.version): - wx.CallAfter(self.callback, release) # Stable -> Stable + wx.CallAfter(self.callback, release) # Stable -> Stable else: if release['prerelease'] and rVersion > config.expansionVersion: - wx.CallAfter(self.callback, release) # Singularity -> Singularity + wx.CallAfter(self.callback, release) # Singularity -> Singularity break except: pass @@ -79,10 +80,9 @@ class CheckUpdateThread(threading.Thread): def versiontuple(self, v): return tuple(map(int, (v.split(".")))) + class Update(): instance = None - def __init__(self): - pass def CheckUpdate(self, callback): thread = CheckUpdateThread(callback) @@ -90,6 +90,6 @@ class Update(): @classmethod def getInstance(cls): - if cls.instance == None: + if cls.instance is None: cls.instance = Update() return cls.instance diff --git a/utils/compat.py b/utils/compat.py index d8177973b..bf49eb4aa 100644 --- a/utils/compat.py +++ b/utils/compat.py @@ -237,7 +237,7 @@ class OrderedDict(dict): ''' if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() + return len(self) == len(other) and self.items() == other.items() return dict.__eq__(self, other) def __ne__(self, other): diff --git a/utils/timer.py b/utils/timer.py index c1ca87a8b..41b5f78b7 100644 --- a/utils/timer.py +++ b/utils/timer.py @@ -1,5 +1,6 @@ import time + class Timer(): def __init__(self, name='', logger=None): self.name = name @@ -9,11 +10,11 @@ class Timer(): @property def elapsed(self): - return (time.time() - self.start)*1000 + return (time.time() - self.start) * 1000 @property def last(self): - return (time.time() - self.__last)*1000 + return (time.time() - self.__last) * 1000 def checkpoint(self, name=''): text = u'Timer - {timer} - {checkpoint} - {last:.2f}ms ({elapsed:.2f}ms elapsed)'.format( @@ -26,7 +27,7 @@ class Timer(): if self.logger: self.logger.debug(text) else: - print text + print(text) def __enter__(self): return self From ab9c925c47c924fd7e076495eb99e1de71026f01 Mon Sep 17 00:00:00 2001 From: a-tal Date: Sat, 3 Dec 2016 18:21:35 -0800 Subject: [PATCH 06/53] non-standard - use forces coding declarations (cherry picked from commit 6e17d88) --- gui/builtinContextMenus/metaSwap.py | 2 ++ gui/builtinContextMenus/moduleAmmoPicker.py | 16 +++++++++------- gui/builtinViewColumns/baseName.py | 1 + gui/shipBrowser.py | 2 ++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py index 43d722346..66e9ed8a2 100644 --- a/gui/builtinContextMenus/metaSwap.py +++ b/gui/builtinContextMenus/metaSwap.py @@ -1,3 +1,5 @@ +# coding: utf-8 + import wx from service.fit import Fit diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 03ace7f52..649b7bc27 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -1,12 +1,14 @@ -# -*- coding: utf-8 -*- -from gui.contextMenu import ContextMenu -import gui.mainFrame +# coding: utf-8 + import wx -from gui.bitmapLoader import BitmapLoader -from eos.types import Hardpoint -import gui.globalEvents as GE -from service.market import Market + from service.fit import Fit +from service.market import Market +from eos.types import Hardpoint +import gui.mainFrame +import gui.globalEvents as GE +from gui.contextMenu import ContextMenu +from gui.bitmapLoader import BitmapLoader class ModuleAmmoPicker(ContextMenu): diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index 9018ac453..d739ee903 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -1,3 +1,4 @@ +# coding: utf-8 # ============================================================================= # Copyright (C) 2010 Diego Duclos # diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 1a00089f9..8c27965b9 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -1,3 +1,5 @@ +# coding: utf-8 + import re import time From 874bfb3305fe278a4c5acd731d2b600621152a52 Mon Sep 17 00:00:00 2001 From: a-tal Date: Sat, 3 Dec 2016 18:28:31 -0800 Subject: [PATCH 07/53] missed a type (cherry picked from commit f416c77) --- gui/builtinStatsViews/resourcesViewFull.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index 4f3f63b3b..d9c71ff65 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -106,7 +106,7 @@ class ResourcesViewFull(StatsView): for type_ in ("turret", "launcher", "drones", "fighter", "calibration"): box = wx.BoxSizer(wx.HORIZONTAL) - bitmap = BitmapLoader.getStaticBitmap("%s_big" % type, parent, "gui") + bitmap = BitmapLoader.getStaticBitmap("%s_big" % type_, parent, "gui") tooltip = wx.ToolTip(tooltipText[type_]) bitmap.SetToolTip(tooltip) From 4a9b1df9b4ae06d59eba42e746c5c4a0cde54154 Mon Sep 17 00:00:00 2001 From: a-tal Date: Sat, 3 Dec 2016 18:36:34 -0800 Subject: [PATCH 08/53] pep8 fixes (cherry picked from commit 5dc43b2) --- pyfa.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/pyfa.py b/pyfa.py index 146ea704d..e3364814e 100755 --- a/pyfa.py +++ b/pyfa.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -#=============================================================================== +# ============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -16,7 +16,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# ============================================================================== import sys import re @@ -24,6 +24,7 @@ import config from optparse import OptionParser, BadOptionError, AmbiguousOptionError + class PassThroughOptionParser(OptionParser): """ An unknown option pass-through implementation of OptionParser. @@ -33,10 +34,11 @@ class PassThroughOptionParser(OptionParser): def _process_args(self, largs, rargs, values): while rargs: try: - OptionParser._process_args(self,largs,rargs,values) - except (BadOptionError,AmbiguousOptionError), e: + OptionParser._process_args(self, largs, rargs, values) + except (BadOptionError, AmbiguousOptionError) as e: largs.append(e.opt_str) + # Parse command line options usage = "usage: %prog [--root]" parser = PassThroughOptionParser(usage=usage) @@ -50,7 +52,7 @@ parser.add_option("-s", "--savepath", action="store", dest="savepath", help="Set if not hasattr(sys, 'frozen'): - if sys.version_info < (2,6) or sys.version_info > (3,0): + if sys.version_info < (2, 6) or sys.version_info > (3, 0): print("Pyfa requires python 2.x branch ( >= 2.6 )\nExiting.") sys.exit(1) @@ -66,13 +68,13 @@ if not hasattr(sys, 'frozen'): else: wxversion.select(['3.0', '2.8']) except wxversion.VersionError: - print "Installed wxPython version doesn't meet requirements.\nYou can download wxPython 2.8 or 3.0 from http://www.wxpython.org/" + print("Installed wxPython version doesn't meet requirements.\nYou can download wxPython 2.8 or 3.0 from http://www.wxpython.org/") sys.exit(1) try: import sqlalchemy - saVersion = sqlalchemy.__version__ + saVersion = sqlalchemy.__version__ saMatch = re.match("([0-9]+).([0-9]+)([b\.])([0-9]+)", saVersion) if saMatch: saMajor = int(saMatch.group(1)) @@ -88,10 +90,10 @@ if not hasattr(sys, 'frozen'): except ImportError: print("Cannot find sqlalchemy.\nYou can download sqlalchemy (0.6+) from http://www.sqlalchemy.org/") sys.exit(1) - + # check also for dateutil module installed. try: - import dateutil.parser # Copied import statement from service/update.py + import dateutil.parser # noqa - Copied import statement from service/update.py except ImportError: print("Cannot find python-dateutil.\nYou can download python-dateutil from https://pypi.python.org/pypi/python-dateutil") sys.exit(1) @@ -101,10 +103,10 @@ if __name__ == "__main__": # Configure paths if options.rootsavedata is True: config.saveInRoot = True - + # set title if it wasn't supplied by argument - if options.title == None: - options.title = "pyfa %s%s - Python Fitting Assistant"%(config.version, "" if config.tag.lower() != 'git' else " (git)") + if options.title is None: + options.title = "pyfa %s%s - Python Fitting Assistant" % (config.version, "" if config.tag.lower() != 'git' else " (git)") config.debug = options.debug # convert to unicode if it is set @@ -122,10 +124,10 @@ if __name__ == "__main__": import os.path import eos.db - import service.prefetch + import service.prefetch # noqa from gui.mainFrame import MainFrame - #Make sure the saveddata db exists + # Make sure the saveddata db exists if not os.path.exists(config.savePath): os.mkdir(config.savePath) From b4dd65cf3e8507a7c2ab5b9dabf668e72a06d5f2 Mon Sep 17 00:00:00 2001 From: a-tal Date: Sat, 3 Dec 2016 18:37:50 -0800 Subject: [PATCH 09/53] dont use built-in function format as a var name (cherry picked from commit 74dd6cf) --- config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.py b/config.py index fdf6a18a9..e2c703f99 100644 --- a/config.py +++ b/config.py @@ -103,10 +103,10 @@ def defPaths(customSavePath): os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem") os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") - format = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' - logging.basicConfig(format=format, level=logLevel) + format_ = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' + logging.basicConfig(format=format_, level=logLevel) handler = logging.handlers.RotatingFileHandler(os.path.join(savePath, "log.txt"), maxBytes=1000000, backupCount=3) - formatter = logging.Formatter(format) + formatter = logging.Formatter(format_) handler.setFormatter(formatter) logging.getLogger('').addHandler(handler) From 57f930c83e28b73cf865c220e47d27f1f8801654 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Tue, 13 Dec 2016 20:35:03 -0800 Subject: [PATCH 10/53] Cherry pick commits from @a-tal --- eos/db/saveddata/fit.py | 15 ++++++++------- gui/mainFrame.py | 3 +++ gui/updateDialog.py | 3 ++- service/character.py | 4 ---- service/fit.py | 4 ---- 5 files changed, 13 insertions(+), 16 deletions(-) diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py index 46a9ff5f5..4e0afeb28 100644 --- a/eos/db/saveddata/fit.py +++ b/eos/db/saveddata/fit.py @@ -24,12 +24,13 @@ from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.sql import and_ from eos.db import saveddata_meta +from eos.db import saveddata_session from eos.db.saveddata.cargo import cargo_table from eos.db.saveddata.drone import drones_table from eos.db.saveddata.fighter import fighters_table from eos.db.saveddata.implant import fitImplants_table from eos.db.saveddata.module import modules_table -from eos.effectHandlerHelpers import * +from eos.effectHandlerHelpers import HandledModuleList, HandledImplantBoosterList, HandledProjectedModList, HandledDroneCargoList, HandledProjectedDroneList from eos.types import Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, \ TargetResists, ImplantLocation @@ -72,9 +73,9 @@ class ProjectedFit(object): def init(self): if self.source_fit.isInvalid: # Very rare for this to happen, but be prepared for it - eos.db.saveddata_session.delete(self.source_fit) - eos.db.saveddata_session.flush() - eos.db.saveddata_session.refresh(self.victim_fit) + saveddata_session.delete(self.source_fit) + saveddata_session.flush() + saveddata_session.refresh(self.victim_fit) # We have a series of setters and getters here just in case someone # downgrades and screws up the table with NULL values @@ -101,9 +102,9 @@ class CommandFit(object): def init(self): if self.booster_fit.isInvalid: # Very rare for this to happen, but be prepared for it - eos.db.saveddata_session.delete(self.booster_fit) - eos.db.saveddata_session.flush() - eos.db.saveddata_session.refresh(self.boosted_fit) + saveddata_session.delete(self.booster_fit) + saveddata_session.flush() + saveddata_session.refresh(self.boosted_fit) def __repr__(self): return "CommandFit(boosterID={}, boostedID={}, active={}) at {}".format( diff --git a/gui/mainFrame.py b/gui/mainFrame.py index f53f617c8..69bdbe3e7 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -72,6 +72,9 @@ from service.settings import HTMLExportSettings from time import gmtime, strftime +import threading +import webbrowser + if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): from service.crest import CrestModes from gui.crestFittings import CrestFittings, ExportToEve, CrestMgmt diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 29381558e..7b148c546 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -21,13 +21,14 @@ import wx from gui.bitmapLoader import BitmapLoader import config import dateutil.parser +from service import settings class UpdateDialog(wx.Dialog): def __init__(self, parent, release): wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "Pyfa Update", pos = wx.DefaultPosition, size = wx.Size( 400,300 ), style = wx.DEFAULT_DIALOG_STYLE ) - self.UpdateSettings = service.settings.UpdateSettings.getInstance() + self.UpdateSettings = settings.UpdateSettings.getInstance() self.releaseInfo = release self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize ) diff --git a/service/character.py b/service/character.py index db7bba12a..f93345224 100644 --- a/service/character.py +++ b/service/character.py @@ -37,7 +37,6 @@ from eos.saveddata.implant import Implant as es_Implant from eos.saveddata.character import Character as es_Character from eos.saveddata.module import Slot as es_Slot, Module as es_Module from eos.saveddata.fighter import Fighter as es_Fighter -from service.fit import Fit as es_Fit logger = logging.getLogger(__name__) @@ -92,9 +91,6 @@ class SkillBackupThread(threading.Thread): def run(self): path = self.path sCharacter = Character.getInstance() - sFit = es_Fit.getInstance() - fit = sFit.getFit(self.activeFit) - backupData = "" if self.saveFmt == "xml" or self.saveFmt == "emp": backupData = sCharacter.exportXml() else: diff --git a/service/fit.py b/service/fit.py index f4727dc3c..877ce4d94 100644 --- a/service/fit.py +++ b/service/fit.py @@ -46,10 +46,6 @@ from eos.saveddata.character import Character as saveddata_Character from service.fleet import Fleet from service.settings import SettingsProvider - -# TODO: port this to port.py -#from service.port import Port - logger = logging.getLogger(__name__) From b2a5a206502b4a3c8feef3bf5eaeda3be760ba52 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Tue, 13 Dec 2016 20:37:52 -0800 Subject: [PATCH 11/53] Travis and CodeCov files --- .codecov.yml | 26 ++++++++++++++++++++++++++ .gitattributes | 17 +++++++++++++++++ .travis.yml | 25 +++++++++++++++++++++++++ requirements.txt | 6 ++++++ requirements_test.txt | 7 +++++++ tox.ini | 15 +++++++++++++++ 6 files changed, 96 insertions(+) create mode 100644 .codecov.yml create mode 100644 .gitattributes create mode 100644 .travis.yml create mode 100644 requirements.txt create mode 100644 requirements_test.txt create mode 100644 tox.ini diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 000000000..2b5037ed5 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,26 @@ +codecov: + notify: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + + status: + project: yes + patch: yes + changes: no + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "header, diff" + behavior: default + require_changes: no diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..c30fccabf --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +# *.c text +# *.h text + +# Declare files that will always have CRLF line endings on checkout. +*.py text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.icns binary +*.ico binary +*.db binary diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..497b67b08 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: python +python: + - '2.7' +env: + - TOXENV=pep8 +addons: + apt: + packages: + # for wxPython: + - python-wxgtk2.8 + - python-wxtools + - wx2.8-doc + - wx2.8-examples + - wx2.8-headers + - wx2.8-i18n +before_install: + - pip install -U tox +install: + - pip install -r requirements.txt + - pip install -r requirements_test.txt +script: + - tox + - py.test --cov=./ +after_success: + - bash <(curl -s https://codecov.io/bash) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..f8e777507 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +matplotlib +PyYAML +python-dateutil +urllib3 +requests==2.11.1 +sqlalchemy diff --git a/requirements_test.txt b/requirements_test.txt new file mode 100644 index 000000000..1bbef8b92 --- /dev/null +++ b/requirements_test.txt @@ -0,0 +1,7 @@ +pytest==3.0.3 +pytest-mock==1.2 +pytest-cov==2.3.1 +pytest-capturelog==0.7 +coverage==4.2 +coveralls==1.1 +codecov diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..3ce2756f3 --- /dev/null +++ b/tox.ini @@ -0,0 +1,15 @@ +[tox] +envlist = pep8 +skipsdist = True + +[testenv] +passenv = CI TRAVIS TRAVIS_* +deps = + -rrequirements.txt + -rrequirements_test.txt +basepython = python2.7 +commands = py.test -vv --cov Pyfa tests/ + +[testenv:pep8] +deps = flake8 +commands = flake8 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,venv,tests,.tox,build,dist,__init__.py --ignore=E501,E731 gui_service gui eos utils config.py pyfa.py From be53dedb189cf573415d9a6643ab3afd16cb15cb Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Tue, 13 Dec 2016 21:23:01 -0800 Subject: [PATCH 12/53] Large pep8 compliance to make work for Tox --- gui/PFListPane.py | 3 +- gui/PFSearchBox.py | 5 +- gui/aboutData.py | 1 - gui/additionsPane.py | 1 - gui/bitmapLoader.py | 1 - gui/cargoView.py | 24 +- gui/characterEditor.py | 80 +-- gui/chromeTabs.py | 30 +- gui/contextMenu.py | 359 +++++++------ gui/copySelectDialog.py | 6 +- gui/crestFittings.py | 106 ++-- gui/display.py | 7 +- gui/fighterView.py | 53 +- gui/fleetBrowser.py | 897 +++++++++++++++---------------- gui/gangView.py | 16 +- gui/globalEvents.py | 1 - gui/graphFrame.py | 563 +++++++++---------- gui/itemStats.py | 45 +- gui/mainFrame.py | 13 +- gui/mainMenuBar.py | 4 +- gui/marketBrowser.py | 923 ++++++++++++++++---------------- gui/patternEditor.py | 28 +- gui/propertyEditor.py | 12 +- gui/pyfatogglepanel.py | 3 +- gui/resistsEditor.py | 4 +- gui/setEditor.py | 18 +- gui/sfBrowserItem.py | 3 +- gui/shipBrowser.py | 50 +- gui/statsPane.py | 3 +- gui/updateDialog.py | 98 ++-- gui/utils/fonts.py | 2 - service/attribute.py | 2 + service/character.py | 11 +- service/crest.py | 25 +- service/damagePattern.py | 4 +- service/eveapi.py | 15 +- service/fit.py | 31 +- service/fleet.py | 435 +++++++-------- service/implantSet.py | 2 + service/market.py | 185 ++++--- service/network.py | 15 +- service/port.py | 6 +- service/prefetch.py | 142 ++--- service/pycrest/eve.py | 1 - service/pycrest/weak_ciphers.py | 6 +- service/server.py | 1 - service/settings.py | 27 +- service/targetResists.py | 4 +- service/update.py | 9 +- tox.ini | 2 +- 50 files changed, 2216 insertions(+), 2066 deletions(-) diff --git a/gui/PFListPane.py b/gui/PFListPane.py index 9803f6873..556b97638 100644 --- a/gui/PFListPane.py +++ b/gui/PFListPane.py @@ -22,7 +22,8 @@ import wx class PFListPane(wx.ScrolledWindow): def __init__(self, parent): - wx.ScrolledWindow.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(1, 1), style=wx.TAB_TRAVERSAL) + wx.ScrolledWindow.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(1, 1), + style=wx.TAB_TRAVERSAL) self._wList = [] self._wCount = 0 diff --git a/gui/PFSearchBox.py b/gui/PFSearchBox.py index 452bdbf09..ece5cef5f 100644 --- a/gui/PFSearchBox.py +++ b/gui/PFSearchBox.py @@ -2,7 +2,6 @@ import wx import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils - SearchButton, EVT_SEARCH_BTN = wx.lib.newevent.NewEvent() CancelButton, EVT_CANCEL_BTN = wx.lib.newevent.NewEvent() TextEnter, EVT_TEXT_ENTER = wx.lib.newevent.NewEvent() @@ -40,7 +39,9 @@ class PFSearchBox(wx.Window): self._hl = False w, h = size - self.EditBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1), wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) + self.EditBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, + (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1), + wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk) diff --git a/gui/aboutData.py b/gui/aboutData.py index 98e4c12f7..6e8ce07bc 100644 --- a/gui/aboutData.py +++ b/gui/aboutData.py @@ -19,7 +19,6 @@ import config - versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion) licenses = ( "pyfa is released under GNU GPLv3 - see included LICENSE file", diff --git a/gui/additionsPane.py b/gui/additionsPane.py index 2e7e459e3..c0616f597 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -35,7 +35,6 @@ import gui.chromeTabs class AdditionsPane(TogglePanel): - def __init__(self, parent): TogglePanel.__init__(self, parent, forceLayout=1) diff --git a/gui/bitmapLoader.py b/gui/bitmapLoader.py index fb8be8a02..307a11267 100644 --- a/gui/bitmapLoader.py +++ b/gui/bitmapLoader.py @@ -30,7 +30,6 @@ except ImportError: class BitmapLoader(object): - try: archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') except IOError: diff --git a/gui/cargoView.py b/gui/cargoView.py index 527b41691..6fe9e3c9e 100644 --- a/gui/cargoView.py +++ b/gui/cargoView.py @@ -27,21 +27,21 @@ from service.market import Market class CargoViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) + def __init__(self, dropFn): + wx.PyDropTarget.__init__(self) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t -# @todo: Was copied form another class and modified. Look through entire file, refine +# @todo: Was copied form another class and modified. Look through entire file, refine class CargoView(d.Display): DEFAULT_COLS = ["Base Icon", "Base Name", diff --git a/gui/characterEditor.py b/gui/characterEditor.py index e9d512a74..2d9e63277 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -99,14 +99,14 @@ class CharacterEntityEditor(EntityEditor): class CharacterEditor(wx.Frame): def __init__(self, parent): - wx.Frame.__init__ (self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition, - size=wx.Size(640, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition, + size=wx.Size(640, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) i = wx.IconFromBitmap(BitmapLoader.getBitmap("character_small", "gui")) self.SetIcon(i) self.mainFrame = parent - #self.disableWin = wx.WindowDisabler(self) + # self.disableWin = wx.WindowDisabler(self) sFit = Fit.getInstance() self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) @@ -179,7 +179,7 @@ class CharacterEditor(wx.Frame): event.Skip() def editingFinished(self, event): - #del self.disableWin + # del self.disableWin wx.PostEvent(self.mainFrame, GE.CharListUpdated()) self.Destroy() @@ -201,7 +201,7 @@ class CharacterEditor(wx.Frame): wx.PostEvent(self, GE.CharListUpdated()) def closeEvent(self, event): - #del self.disableWin + # del self.disableWin wx.PostEvent(self.mainFrame, GE.CharListUpdated()) self.Destroy() @@ -234,18 +234,20 @@ class CharacterEditor(wx.Frame): wx.Frame.Destroy(self) -class SkillTreeView (wx.Panel): + +class SkillTreeView(wx.Panel): def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.TAB_TRAVERSAL) self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.VERTICAL) - tree = self.skillTreeListCtrl = wx.gizmos.TreeListCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) + tree = self.skillTreeListCtrl = wx.gizmos.TreeListCtrl(self, wx.ID_ANY, + style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) pmainSizer.Add(tree, 1, wx.EXPAND | wx.ALL, 5) - self.imageList = wx.ImageList(16, 16) tree.SetImageList(self.imageList) self.skillBookImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) @@ -289,7 +291,6 @@ class SkillTreeView (wx.Panel): self.revertID = wx.NewId() self.levelChangeMenu.Append(self.revertID, "Revert") - self.saveID = wx.NewId() self.levelChangeMenu.Append(self.saveID, "Save") @@ -328,7 +329,7 @@ class SkillTreeView (wx.Panel): if tree.GetItemText(child) == "dummy": tree.Delete(child) - #Get the real intrestin' stuff + # Get the real intrestin' stuff sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() for id, name in sChar.getSkills(tree.GetPyData(root)): @@ -391,7 +392,7 @@ class SkillTreeView (wx.Panel): class ImplantEditorView(BaseImplantEditorView): def __init__(self, parent): - BaseImplantEditorView.__init__ (self, parent) + BaseImplantEditorView.__init__(self, parent) self.determineEnabled() @@ -445,9 +446,10 @@ class ImplantEditorView(BaseImplantEditorView): self.Enable() -class APIView (wx.Panel): +class APIView(wx.Panel): def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), + style=wx.TAB_TRAVERSAL) self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) @@ -456,17 +458,17 @@ class APIView (wx.Panel): pmainSizer = wx.BoxSizer(wx.VERTICAL) - hintSizer = wx.BoxSizer( wx.HORIZONTAL ) + hintSizer = wx.BoxSizer(wx.HORIZONTAL) hintSizer.AddStretchSpacer() - self.stDisabledTip = wx.StaticText( self, wx.ID_ANY, u"You cannot add API Details for All 0 and All 5 characters.\n" - u"Please select another character or make a new one.", style=wx.ALIGN_CENTER ) - self.stDisabledTip.Wrap( -1 ) - hintSizer.Add( self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10 ) + self.stDisabledTip = wx.StaticText(self, wx.ID_ANY, + u"You cannot add API Details for All 0 and All 5 characters.\n" + u"Please select another character or make a new one.", style=wx.ALIGN_CENTER) + self.stDisabledTip.Wrap(-1) + hintSizer.Add(self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10) self.stDisabledTip.Hide() hintSizer.AddStretchSpacer() pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5) - fgSizerInput = wx.FlexGridSizer(3, 2, 0, 0) fgSizerInput.AddGrowableCol(1) fgSizerInput.SetFlexibleDirection(wx.BOTH) @@ -498,39 +500,44 @@ class APIView (wx.Panel): pmainSizer.Add(fgSizerInput, 0, wx.EXPAND, 5) - btnSizer = wx.BoxSizer( wx.HORIZONTAL ) + btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.AddStretchSpacer() self.btnFetchCharList = wx.Button(self, wx.ID_ANY, u"Get Characters") btnSizer.Add(self.btnFetchCharList, 0, wx.ALL, 2) self.btnFetchCharList.Bind(wx.EVT_BUTTON, self.fetchCharList) - self.btnFetchSkills = wx.Button(self, wx.ID_ANY, u"Fetch Skills") - btnSizer.Add(self.btnFetchSkills, 0, wx.ALL, 2) + self.btnFetchSkills = wx.Button(self, wx.ID_ANY, u"Fetch Skills") + btnSizer.Add(self.btnFetchSkills, 0, wx.ALL, 2) self.btnFetchSkills.Bind(wx.EVT_BUTTON, self.fetchSkills) self.btnFetchSkills.Enable(False) btnSizer.AddStretchSpacer() pmainSizer.Add(btnSizer, 0, wx.EXPAND, 5) - self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString) + self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString) pmainSizer.Add(self.stStatus, 0, wx.ALL, 5) pmainSizer.AddStretchSpacer() - self.stAPITip = wx.StaticText( self, wx.ID_ANY, u"You can create a pre-defined key here (only CharacterSheet is required):", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stAPITip.Wrap( -1 ) + self.stAPITip = wx.StaticText(self, wx.ID_ANY, + u"You can create a pre-defined key here (only CharacterSheet is required):", + wx.DefaultPosition, wx.DefaultSize, 0) + self.stAPITip.Wrap(-1) - pmainSizer.Add( self.stAPITip, 0, wx.ALL, 2 ) + pmainSizer.Add(self.stAPITip, 0, wx.ALL, 2) - self.hlEveAPI = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlCreatePredefined, self.apiUrlCreatePredefined, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE ) - pmainSizer.Add( self.hlEveAPI, 0, wx.ALL, 2 ) + self.hlEveAPI = wx.HyperlinkCtrl(self, wx.ID_ANY, self.apiUrlCreatePredefined, self.apiUrlCreatePredefined, + wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE) + pmainSizer.Add(self.hlEveAPI, 0, wx.ALL, 2) - self.stAPITip2 = wx.StaticText( self, wx.ID_ANY, u"Or, you can choose an existing key from:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stAPITip2.Wrap( -1 ) - pmainSizer.Add( self.stAPITip2, 0, wx.ALL, 2 ) + self.stAPITip2 = wx.StaticText(self, wx.ID_ANY, u"Or, you can choose an existing key from:", wx.DefaultPosition, + wx.DefaultSize, 0) + self.stAPITip2.Wrap(-1) + pmainSizer.Add(self.stAPITip2, 0, wx.ALL, 2) - self.hlEveAPI2 = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlKeyList, self.apiUrlKeyList, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE ) - pmainSizer.Add( self.hlEveAPI2, 0, wx.ALL, 2 ) + self.hlEveAPI2 = wx.HyperlinkCtrl(self, wx.ID_ANY, self.apiUrlKeyList, self.apiUrlKeyList, wx.DefaultPosition, + wx.DefaultSize, wx.HL_DEFAULT_STYLE) + pmainSizer.Add(self.hlEveAPI2, 0, wx.ALL, 2) self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged) @@ -587,7 +594,7 @@ class APIView (wx.Panel): except TimeoutError, e: self.stStatus.SetLabel("Request timed out. Please check network connectivity and/or proxy settings.") except Exception, e: - self.stStatus.SetLabel("Error:\n%s"%e.message) + self.stStatus.SetLabel("Error:\n%s" % e.message) else: self.charChoice.Clear() for charName in list: @@ -611,8 +618,8 @@ class APIView (wx.Panel): except Exception, e: self.stStatus.SetLabel("Unable to retrieve %s\'s skills. Error message:\n%s" % (charName, e)) -class SaveCharacterAs(wx.Dialog): +class SaveCharacterAs(wx.Dialog): def __init__(self, parent, charID): wx.Dialog.__init__(self, parent, title="Save Character As...", size=wx.Size(300, 60)) self.charID = charID @@ -640,4 +647,3 @@ class SaveCharacterAs(wx.Dialog): event.Skip() self.Close() - \ No newline at end of file diff --git a/gui/chromeTabs.py b/gui/chromeTabs.py index be2342622..00998eb87 100644 --- a/gui/chromeTabs.py +++ b/gui/chromeTabs.py @@ -26,7 +26,6 @@ from gui.bitmapLoader import BitmapLoader from service.fit import Fit - _PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() _PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() _PageAdding, EVT_NOTEBOOK_PAGE_ADDING = wx.lib.newevent.NewEvent() @@ -527,7 +526,10 @@ class PFTabRenderer: """ self.tabRegion = wx.RegionFromBitmap(self.tabBackBitmap) self.closeBtnRegion = wx.RegionFromBitmap(self.ctabCloseBmp) - self.closeBtnRegion.Offset(self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth() / 2, (self.tabHeight - self.ctabCloseBmp.GetHeight()) / 2) + self.closeBtnRegion.Offset( + self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth() / 2, + (self.tabHeight - self.ctabCloseBmp.GetHeight()) / 2 + ) def InitColors(self): """Determines colors used for tab, based on system settings""" @@ -1277,7 +1279,12 @@ class PFTabsContainer(wx.Panel): selpos = pos if selected is not skipTab: selected.SetPosition((selpos, self.containerHeight - self.height)) - self.addButton.SetPosition((round(tabsWidth) + self.inclination * 2, self.containerHeight - self.height / 2 - self.addButton.GetHeight() / 3)) + self.addButton.SetPosition( + ( + round(tabsWidth) + self.inclination * 2, + self.containerHeight - self.height / 2 - self.addButton.GetHeight() / 3 + ) + ) def OnLeaveWindow(self, event): @@ -1302,7 +1309,12 @@ class PFTabsContainer(wx.Panel): if not self.previewTab.GetSelected(): page = self.Parent.GetPage(self.GetTabIndex(self.previewTab)) if page.Snapshot(): - self.previewWnd = PFNotebookPagePreview(self, (mposx + 3, mposy + 3), page.Snapshot(), self.previewTab.text) + self.previewWnd = PFNotebookPagePreview( + self, + (mposx + 3, mposy + 3), + page.Snapshot(), + self.previewTab.text + ) self.previewWnd.Show() event.Skip() @@ -1310,7 +1322,15 @@ class PFTabsContainer(wx.Panel): class PFNotebookPagePreview(wx.Frame): def __init__(self, parent, pos, bitmap, title): - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=pos, size=wx.DefaultSize, style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) + wx.Frame.__init__( + self, + parent, + id=wx.ID_ANY, + title=wx.EmptyString, + pos=pos, + size=wx.DefaultSize, + style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP + ) self.title = title self.bitmap = bitmap diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 2a8033b44..5e8839cac 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -1,180 +1,179 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -import wx -import logging - - -logger = logging.getLogger(__name__) - - -class ContextMenu(object): - menus = [] - _ids = [] # [wx.NewId() for x in xrange(200)] # init with decent amount - _idxid = -1 - - @classmethod - def register(cls): - ContextMenu.menus.append(cls) - - @classmethod - def getMenu(cls, selection, *fullContexts): - """ - getMenu returns a menu that is used with wx.PopupMenu. - - selection: provides a list of what was selected. If only 1 item was - selected, it's is a 1-item list or tuple. Can also be None for - contexts without selection, such as statsPane or projected view - fullContexts: a number of tuples of the following tuple: - srcContext - context were menu was spawned, eg: projectedFit, - cargoItem, etc - itemContext - usually the name of the item's category - - eg: - (('fittingModule', 'Module'), ('fittingShip', 'Ship')) - (('marketItemGroup', 'Implant'),) - (('fittingShip', 'Ship'),) - """ - cls._idxid = -1 - debug_start = len(cls._ids) - - rootMenu = wx.Menu() - rootMenu.info = {} - rootMenu.selection = (selection,) if not hasattr(selection, "__iter__") else selection - empty = True - for i, fullContext in enumerate(fullContexts): - amount = 0 - srcContext = fullContext[0] - try: - itemContext = fullContext[1] - except IndexError: - itemContext = None - for menuHandler in cls.menus: - # loop through registered menus - m = menuHandler() - if m.display(srcContext, selection): - amount += 1 - texts = m.getText(itemContext, selection) - if isinstance(texts, basestring): - texts = (texts,) - - bitmap = m.getBitmap(srcContext, selection) - multiple = not isinstance(bitmap, wx.Bitmap) - for it, text in enumerate(texts): - id = cls.nextID() - rootItem = wx.MenuItem(rootMenu, id, text) - rootMenu.info[id] = (m, fullContext, it) - - sub = m.getSubMenu(srcContext, selection, rootMenu, it, rootItem) - - if sub is None: - # if there is no sub menu, bind the handler to the rootItem - rootMenu.Bind(wx.EVT_MENU, cls.handler, rootItem) - elif sub: - # If sub exists and is not False, set submenu. - # Sub might return False when we have a mix of - # single menu items and submenus (see: damage profile - # context menu) - # - # If there is a submenu, it is expected that the sub - # logic take care of it's own bindings, including for - # any single root items. No binding is done here - # - # It is important to remember that when binding sub - # menu items, the menu to bind to depends on platform. - # Windows should bind to rootMenu, and all other - # platforms should bind to sub menu. See existing - # implementations for examples. - rootItem.SetSubMenu(sub) - - if bitmap is not None: - if multiple: - bp = bitmap[it] - if bp: - rootItem.SetBitmap(bp) - else: - rootItem.SetBitmap(bitmap) - - rootMenu.AppendItem(rootItem) - - empty = False - - if amount > 0 and i != len(fullContexts) - 1: - rootMenu.AppendSeparator() - - debug_end = len(cls._ids) - if (debug_end - debug_start): - logger.debug("%d new IDs created for this menu" % (debug_end - debug_start)) - - return rootMenu if empty is False else None - - @classmethod - def handler(cls, event): - menu = event.EventObject - stuff = menu.info.get(event.Id) - if stuff is not None: - menuHandler, context, i = stuff - selection = menu.selection - if not hasattr(selection, "__iter__"): - selection = (selection,) - - menuHandler.activate(context, selection, i) - else: - event.Skip() - - def display(self, context, selection): - raise NotImplementedError() - - def activate(self, fullContext, selection, i): - return None - - def getSubMenu(self, context, selection, rootMenu, i, pitem): - return None - - @classmethod - def nextID(cls): - """ - Fetches an ID from the pool of IDs allocated to Context Menu. - If we don't have enough ID's to fulfill request, create new - ID and add it to the pool. - - See GH Issue #589 - """ - cls._idxid += 1 - - if cls._idxid >= len(cls._ids): # We don't ahve an ID for this index, create one - cls._ids.append(wx.NewId()) - - return cls._ids[cls._idxid] - - def getText(self, context, selection): - """ - getText should be implemented in child classes, and should return either - a string that will make up a menu item label or a list of strings which - will make numerous menu items. - - These menu items will be added to the root menu - """ - raise NotImplementedError() - - def getBitmap(self, context, selection): - return None - - -from gui.builtinContextMenus import * # noqa +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +import wx +import logging + +logger = logging.getLogger(__name__) + + +class ContextMenu(object): + menus = [] + _ids = [] # [wx.NewId() for x in xrange(200)] # init with decent amount + _idxid = -1 + + @classmethod + def register(cls): + ContextMenu.menus.append(cls) + + @classmethod + def getMenu(cls, selection, *fullContexts): + """ + getMenu returns a menu that is used with wx.PopupMenu. + + selection: provides a list of what was selected. If only 1 item was + selected, it's is a 1-item list or tuple. Can also be None for + contexts without selection, such as statsPane or projected view + fullContexts: a number of tuples of the following tuple: + srcContext - context were menu was spawned, eg: projectedFit, + cargoItem, etc + itemContext - usually the name of the item's category + + eg: + (('fittingModule', 'Module'), ('fittingShip', 'Ship')) + (('marketItemGroup', 'Implant'),) + (('fittingShip', 'Ship'),) + """ + cls._idxid = -1 + debug_start = len(cls._ids) + + rootMenu = wx.Menu() + rootMenu.info = {} + rootMenu.selection = (selection,) if not hasattr(selection, "__iter__") else selection + empty = True + for i, fullContext in enumerate(fullContexts): + amount = 0 + srcContext = fullContext[0] + try: + itemContext = fullContext[1] + except IndexError: + itemContext = None + for menuHandler in cls.menus: + # loop through registered menus + m = menuHandler() + if m.display(srcContext, selection): + amount += 1 + texts = m.getText(itemContext, selection) + if isinstance(texts, basestring): + texts = (texts,) + + bitmap = m.getBitmap(srcContext, selection) + multiple = not isinstance(bitmap, wx.Bitmap) + for it, text in enumerate(texts): + id = cls.nextID() + rootItem = wx.MenuItem(rootMenu, id, text) + rootMenu.info[id] = (m, fullContext, it) + + sub = m.getSubMenu(srcContext, selection, rootMenu, it, rootItem) + + if sub is None: + # if there is no sub menu, bind the handler to the rootItem + rootMenu.Bind(wx.EVT_MENU, cls.handler, rootItem) + elif sub: + # If sub exists and is not False, set submenu. + # Sub might return False when we have a mix of + # single menu items and submenus (see: damage profile + # context menu) + # + # If there is a submenu, it is expected that the sub + # logic take care of it's own bindings, including for + # any single root items. No binding is done here + # + # It is important to remember that when binding sub + # menu items, the menu to bind to depends on platform. + # Windows should bind to rootMenu, and all other + # platforms should bind to sub menu. See existing + # implementations for examples. + rootItem.SetSubMenu(sub) + + if bitmap is not None: + if multiple: + bp = bitmap[it] + if bp: + rootItem.SetBitmap(bp) + else: + rootItem.SetBitmap(bitmap) + + rootMenu.AppendItem(rootItem) + + empty = False + + if amount > 0 and i != len(fullContexts) - 1: + rootMenu.AppendSeparator() + + debug_end = len(cls._ids) + if (debug_end - debug_start): + logger.debug("%d new IDs created for this menu" % (debug_end - debug_start)) + + return rootMenu if empty is False else None + + @classmethod + def handler(cls, event): + menu = event.EventObject + stuff = menu.info.get(event.Id) + if stuff is not None: + menuHandler, context, i = stuff + selection = menu.selection + if not hasattr(selection, "__iter__"): + selection = (selection,) + + menuHandler.activate(context, selection, i) + else: + event.Skip() + + def display(self, context, selection): + raise NotImplementedError() + + def activate(self, fullContext, selection, i): + return None + + def getSubMenu(self, context, selection, rootMenu, i, pitem): + return None + + @classmethod + def nextID(cls): + """ + Fetches an ID from the pool of IDs allocated to Context Menu. + If we don't have enough ID's to fulfill request, create new + ID and add it to the pool. + + See GH Issue #589 + """ + cls._idxid += 1 + + if cls._idxid >= len(cls._ids): # We don't ahve an ID for this index, create one + cls._ids.append(wx.NewId()) + + return cls._ids[cls._idxid] + + def getText(self, context, selection): + """ + getText should be implemented in child classes, and should return either + a string that will make up a menu item label or a list of strings which + will make numerous menu items. + + These menu items will be added to the root menu + """ + raise NotImplementedError() + + def getBitmap(self, context, selection): + return None + + +from gui.builtinContextMenus import * # noqa diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index 659e01b5a..bb5a6e601 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -30,7 +30,8 @@ class CopySelectDialog(wx.Dialog): copyFormatMultiBuy = 5 def __init__(self, parent): - wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Select a format", size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Select a format", size=(-1, -1), + style=wx.DEFAULT_DIALOG_STYLE) mainSizer = wx.BoxSizer(wx.VERTICAL) copyFormats = [u"EFT", u"EFT (Implants)", u"XML", u"DNA", u"CREST", u"MultiBuy"] @@ -40,7 +41,8 @@ class CopySelectDialog(wx.Dialog): CopySelectDialog.copyFormatDna: u"A one-line text format", CopySelectDialog.copyFormatCrest: u"A JSON format used for EVE CREST", CopySelectDialog.copyFormatMultiBuy: u"MultiBuy text format"} - selector = wx.RadioBox(self, wx.ID_ANY, label=u"Copy to the clipboard using:", choices=copyFormats, style=wx.RA_SPECIFY_ROWS) + selector = wx.RadioBox(self, wx.ID_ANY, label=u"Copy to the clipboard using:", choices=copyFormats, + style=wx.RA_SPECIFY_ROWS) selector.Bind(wx.EVT_RADIOBOX, self.Selected) for format, tooltip in copyFormatTooltips.iteritems(): selector.SetItemToolTip(format, tooltip) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 585e0f7ec..aa1fdc595 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -16,9 +16,9 @@ import gui.globalEvents as GE class CrestFittings(wx.Frame): - def __init__(self, parent): - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Browse EVE Fittings", pos=wx.DefaultPosition, size=wx.Size( 550,450 ), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Browse EVE Fittings", pos=wx.DefaultPosition, + size=wx.Size(550, 450), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) @@ -26,42 +26,43 @@ class CrestFittings(wx.Frame): mainSizer = wx.BoxSizer(wx.VERTICAL) sCrest = Crest.getInstance() - characterSelectSizer = wx.BoxSizer( wx.HORIZONTAL ) + characterSelectSizer = wx.BoxSizer(wx.HORIZONTAL) if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s"%sCrest.implicitCharacter.name, wx.DefaultPosition, wx.DefaultSize) - self.stLogged.Wrap( -1 ) + self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s" % sCrest.implicitCharacter.name, + wx.DefaultPosition, wx.DefaultSize) + self.stLogged.Wrap(-1) - characterSelectSizer.Add( self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + characterSelectSizer.Add(self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) else: self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) - characterSelectSizer.Add( self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + characterSelectSizer.Add(self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) self.updateCharList() - self.fetchBtn = wx.Button( self, wx.ID_ANY, u"Fetch Fits", wx.DefaultPosition, wx.DefaultSize, 5 ) - characterSelectSizer.Add( self.fetchBtn, 0, wx.ALL, 5 ) - mainSizer.Add( characterSelectSizer, 0, wx.EXPAND, 5 ) + self.fetchBtn = wx.Button(self, wx.ID_ANY, u"Fetch Fits", wx.DefaultPosition, wx.DefaultSize, 5) + characterSelectSizer.Add(self.fetchBtn, 0, wx.ALL, 5) + mainSizer.Add(characterSelectSizer, 0, wx.EXPAND, 5) - self.sl = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.sl, 0, wx.EXPAND |wx.ALL, 5 ) + self.sl = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.sl, 0, wx.EXPAND | wx.ALL, 5) - contentSizer = wx.BoxSizer( wx.HORIZONTAL ) - browserSizer = wx.BoxSizer( wx.VERTICAL ) + contentSizer = wx.BoxSizer(wx.HORIZONTAL) + browserSizer = wx.BoxSizer(wx.VERTICAL) self.fitTree = FittingsTreeView(self) - browserSizer.Add( self.fitTree, 1, wx.ALL|wx.EXPAND, 5 ) - contentSizer.Add( browserSizer, 1, wx.EXPAND, 0 ) - fitSizer = wx.BoxSizer( wx.VERTICAL ) + browserSizer.Add(self.fitTree, 1, wx.ALL | wx.EXPAND, 5) + contentSizer.Add(browserSizer, 1, wx.EXPAND, 0) + fitSizer = wx.BoxSizer(wx.VERTICAL) self.fitView = FitView(self) - fitSizer.Add( self.fitView, 1, wx.ALL|wx.EXPAND, 5 ) + fitSizer.Add(self.fitView, 1, wx.ALL | wx.EXPAND, 5) - btnSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.importBtn = wx.Button( self, wx.ID_ANY, u"Import to pyfa", wx.DefaultPosition, wx.DefaultSize, 5 ) - self.deleteBtn = wx.Button( self, wx.ID_ANY, u"Delete from EVE", wx.DefaultPosition, wx.DefaultSize, 5 ) - btnSizer.Add( self.importBtn, 1, wx.ALL, 5 ) - btnSizer.Add( self.deleteBtn, 1, wx.ALL, 5 ) - fitSizer.Add( btnSizer, 0, wx.EXPAND ) + btnSizer = wx.BoxSizer(wx.HORIZONTAL) + self.importBtn = wx.Button(self, wx.ID_ANY, u"Import to pyfa", wx.DefaultPosition, wx.DefaultSize, 5) + self.deleteBtn = wx.Button(self, wx.ID_ANY, u"Delete from EVE", wx.DefaultPosition, wx.DefaultSize, 5) + btnSizer.Add(self.importBtn, 1, wx.ALL, 5) + btnSizer.Add(self.deleteBtn, 1, wx.ALL, 5) + fitSizer.Add(btnSizer, 0, wx.EXPAND) contentSizer.Add(fitSizer, 1, wx.EXPAND, 0) mainSizer.Add(contentSizer, 1, wx.EXPAND, 5) @@ -104,12 +105,12 @@ class CrestFittings(wx.Frame): self.charChoice.SetSelection(0) def updateCacheStatus(self, event): - t = time.gmtime(self.cacheTime-time.time()) + t = time.gmtime(self.cacheTime - time.time()) if t < 0: self.cacheTimer.Stop() else: sTime = time.strftime("%H:%M:%S", t) - self.statusbar.SetStatusText("Cached for %s"%sTime, 0) + self.statusbar.SetStatusText("Cached for %s" % sTime, 0) def ssoLogout(self, event): if event.type == CrestModes.IMPLICIT: @@ -163,8 +164,8 @@ class CrestFittings(wx.Frame): data = json.loads(self.fitTree.fittingsTreeCtrl.GetPyData(selection)) dlg = wx.MessageDialog(self, - "Do you really want to delete %s (%s) from EVE?"%(data['name'], data['ship']['name']), - "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + "Do you really want to delete %s (%s) from EVE?" % (data['name'], data['ship']['name']), + "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) if dlg.ShowModal() == wx.ID_YES: try: @@ -174,9 +175,9 @@ class CrestFittings(wx.Frame): class ExportToEve(wx.Frame): - def __init__(self, parent): - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Export fit to EVE", pos=wx.DefaultPosition, size=(wx.Size(350,100)), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Export fit to EVE", pos=wx.DefaultPosition, + size=(wx.Size(350, 100)), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) self.mainFrame = parent self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) @@ -186,20 +187,21 @@ class ExportToEve(wx.Frame): hSizer = wx.BoxSizer(wx.HORIZONTAL) if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s"%sCrest.implicitCharacter.name, wx.DefaultPosition, wx.DefaultSize) - self.stLogged.Wrap( -1 ) + self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s" % sCrest.implicitCharacter.name, + wx.DefaultPosition, wx.DefaultSize) + self.stLogged.Wrap(-1) - hSizer.Add( self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + hSizer.Add(self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) else: self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) - hSizer.Add( self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + hSizer.Add(self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) self.updateCharList() self.charChoice.SetSelection(0) - self.exportBtn = wx.Button( self, wx.ID_ANY, u"Export Fit", wx.DefaultPosition, wx.DefaultSize, 5 ) - hSizer.Add( self.exportBtn, 0, wx.ALL, 5 ) + self.exportBtn = wx.Button(self, wx.ID_ANY, u"Export Fit", wx.DefaultPosition, wx.DefaultSize, 5) + hSizer.Add(self.exportBtn, 0, wx.ALL, 5) - mainSizer.Add( hSizer, 0, wx.EXPAND, 5 ) + mainSizer.Add(hSizer, 0, wx.EXPAND, 5) self.exportBtn.Bind(wx.EVT_BUTTON, self.exportFitting) @@ -271,7 +273,7 @@ class ExportToEve(wx.Frame): data = sFit.exportCrest(fitID) res = sCrest.postFitting(self.getActiveCharacter(), data) - self.statusbar.SetStatusText("%d: %s"%(res.status_code, res.reason), 0) + self.statusbar.SetStatusText("%d: %s" % (res.status_code, res.reason), 0) try: text = json.loads(res.text) self.statusbar.SetStatusText(text['message'], 1) @@ -282,40 +284,40 @@ class ExportToEve(wx.Frame): class CrestMgmt(wx.Dialog): - - def __init__( self, parent ): - wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "CREST Character Management", pos = wx.DefaultPosition, size = wx.Size( 550,250 ), style = wx.DEFAULT_DIALOG_STYLE ) + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="CREST Character Management", pos=wx.DefaultPosition, + size=wx.Size(550, 250), style=wx.DEFAULT_DIALOG_STYLE) self.mainFrame = parent - mainSizer = wx.BoxSizer( wx.HORIZONTAL ) + mainSizer = wx.BoxSizer(wx.HORIZONTAL) - self.lcCharacters = wx.ListCtrl( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT) + self.lcCharacters = wx.ListCtrl(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT) self.lcCharacters.InsertColumn(0, heading='Character') self.lcCharacters.InsertColumn(1, heading='Refresh Token') self.popCharList() - mainSizer.Add( self.lcCharacters, 1, wx.ALL|wx.EXPAND, 5 ) + mainSizer.Add(self.lcCharacters, 1, wx.ALL | wx.EXPAND, 5) - btnSizer = wx.BoxSizer( wx.VERTICAL ) + btnSizer = wx.BoxSizer(wx.VERTICAL) - self.addBtn = wx.Button( self, wx.ID_ANY, u"Add Character", wx.DefaultPosition, wx.DefaultSize, 0 ) - btnSizer.Add( self.addBtn, 0, wx.ALL | wx.EXPAND, 5 ) + self.addBtn = wx.Button(self, wx.ID_ANY, u"Add Character", wx.DefaultPosition, wx.DefaultSize, 0) + btnSizer.Add(self.addBtn, 0, wx.ALL | wx.EXPAND, 5) - self.deleteBtn = wx.Button( self, wx.ID_ANY, u"Revoke Character", wx.DefaultPosition, wx.DefaultSize, 0 ) - btnSizer.Add( self.deleteBtn, 0, wx.ALL | wx.EXPAND, 5 ) + self.deleteBtn = wx.Button(self, wx.ID_ANY, u"Revoke Character", wx.DefaultPosition, wx.DefaultSize, 0) + btnSizer.Add(self.deleteBtn, 0, wx.ALL | wx.EXPAND, 5) - mainSizer.Add( btnSizer, 0, wx.EXPAND, 5 ) + mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) self.addBtn.Bind(wx.EVT_BUTTON, self.addChar) self.deleteBtn.Bind(wx.EVT_BUTTON, self.delChar) self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoLogin) - self.SetSizer( mainSizer ) + self.SetSizer(mainSizer) self.Layout() - self.Centre( wx.BOTH ) + self.Centre(wx.BOTH) def ssoLogin(self, event): self.popCharList() diff --git a/gui/display.py b/gui/display.py index 2aebc1db7..0d9e0308f 100644 --- a/gui/display.py +++ b/gui/display.py @@ -70,9 +70,8 @@ class Display(wx.ListCtrl): self.imageListBase = self.imageList.ImageCount - -# Override native HitTestSubItem (doesn't work as it should on GTK) -# Source: ObjectListView + # Override native HitTestSubItem (doesn't work as it should on GTK) + # Source: ObjectListView def HitTestSubItem(self, pt): """ @@ -282,7 +281,7 @@ class Display(wx.ListCtrl): self.SetColumnWidth(i, headerWidth) else: self.SetColumnWidth(i, col.size) - # self.Thaw() + # self.Thaw() def update(self, stuff): self.populate(stuff) diff --git a/gui/fighterView.py b/gui/fighterView.py index 56aeb73b1..524c07976 100644 --- a/gui/fighterView.py +++ b/gui/fighterView.py @@ -28,24 +28,25 @@ from eos.types import Slot from gui.contextMenu import ContextMenu from service.fit import Fit -class FighterViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t +class FighterViewDrop(wx.PyDropTarget): + def __init__(self, dropFn): + wx.PyDropTarget.__init__(self) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) + + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t class FighterView(wx.Panel): def __init__(self, parent): - wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL ) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.labels = ["Light", "Heavy", "Support"] @@ -55,7 +56,7 @@ class FighterView(wx.Panel): mainSizer.Add(self.fighterDisplay, 1, wx.EXPAND, 0) textSizer = wx.BoxSizer(wx.HORIZONTAL) - textSizer.AddSpacer(( 0, 0), 1, wx.EXPAND, 5) + textSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) for x in self.labels: lbl = wx.StaticText(self, wx.ID_ANY, x.capitalize()) @@ -74,10 +75,9 @@ class FighterView(wx.Panel): mainSizer.Add(textSizer, 0, wx.EXPAND, 5) - self.SetSizer( mainSizer ) + self.SetSizer(mainSizer) self.SetAutoLayout(True) - self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) def fitChanged(self, event): @@ -90,7 +90,8 @@ class FighterView(wx.Panel): slot = getattr(Slot, "F_{}".format(x.upper())) used = fit.getSlotsUsed(slot) total = fit.getNumSlots(slot) - color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings_GetColour( + wx.SYS_COLOUR_WINDOWTEXT) lbl = getattr(self, "label%sUsed" % x.capitalize()) lbl.SetLabel(str(int(used))) @@ -105,15 +106,15 @@ class FighterView(wx.Panel): class FighterDisplay(d.Display): DEFAULT_COLS = ["State", - #"Base Icon", + # "Base Icon", "Base Name", # "prop:droneDps,droneBandwidth", - #"Max Range", - #"Miscellanea", + # "Max Range", + # "Miscellanea", "attr:maxVelocity", "Fighter Abilities" - #"Price", - ] + # "Price", + ] def __init__(self, parent): d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL | wx.BORDER_NONE) @@ -131,12 +132,11 @@ class FighterDisplay(d.Display): self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - if "__WXGTK__" in wx.PlatformInfo: + if "__WXGTK__" in wx.PlatformInfo: self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) - self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) self.SetDropTarget(FighterViewDrop(self.handleDragDrop)) @@ -183,7 +183,7 @@ class FighterDisplay(d.Display): row = event.GetIndex() if row != -1: data = wx.PyTextDataObject() - data.SetText("fighter:"+str(row)) + data.SetText("fighter:" + str(row)) dropSource = wx.DropSource(self) dropSource.SetData(data) @@ -229,7 +229,7 @@ class FighterDisplay(d.Display): self.Parent.Parent.Parent.DisablePage(self.Parent, not fit) - #Clear list and get out if current fitId is None + # Clear list and get out if current fitId is None if event.fitID is None and self.lastFitId is not None: self.DeleteAllItems() self.lastFitId = None @@ -257,7 +257,6 @@ class FighterDisplay(d.Display): self.update(stuff) event.Skip() - def addItem(self, event): sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() diff --git a/gui/fleetBrowser.py b/gui/fleetBrowser.py index 1d3c6b38c..ab50e58ca 100644 --- a/gui/fleetBrowser.py +++ b/gui/fleetBrowser.py @@ -1,448 +1,449 @@ -import wx -from wx.lib.buttons import GenBitmapButton - -import service.fleet -import gui.mainFrame -import gui.utils.colorUtils as colorUtils -import gui.sfBrowserItem as SFItem -from gui.bitmapLoader import BitmapLoader -from gui.PFListPane import PFListPane -from gui.utils.drawUtils import GetPartialText - - -FleetSelected, EVT_FLEET_SELECTED = wx.lib.newevent.NewEvent() -FleetRenamed, EVT_FLEET_RENAMED = wx.lib.newevent.NewEvent() -FleetRemoved, EVT_FLEET_REMOVED = wx.lib.newevent.NewEvent() -FleetItemSelect, EVT_FLEET_ITEM_SELECT = wx.lib.newevent.NewEvent() -FleetItemDelete, EVT_FLEET_ITEM_DELETE = wx.lib.newevent.NewEvent() -FleetItemNew, EVT_FLEET_ITEM_NEW = wx.lib.newevent.NewEvent() -FleetItemCopy, EVT_FLEET_ITEM_COPY = wx.lib.newevent.NewEvent() -FleetItemRename, EVT_FLEET_ITEM_RENAME = wx.lib.newevent.NewEvent() - - -class FleetBrowser(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - - self.sFleet = service.fleet.Fleet.getInstance() - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - mainSizer = wx.BoxSizer(wx.VERTICAL) - - self.hpane = FleetBrowserHeader(self) - mainSizer.Add(self.hpane, 0, wx.EXPAND) - - self.m_sl2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - mainSizer.Add(self.m_sl2, 0, wx.EXPAND, 0) - - self.fleetItemContainer = PFFleetItemContainer(self) - - mainSizer.Add(self.fleetItemContainer, 1, wx.EXPAND) - - self.SetSizer(mainSizer) - self.Layout() - - self.filter = "" - self.fleetIDMustEditName = -1 - - self.Bind(wx.EVT_SIZE, self.SizeRefreshList) - - self.Bind(EVT_FLEET_ITEM_NEW, self.AddNewFleetItem) - self.Bind(EVT_FLEET_ITEM_SELECT, self.SelectFleetItem) - self.Bind(EVT_FLEET_ITEM_DELETE, self.DeleteFleetItem) - self.Bind(EVT_FLEET_ITEM_COPY, self.CopyFleetItem) - self.Bind(EVT_FLEET_ITEM_RENAME, self.RenameFleetItem) - - self.PopulateFleetList() - - def AddNewFleetItem(self, event): - fleetName = event.fleetName - newFleet = self.sFleet.addFleet() - self.sFleet.renameFleet(newFleet, fleetName) - - self.fleetIDMustEditName = newFleet.ID - self.AddItem(newFleet.ID, newFleet.name, newFleet.count()) - - def SelectFleetItem(self, event): - fleetID = event.fleetID - self.fleetItemContainer.SelectWidgetByFleetID(fleetID) - wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleetID)) - - def CopyFleetItem(self, event): - fleetID = event.fleetID - fleet = self.sFleet.copyFleetByID(fleetID) - - fleetName = fleet.name + " Copy" - self.sFleet.renameFleet(fleet, fleetName) - - self.fleetIDMustEditName = fleet.ID - self.AddItem(fleet.ID, fleet.name, fleet.count()) - - self.fleetItemContainer.SelectWidgetByFleetID(fleet.ID) - wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleet.ID)) - - def RenameFleetItem(self, event): - fleetID = event.fleetID - fleet = self.sFleet.getFleetByID(fleetID) - - newFleetName = event.fleetName - - self.sFleet.renameFleet(fleet, newFleetName) - wx.PostEvent(self.mainFrame, FleetRenamed(fleetID=fleet.ID)) - - def DeleteFleetItem(self, event): - self.sFleet.deleteFleetByID(event.fleetID) - self.PopulateFleetList() - wx.PostEvent(self.mainFrame, FleetRemoved(fleetID=event.fleetID)) - - def AddItem(self, ID, name, count): - self.fleetItemContainer.AddWidget(FleetItem(self, ID, name, count)) - widget = self.fleetItemContainer.GetWidgetByFleetID(ID) - self.fleetItemContainer.RefreshList(True) - self.fleetItemContainer.ScrollChildIntoView(widget) - wx.PostEvent(self, FleetItemSelect(fleetID=ID)) - - def PopulateFleetList(self): - self.Freeze() - filter_ = self.filter - self.fleetItemContainer.RemoveAllChildren() - fleetList = self.sFleet.getFleetList() - for fleetID, fleetName, fleetCount in fleetList: - if fleetName.lower().find(filter_.lower()) != -1: - self.fleetItemContainer.AddWidget(FleetItem(self, fleetID, fleetName, fleetCount)) - self.fleetItemContainer.RefreshList() - self.Thaw() - - def SetFilter(self, filter): - self.filter = filter - - def SizeRefreshList(self, event): - ewidth, eheight = event.GetSize() - self.Layout() - self.fleetItemContainer.Layout() - self.fleetItemContainer.RefreshList(True) - event.Skip() - - -class FleetBrowserHeader(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 24), style=wx.TAB_TRAVERSAL) - self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) - - self.newBmp = BitmapLoader.getBitmap("fit_add_small", "gui") - bmpSize = (16, 16) - - mainSizer = wx.BoxSizer(wx.HORIZONTAL) - - if 'wxMac' in wx.PlatformInfo: - bgcolour = wx.Colour(0, 0, 0, 0) - else: - bgcolour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) - - self.fbNewFleet = PFGenBitmapButton(self, wx.ID_ANY, self.newBmp, wx.DefaultPosition, bmpSize, wx.BORDER_NONE) - mainSizer.Add(self.fbNewFleet, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, 5) - self.fbNewFleet.SetBackgroundColour(bgcolour) - - self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL) - mainSizer.Add(self.sl1, 0, wx.EXPAND | wx.LEFT, 5) - - self.tcFilter = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) - mainSizer.Add(self.tcFilter, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - - self.stStatus = wx.StaticText(self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0) - self.stStatus.Wrap(-1) - mainSizer.Add(self.stStatus, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - - self.SetSizer(mainSizer) - self.Layout() - - self.fbNewFleet.Bind(wx.EVT_ENTER_WINDOW, self.fbNewEnterWindow) - self.fbNewFleet.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) - self.fbNewFleet.Bind(wx.EVT_BUTTON, self.OnNewFleetItem) - - self.tcFilter.Bind(wx.EVT_TEXT, self.OnFilterText) - - self.tcFilter.Bind(wx.EVT_ENTER_WINDOW, self.fbFilterEnterWindow) - self.tcFilter.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) - - def OnFilterText(self, event): - filter = self.tcFilter.GetValue() - self.Parent.SetFilter(filter) - self.Parent.PopulateFleetList() - event.Skip() - - def OnNewFleetItem(self, event): - wx.PostEvent(self.Parent, FleetItemNew(fleetName="New Fleet")) - - def fbNewEnterWindow(self, event): - self.stStatus.SetLabel("New fleet") - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) - event.Skip() - - def fbHItemLeaveWindow(self, event): - self.stStatus.SetLabel("") - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) - event.Skip() - - def fbFilterEnterWindow(self, event): - self.stStatus.SetLabel("Filter list") - event.Skip() - - -class PFFleetItemContainer(PFListPane): - def __init__(self, parent): - PFListPane.__init__(self, parent) - self.selectedWidget = -1 - self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) - - def IsWidgetSelectedByContext(self, widget): - if self.GetWidgetList()[widget].IsSelected(): - return True - return False - - def GetWidgetIndex(self, widgetWnd): - return self.GetWidgetList().index(widgetWnd) - - def GetWidgetByFleetID(self, fleetID): - for widget in self.GetWidgetList(): - if widget.fleetID == fleetID: - return widget - return None - - def SelectWidget(self, widgetWnd): - wlist = self.GetWidgetList() - if self.selectedWidget != -1: - wlist[self.selectedWidget].SetSelected(False) - wlist[self.selectedWidget].Refresh() - windex = self.GetWidgetIndex(widgetWnd) - wlist[windex].SetSelected(True) - wlist[windex].Refresh() - self.selectedWidget = windex - - def SelectWidgetByFleetID(self, fleetID): - widgetWnd = self.GetWidgetByFleetID(fleetID) - if widgetWnd: - self.SelectWidget(widgetWnd) - - def RemoveWidget(self, child): - child.Destroy() - self.selectedWidget = -1 - self._wList.remove(child) - - def RemoveAllChildren(self): - for widget in self._wList: - widget.Destroy() - - self.selectedWidget = -1 - self._wList = [] - - def OnLeftUp(self, event): - event.Skip() - - -class FleetItem(SFItem.SFBrowserItem): - def __init__(self, parent, fleetID, fleetName, fleetCount, - id=wx.ID_ANY, pos=wx.DefaultPosition, - size=(0, 40), style=0): - SFItem.SFBrowserItem.__init__(self, parent, size=size) - - self.fleetBrowser = self.Parent - self.fleetID = fleetID - self.fleetName = fleetName - self.fleetCount = fleetCount - - self.padding = 4 - - self.fontBig = wx.FontFromPixelSize((0, 15), wx.SWISS, wx.NORMAL, wx.BOLD, False) - self.fontNormal = wx.FontFromPixelSize((0, 14), wx.SWISS, wx.NORMAL, wx.NORMAL, False) - self.fontSmall = wx.FontFromPixelSize((0, 12), wx.SWISS, wx.NORMAL, wx.NORMAL, False) - - self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") - self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") - self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small", "gui") - self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") - self.fleetBmp = BitmapLoader.getBitmap("fleet_item_big", "gui") - - fleetImg = self.fleetBmp.ConvertToImage() - fleetImg = fleetImg.Blur(2) - - if not fleetImg.HasAlpha(): - fleetImg.InitAlpha() - - fleetImg = fleetImg.AdjustChannels(1, 1, 1, 0.5) - self.fleetEffBmp = wx.BitmapFromImage(fleetImg) - - self.toolbar.AddButton(self.copyBmp, "Copy", self.CopyFleetCB) - self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.RenameFleetCB) - self.toolbar.AddButton(self.deleteBmp, "Delete", self.DeleteFleetCB) - - self.editWidth = 150 - self.tcFleetName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fleetName, wx.DefaultPosition, (self.editWidth, -1), wx.TE_PROCESS_ENTER) - - if self.fleetBrowser.fleetIDMustEditName != self.fleetID: - self.tcFleetName.Show(False) - else: - self.tcFleetName.SetFocus() - self.tcFleetName.SelectAll() - self.fleetBrowser.fleetIDMustEditName = -1 - self.renameBtn.SetBitmap(self.acceptBmp) - self.selected = True - - self.tcFleetName.Bind(wx.EVT_KILL_FOCUS, self.OnEditLostFocus) - self.tcFleetName.Bind(wx.EVT_TEXT_ENTER, self.RenameFleet) - self.tcFleetName.Bind(wx.EVT_KEY_DOWN, self.EditCheckEsc) - - self.animCount = 0 - - def MouseLeftUp(self, event): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - else: - wx.PostEvent(self.fleetBrowser, FleetItemSelect(fleetID=self.fleetID)) - - def CopyFleetCB(self): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - return - - wx.PostEvent(self.fleetBrowser, FleetItemCopy(fleetID=self.fleetID)) - - def RenameFleetCB(self): - - if self.tcFleetName.IsShown(): - - self.RenameFleet(None) - self.RestoreEditButton() - - else: - self.tcFleetName.SetValue(self.fleetName) - self.tcFleetName.Show() - - self.renameBtn.SetBitmap(self.acceptBmp) - self.Refresh() - - self.tcFleetName.SetFocus() - self.tcFleetName.SelectAll() - - self.Refresh() - - def RenameFleet(self, event): - - newFleetName = self.tcFleetName.GetValue() - self.fleetName = newFleetName - - self.tcFleetName.Show(False) - - wx.PostEvent(self.fleetBrowser, FleetItemRename(fleetID=self.fleetID, fleetName=self.fleetName)) - self.Refresh() - - def DeleteFleetCB(self): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - return - wx.PostEvent(self.fleetBrowser, FleetItemDelete(fleetID=self.fleetID)) - - def RestoreEditButton(self): - self.tcFleetName.Show(False) - self.renameBtn.SetBitmap(self.renameBmp) - self.Refresh() - - def OnEditLostFocus(self, event): - self.RestoreEditButton() - self.Refresh() - - def EditCheckEsc(self, event): - if event.GetKeyCode() == wx.WXK_ESCAPE: - self.RestoreEditButton() - else: - event.Skip() - - def IsSelected(self): - return self.selected - - def UpdateElementsPos(self, mdc): - rect = self.GetRect() - - self.toolbarx = rect.width - self.toolbar.GetWidth() - self.padding - self.toolbary = (rect.height - self.toolbar.GetHeight()) / 2 - - self.toolbarx = self.toolbarx + self.animCount - - self.fleetBmpx = self.padding + (rect.height - self.fleetBmp.GetWidth()) / 2 - self.fleetBmpy = (rect.height - self.fleetBmp.GetHeight()) / 2 - - self.fleetBmpx -= self.animCount - - self.textStartx = self.fleetBmpx + self.fleetBmp.GetWidth() + self.padding - - self.fleetNamey = (rect.height - self.fleetBmp.GetHeight()) / 2 - - mdc.SetFont(self.fontBig) - wtext, htext = mdc.GetTextExtent(self.fleetName) - - self.fleetCounty = self.fleetNamey + htext - - mdc.SetFont(self.fontSmall) - - wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) - - self.thoverx = self.toolbarx - self.padding - wlabel - self.thovery = (rect.height - hlabel) / 2 - self.thoverw = wlabel - - def DrawItem(self, mdc): - # rect = self.GetRect() - - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) - - mdc.SetTextForeground(textColor) - - self.UpdateElementsPos(mdc) - - self.toolbar.SetPosition((self.toolbarx, self.toolbary)) - mdc.DrawBitmap(self.fleetEffBmp, self.fleetBmpx + 3, self.fleetBmpy + 2) - mdc.DrawBitmap(self.fleetBmp, self.fleetBmpx, self.fleetBmpy) - - mdc.SetFont(self.fontNormal) - - suffix = "%d ships" % self.fleetCount if self.fleetCount > 1 else "%d ship" % self.fleetCount if self.fleetCount == 1 else "No ships" - fleetCount = "Fleet size: %s" % suffix - fleetCount = GetPartialText(mdc, fleetCount, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) - - mdc.DrawText(fleetCount, self.textStartx, self.fleetCounty) - - mdc.SetFont(self.fontSmall) - mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) - - mdc.SetFont(self.fontBig) - - pfname = GetPartialText(mdc, self.fleetName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) - mdc.DrawText(pfname, self.textStartx, self.fleetNamey) - - if self.tcFleetName.IsShown(): - self.AdjustControlSizePos(self.tcFleetName, self.textStartx, self.toolbarx - self.editWidth - self.padding) - - def AdjustControlSizePos(self, editCtl, start, end): - fnEditSize = editCtl.GetSize() - wSize = self.GetSize() - fnEditPosX = end - fnEditPosY = (wSize.height - fnEditSize.height) / 2 - if fnEditPosX < start: - editCtl.SetSize((self.editWidth + fnEditPosX - start, -1)) - editCtl.SetPosition((start, fnEditPosY)) - else: - editCtl.SetSize((self.editWidth, -1)) - editCtl.SetPosition((fnEditPosX, fnEditPosY)) - - -class PFGenBitmapButton(GenBitmapButton): - def __init__(self, parent, id, bitmap, pos, size, style): - GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style) - self.bgcolor = wx.Brush(wx.WHITE) - - def SetBackgroundColour(self, color): - self.bgcolor = wx.Brush(color) - - def GetBackgroundBrush(self, dc): - return self.bgcolor +import wx +from wx.lib.buttons import GenBitmapButton + +import service.fleet +import gui.mainFrame +import gui.utils.colorUtils as colorUtils +import gui.sfBrowserItem as SFItem +from gui.bitmapLoader import BitmapLoader +from gui.PFListPane import PFListPane +from gui.utils.drawUtils import GetPartialText + +FleetSelected, EVT_FLEET_SELECTED = wx.lib.newevent.NewEvent() +FleetRenamed, EVT_FLEET_RENAMED = wx.lib.newevent.NewEvent() +FleetRemoved, EVT_FLEET_REMOVED = wx.lib.newevent.NewEvent() +FleetItemSelect, EVT_FLEET_ITEM_SELECT = wx.lib.newevent.NewEvent() +FleetItemDelete, EVT_FLEET_ITEM_DELETE = wx.lib.newevent.NewEvent() +FleetItemNew, EVT_FLEET_ITEM_NEW = wx.lib.newevent.NewEvent() +FleetItemCopy, EVT_FLEET_ITEM_COPY = wx.lib.newevent.NewEvent() +FleetItemRename, EVT_FLEET_ITEM_RENAME = wx.lib.newevent.NewEvent() + + +class FleetBrowser(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent) + + self.sFleet = service.fleet.Fleet.getInstance() + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + mainSizer = wx.BoxSizer(wx.VERTICAL) + + self.hpane = FleetBrowserHeader(self) + mainSizer.Add(self.hpane, 0, wx.EXPAND) + + self.m_sl2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_sl2, 0, wx.EXPAND, 0) + + self.fleetItemContainer = PFFleetItemContainer(self) + + mainSizer.Add(self.fleetItemContainer, 1, wx.EXPAND) + + self.SetSizer(mainSizer) + self.Layout() + + self.filter = "" + self.fleetIDMustEditName = -1 + + self.Bind(wx.EVT_SIZE, self.SizeRefreshList) + + self.Bind(EVT_FLEET_ITEM_NEW, self.AddNewFleetItem) + self.Bind(EVT_FLEET_ITEM_SELECT, self.SelectFleetItem) + self.Bind(EVT_FLEET_ITEM_DELETE, self.DeleteFleetItem) + self.Bind(EVT_FLEET_ITEM_COPY, self.CopyFleetItem) + self.Bind(EVT_FLEET_ITEM_RENAME, self.RenameFleetItem) + + self.PopulateFleetList() + + def AddNewFleetItem(self, event): + fleetName = event.fleetName + newFleet = self.sFleet.addFleet() + self.sFleet.renameFleet(newFleet, fleetName) + + self.fleetIDMustEditName = newFleet.ID + self.AddItem(newFleet.ID, newFleet.name, newFleet.count()) + + def SelectFleetItem(self, event): + fleetID = event.fleetID + self.fleetItemContainer.SelectWidgetByFleetID(fleetID) + wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleetID)) + + def CopyFleetItem(self, event): + fleetID = event.fleetID + fleet = self.sFleet.copyFleetByID(fleetID) + + fleetName = fleet.name + " Copy" + self.sFleet.renameFleet(fleet, fleetName) + + self.fleetIDMustEditName = fleet.ID + self.AddItem(fleet.ID, fleet.name, fleet.count()) + + self.fleetItemContainer.SelectWidgetByFleetID(fleet.ID) + wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleet.ID)) + + def RenameFleetItem(self, event): + fleetID = event.fleetID + fleet = self.sFleet.getFleetByID(fleetID) + + newFleetName = event.fleetName + + self.sFleet.renameFleet(fleet, newFleetName) + wx.PostEvent(self.mainFrame, FleetRenamed(fleetID=fleet.ID)) + + def DeleteFleetItem(self, event): + self.sFleet.deleteFleetByID(event.fleetID) + self.PopulateFleetList() + wx.PostEvent(self.mainFrame, FleetRemoved(fleetID=event.fleetID)) + + def AddItem(self, ID, name, count): + self.fleetItemContainer.AddWidget(FleetItem(self, ID, name, count)) + widget = self.fleetItemContainer.GetWidgetByFleetID(ID) + self.fleetItemContainer.RefreshList(True) + self.fleetItemContainer.ScrollChildIntoView(widget) + wx.PostEvent(self, FleetItemSelect(fleetID=ID)) + + def PopulateFleetList(self): + self.Freeze() + filter_ = self.filter + self.fleetItemContainer.RemoveAllChildren() + fleetList = self.sFleet.getFleetList() + for fleetID, fleetName, fleetCount in fleetList: + if fleetName.lower().find(filter_.lower()) != -1: + self.fleetItemContainer.AddWidget(FleetItem(self, fleetID, fleetName, fleetCount)) + self.fleetItemContainer.RefreshList() + self.Thaw() + + def SetFilter(self, filter): + self.filter = filter + + def SizeRefreshList(self, event): + ewidth, eheight = event.GetSize() + self.Layout() + self.fleetItemContainer.Layout() + self.fleetItemContainer.RefreshList(True) + event.Skip() + + +class FleetBrowserHeader(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 24), + style=wx.TAB_TRAVERSAL) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) + + self.newBmp = BitmapLoader.getBitmap("fit_add_small", "gui") + bmpSize = (16, 16) + + mainSizer = wx.BoxSizer(wx.HORIZONTAL) + + if 'wxMac' in wx.PlatformInfo: + bgcolour = wx.Colour(0, 0, 0, 0) + else: + bgcolour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) + + self.fbNewFleet = PFGenBitmapButton(self, wx.ID_ANY, self.newBmp, wx.DefaultPosition, bmpSize, wx.BORDER_NONE) + mainSizer.Add(self.fbNewFleet, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, 5) + self.fbNewFleet.SetBackgroundColour(bgcolour) + + self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL) + mainSizer.Add(self.sl1, 0, wx.EXPAND | wx.LEFT, 5) + + self.tcFilter = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.tcFilter, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + + self.stStatus = wx.StaticText(self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0) + self.stStatus.Wrap(-1) + mainSizer.Add(self.stStatus, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) + + self.SetSizer(mainSizer) + self.Layout() + + self.fbNewFleet.Bind(wx.EVT_ENTER_WINDOW, self.fbNewEnterWindow) + self.fbNewFleet.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) + self.fbNewFleet.Bind(wx.EVT_BUTTON, self.OnNewFleetItem) + + self.tcFilter.Bind(wx.EVT_TEXT, self.OnFilterText) + + self.tcFilter.Bind(wx.EVT_ENTER_WINDOW, self.fbFilterEnterWindow) + self.tcFilter.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) + + def OnFilterText(self, event): + filter = self.tcFilter.GetValue() + self.Parent.SetFilter(filter) + self.Parent.PopulateFleetList() + event.Skip() + + def OnNewFleetItem(self, event): + wx.PostEvent(self.Parent, FleetItemNew(fleetName="New Fleet")) + + def fbNewEnterWindow(self, event): + self.stStatus.SetLabel("New fleet") + self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) + event.Skip() + + def fbHItemLeaveWindow(self, event): + self.stStatus.SetLabel("") + self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + event.Skip() + + def fbFilterEnterWindow(self, event): + self.stStatus.SetLabel("Filter list") + event.Skip() + + +class PFFleetItemContainer(PFListPane): + def __init__(self, parent): + PFListPane.__init__(self, parent) + self.selectedWidget = -1 + self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) + + def IsWidgetSelectedByContext(self, widget): + if self.GetWidgetList()[widget].IsSelected(): + return True + return False + + def GetWidgetIndex(self, widgetWnd): + return self.GetWidgetList().index(widgetWnd) + + def GetWidgetByFleetID(self, fleetID): + for widget in self.GetWidgetList(): + if widget.fleetID == fleetID: + return widget + return None + + def SelectWidget(self, widgetWnd): + wlist = self.GetWidgetList() + if self.selectedWidget != -1: + wlist[self.selectedWidget].SetSelected(False) + wlist[self.selectedWidget].Refresh() + windex = self.GetWidgetIndex(widgetWnd) + wlist[windex].SetSelected(True) + wlist[windex].Refresh() + self.selectedWidget = windex + + def SelectWidgetByFleetID(self, fleetID): + widgetWnd = self.GetWidgetByFleetID(fleetID) + if widgetWnd: + self.SelectWidget(widgetWnd) + + def RemoveWidget(self, child): + child.Destroy() + self.selectedWidget = -1 + self._wList.remove(child) + + def RemoveAllChildren(self): + for widget in self._wList: + widget.Destroy() + + self.selectedWidget = -1 + self._wList = [] + + def OnLeftUp(self, event): + event.Skip() + + +class FleetItem(SFItem.SFBrowserItem): + def __init__(self, parent, fleetID, fleetName, fleetCount, + id=wx.ID_ANY, pos=wx.DefaultPosition, + size=(0, 40), style=0): + SFItem.SFBrowserItem.__init__(self, parent, size=size) + + self.fleetBrowser = self.Parent + self.fleetID = fleetID + self.fleetName = fleetName + self.fleetCount = fleetCount + + self.padding = 4 + + self.fontBig = wx.FontFromPixelSize((0, 15), wx.SWISS, wx.NORMAL, wx.BOLD, False) + self.fontNormal = wx.FontFromPixelSize((0, 14), wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.fontSmall = wx.FontFromPixelSize((0, 12), wx.SWISS, wx.NORMAL, wx.NORMAL, False) + + self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") + self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") + self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small", "gui") + self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") + self.fleetBmp = BitmapLoader.getBitmap("fleet_item_big", "gui") + + fleetImg = self.fleetBmp.ConvertToImage() + fleetImg = fleetImg.Blur(2) + + if not fleetImg.HasAlpha(): + fleetImg.InitAlpha() + + fleetImg = fleetImg.AdjustChannels(1, 1, 1, 0.5) + self.fleetEffBmp = wx.BitmapFromImage(fleetImg) + + self.toolbar.AddButton(self.copyBmp, "Copy", self.CopyFleetCB) + self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.RenameFleetCB) + self.toolbar.AddButton(self.deleteBmp, "Delete", self.DeleteFleetCB) + + self.editWidth = 150 + self.tcFleetName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fleetName, wx.DefaultPosition, (self.editWidth, -1), + wx.TE_PROCESS_ENTER) + + if self.fleetBrowser.fleetIDMustEditName != self.fleetID: + self.tcFleetName.Show(False) + else: + self.tcFleetName.SetFocus() + self.tcFleetName.SelectAll() + self.fleetBrowser.fleetIDMustEditName = -1 + self.renameBtn.SetBitmap(self.acceptBmp) + self.selected = True + + self.tcFleetName.Bind(wx.EVT_KILL_FOCUS, self.OnEditLostFocus) + self.tcFleetName.Bind(wx.EVT_TEXT_ENTER, self.RenameFleet) + self.tcFleetName.Bind(wx.EVT_KEY_DOWN, self.EditCheckEsc) + + self.animCount = 0 + + def MouseLeftUp(self, event): + if self.tcFleetName.IsShown(): + self.RestoreEditButton() + else: + wx.PostEvent(self.fleetBrowser, FleetItemSelect(fleetID=self.fleetID)) + + def CopyFleetCB(self): + if self.tcFleetName.IsShown(): + self.RestoreEditButton() + return + + wx.PostEvent(self.fleetBrowser, FleetItemCopy(fleetID=self.fleetID)) + + def RenameFleetCB(self): + + if self.tcFleetName.IsShown(): + + self.RenameFleet(None) + self.RestoreEditButton() + + else: + self.tcFleetName.SetValue(self.fleetName) + self.tcFleetName.Show() + + self.renameBtn.SetBitmap(self.acceptBmp) + self.Refresh() + + self.tcFleetName.SetFocus() + self.tcFleetName.SelectAll() + + self.Refresh() + + def RenameFleet(self, event): + + newFleetName = self.tcFleetName.GetValue() + self.fleetName = newFleetName + + self.tcFleetName.Show(False) + + wx.PostEvent(self.fleetBrowser, FleetItemRename(fleetID=self.fleetID, fleetName=self.fleetName)) + self.Refresh() + + def DeleteFleetCB(self): + if self.tcFleetName.IsShown(): + self.RestoreEditButton() + return + wx.PostEvent(self.fleetBrowser, FleetItemDelete(fleetID=self.fleetID)) + + def RestoreEditButton(self): + self.tcFleetName.Show(False) + self.renameBtn.SetBitmap(self.renameBmp) + self.Refresh() + + def OnEditLostFocus(self, event): + self.RestoreEditButton() + self.Refresh() + + def EditCheckEsc(self, event): + if event.GetKeyCode() == wx.WXK_ESCAPE: + self.RestoreEditButton() + else: + event.Skip() + + def IsSelected(self): + return self.selected + + def UpdateElementsPos(self, mdc): + rect = self.GetRect() + + self.toolbarx = rect.width - self.toolbar.GetWidth() - self.padding + self.toolbary = (rect.height - self.toolbar.GetHeight()) / 2 + + self.toolbarx = self.toolbarx + self.animCount + + self.fleetBmpx = self.padding + (rect.height - self.fleetBmp.GetWidth()) / 2 + self.fleetBmpy = (rect.height - self.fleetBmp.GetHeight()) / 2 + + self.fleetBmpx -= self.animCount + + self.textStartx = self.fleetBmpx + self.fleetBmp.GetWidth() + self.padding + + self.fleetNamey = (rect.height - self.fleetBmp.GetHeight()) / 2 + + mdc.SetFont(self.fontBig) + wtext, htext = mdc.GetTextExtent(self.fleetName) + + self.fleetCounty = self.fleetNamey + htext + + mdc.SetFont(self.fontSmall) + + wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) + + self.thoverx = self.toolbarx - self.padding - wlabel + self.thovery = (rect.height - hlabel) / 2 + self.thoverw = wlabel + + def DrawItem(self, mdc): + # rect = self.GetRect() + + windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + textColor = colorUtils.GetSuitableColor(windowColor, 1) + + mdc.SetTextForeground(textColor) + + self.UpdateElementsPos(mdc) + + self.toolbar.SetPosition((self.toolbarx, self.toolbary)) + mdc.DrawBitmap(self.fleetEffBmp, self.fleetBmpx + 3, self.fleetBmpy + 2) + mdc.DrawBitmap(self.fleetBmp, self.fleetBmpx, self.fleetBmpy) + + mdc.SetFont(self.fontNormal) + + suffix = "%d ships" % self.fleetCount if self.fleetCount > 1 else "%d ship" % self.fleetCount if self.fleetCount == 1 else "No ships" + fleetCount = "Fleet size: %s" % suffix + fleetCount = GetPartialText(mdc, fleetCount, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + + mdc.DrawText(fleetCount, self.textStartx, self.fleetCounty) + + mdc.SetFont(self.fontSmall) + mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) + + mdc.SetFont(self.fontBig) + + pfname = GetPartialText(mdc, self.fleetName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + mdc.DrawText(pfname, self.textStartx, self.fleetNamey) + + if self.tcFleetName.IsShown(): + self.AdjustControlSizePos(self.tcFleetName, self.textStartx, self.toolbarx - self.editWidth - self.padding) + + def AdjustControlSizePos(self, editCtl, start, end): + fnEditSize = editCtl.GetSize() + wSize = self.GetSize() + fnEditPosX = end + fnEditPosY = (wSize.height - fnEditSize.height) / 2 + if fnEditPosX < start: + editCtl.SetSize((self.editWidth + fnEditPosX - start, -1)) + editCtl.SetPosition((start, fnEditPosY)) + else: + editCtl.SetSize((self.editWidth, -1)) + editCtl.SetPosition((fnEditPosX, fnEditPosY)) + + +class PFGenBitmapButton(GenBitmapButton): + def __init__(self, parent, id, bitmap, pos, size, style): + GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style) + self.bgcolor = wx.Brush(wx.WHITE) + + def SetBackgroundColour(self, color): + self.bgcolor = wx.Brush(color) + + def GetBackgroundBrush(self, dc): + return self.bgcolor diff --git a/gui/gangView.py b/gui/gangView.py index ea422bc83..93e48c117 100644 --- a/gui/gangView.py +++ b/gui/gangView.py @@ -32,7 +32,8 @@ import gui.globalEvents as GE class GangView(ScrolledPanel): def __init__(self, parent): - ScrolledPanel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(100, 20), style=wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL) + ScrolledPanel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(100, 20), + style=wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL) mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) @@ -49,10 +50,10 @@ class GangView(ScrolledPanel): self.fleet = {} for id_, option in enumerate(self.options): - # set content for each commander self.fleet[id_] = {} - self.fleet[id_]['stLabel'] = wx.StaticText(self, wx.ID_ANY, self.options[id_] + ':', wx.DefaultPosition, wx.DefaultSize, 0) + self.fleet[id_]['stLabel'] = wx.StaticText(self, wx.ID_ANY, self.options[id_] + ':', wx.DefaultPosition, + wx.DefaultSize, 0) self.fleet[id_]['stText'] = wx.StaticText(self, wx.ID_ANY, 'None', wx.DefaultPosition, wx.DefaultSize, 0) self.fleet[id_]['chFit'] = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) self.fleet[id_]['chChar'] = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) @@ -94,7 +95,8 @@ class GangView(ScrolledPanel): for id_ in self.fleet: # set various properties self.fleet[id_]['stLabel'].Wrap(-1) - self.fleet[id_]['stLabel'].SetFont(wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) + self.fleet[id_]['stLabel'].SetFont( + wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) self.fleet[id_]['stText'].Wrap(-1) # bind text and choice events @@ -107,7 +109,8 @@ class GangView(ScrolledPanel): # add fit text and choice to the fit sizer self.fleet[id_]['fitSizer'].Add(self.fleet[id_]['stText'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) - self.fleet[id_]['fitSizer'].Add(self.fleet[id_]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, 1) + self.fleet[id_]['fitSizer'].Add(self.fleet[id_]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, + 1) # add everything to the content sizer contentFGSizer.Add(self.fleet[id_]['stLabel'], 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) @@ -271,7 +274,8 @@ class GangView(ScrolledPanel): raise Exception() self.fleet[id_]['stText'].SetLabel(commander.ship.item.name + ": " + commander.name) - self.fleet[id_]['chChar'].SetStringSelection(commander.character.name if commander.character is not None else "All 0") + self.fleet[id_]['chChar'].SetStringSelection( + commander.character.name if commander.character is not None else "All 0") self.fleet[id_]['chChar'].Enable() self.fleet[id_]['chFit'].Hide() self.fleet[id_]['stText'].Show() diff --git a/gui/globalEvents.py b/gui/globalEvents.py index 7b81b3218..18f91d348 100644 --- a/gui/globalEvents.py +++ b/gui/globalEvents.py @@ -1,6 +1,5 @@ import wx.lib.newevent - FitChanged, FIT_CHANGED = wx.lib.newevent.NewEvent() CharListUpdated, CHAR_LIST_UPDATED = wx.lib.newevent.NewEvent() CharChanged, CHAR_CHANGED = wx.lib.newevent.NewEvent() diff --git a/gui/graphFrame.py b/gui/graphFrame.py index dc286af1b..993998d10 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -1,281 +1,282 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -import os - -import wx - -from service.fit import Fit -import gui.display -import gui.mainFrame -import gui.globalEvents as GE -from gui.graph import Graph -from gui.bitmapLoader import BitmapLoader - - -enabled = True -mplImported = False - - -class GraphFrame(wx.Frame): - def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): - - global enabled - global mplImported - - self.legendFix = False - if not enabled: - return - - try: - import matplotlib as mpl - - try: - cache_dir = mpl._get_cachedir() - except: - cache_dir = unicode(os.path.expanduser(os.path.join("~", ".matplotlib"))) - - cache_file = os.path.join(cache_dir, 'fontList.cache') - if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): - # remove matplotlib font cache, see #234 - os.remove(cache_file) - if not mplImported: - mpl.use('wxagg') - from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas - from matplotlib.figure import Figure - enabled = True - if mpl.__version__[0] != "1": - print("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds") - print("pyfa: Recommended minimum matplotlib version is 1.0.0") - self.legendFix = True - except: - print("Problems importing matplotlib; continuing without graphs") - enabled = False - return - - mplImported = True - - wx.Frame.__init__(self, parent, title=u"pyfa: Graph Generator", style=style, size=(520, 390)) - - i = wx.IconFromBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) - self.SetIcon(i) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.CreateStatusBar() - - self.mainSizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self.mainSizer) - - sFit = Fit.getInstance() - fit = sFit.getFit(self.mainFrame.getActiveFit()) - self.fits = [fit] if fit is not None else [] - self.fitList = FitList(self) - self.fitList.SetMinSize((270, -1)) - - self.fitList.fitList.update(self.fits) - - self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) - self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) - - self.figure = Figure(figsize=(4, 3)) - - rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() - clr = [c / 255. for c in rgbtuple] - self.figure.set_facecolor(clr) - self.figure.set_edgecolor(clr) - - self.canvas = Canvas(self, -1, self.figure) - self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) - - self.subplot = self.figure.add_subplot(111) - self.subplot.grid(True) - - self.mainSizer.Add(self.canvas, 1, wx.EXPAND) - self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND) - - self.gridPanel = wx.Panel(self) - self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) - - dummyBox = wx.BoxSizer(wx.VERTICAL) - self.gridPanel.SetSizer(dummyBox) - - self.gridSizer = wx.FlexGridSizer(0, 4) - self.gridSizer.AddGrowableCol(1) - dummyBox.Add(self.gridSizer, 0, wx.EXPAND) - - for view in Graph.views: - view = view() - self.graphSelection.Append(view.name, view) - - self.graphSelection.SetSelection(0) - self.fields = {} - self.select(0) - self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - self.mainSizer.Add(self.sl1, 0, wx.EXPAND) - self.mainSizer.Add(self.fitList, 0, wx.EXPAND) - - self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) - self.mainFrame.Bind(GE.FIT_CHANGED, self.draw) - self.Bind(wx.EVT_CLOSE, self.close) - - self.Fit() - self.SetMinSize(self.GetSize()) - - def handleDrag(self, type, fitID): - if type == "fit": - self.AppendFitToList(fitID) - - def close(self, event): - self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) - self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw) - event.Skip() - - def getView(self): - return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) - - def getValues(self): - values = {} - for fieldName, field in self.fields.iteritems(): - values[fieldName] = field.GetValue() - - return values - - def select(self, index): - view = self.getView() - icons = view.getIcons() - labels = view.getLabels() - sizer = self.gridSizer - self.gridPanel.DestroyChildren() - self.fields.clear() - - # Setup textboxes - for field, defaultVal in view.getFields().iteritems(): - - textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) - self.fields[field] = textBox - textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) - sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) - if defaultVal is not None: - if not isinstance(defaultVal, basestring): - defaultVal = ("%f" % defaultVal).rstrip("0") - if defaultVal[-1:] == ".": - defaultVal = defaultVal + "0" - - textBox.ChangeValue(defaultVal) - - imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) - if icons: - icon = icons.get(field) - if icon is not None: - static = wx.StaticBitmap(self.gridPanel) - static.SetBitmap(icon) - imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) - - if labels: - label = labels.get(field) - label = label if label is not None else field - else: - label = field - - imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) - sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) - self.draw() - - def draw(self, event=None): - values = self.getValues() - view = self.getView() - self.subplot.clear() - self.subplot.grid(True) - legend = [] - - for fit in self.fits: - try: - success, status = view.getPoints(fit, values) - if not success: - # TODO: Add a pwetty statys bar to report errors with - self.SetStatusText(status) - return - - x, y = success, status - - self.subplot.plot(x, y) - legend.append(fit.name) - except: - self.SetStatusText("Invalid values in '%s'" % fit.name) - self.canvas.draw() - return - - if self.legendFix and len(legend) > 0: - leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) - for t in leg.get_texts(): - t.set_fontsize('small') - - for l in leg.get_lines(): - l.set_linewidth(1) - - elif not self.legendFix and len(legend) > 0: - leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) - for t in leg.get_texts(): - t.set_fontsize('small') - - for l in leg.get_lines(): - l.set_linewidth(1) - - self.canvas.draw() - self.SetStatusText("") - if event is not None: - event.Skip() - - def onFieldChanged(self, event): - self.draw() - - def AppendFitToList(self, fitID): - sFit = Fit.getInstance() - fit = sFit.getFit(fitID) - if fit not in self.fits: - self.fits.append(fit) - - self.fitList.fitList.update(self.fits) - self.draw() - - def removeItem(self, event): - row, _ = self.fitList.fitList.HitTest(event.Position) - if row != -1: - del self.fits[row] - self.fitList.fitList.update(self.fits) - self.draw() - - -class FitList(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - self.mainSizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self.mainSizer) - - self.fitList = FitDisplay(self) - self.mainSizer.Add(self.fitList, 1, wx.EXPAND) - fitToolTip = wx.ToolTip("Drag a fit into this list to graph it") - self.fitList.SetToolTip(fitToolTip) - - -class FitDisplay(gui.display.Display): - DEFAULT_COLS = ["Base Icon", - "Base Name"] - - def __init__(self, parent): - gui.display.Display.__init__(self, parent) +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +import os + +import wx + +from service.fit import Fit +import gui.display +import gui.mainFrame +import gui.globalEvents as GE +from gui.graph import Graph +from gui.bitmapLoader import BitmapLoader + +enabled = True +mplImported = False + + +class GraphFrame(wx.Frame): + def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): + + global enabled + global mplImported + + self.legendFix = False + if not enabled: + return + + try: + import matplotlib as mpl + + try: + cache_dir = mpl._get_cachedir() + except: + cache_dir = unicode(os.path.expanduser(os.path.join("~", ".matplotlib"))) + + cache_file = os.path.join(cache_dir, 'fontList.cache') + if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): + # remove matplotlib font cache, see #234 + os.remove(cache_file) + if not mplImported: + mpl.use('wxagg') + from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas + from matplotlib.figure import Figure + enabled = True + if mpl.__version__[0] != "1": + print("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds") + print("pyfa: Recommended minimum matplotlib version is 1.0.0") + self.legendFix = True + except: + print("Problems importing matplotlib; continuing without graphs") + enabled = False + return + + mplImported = True + + wx.Frame.__init__(self, parent, title=u"pyfa: Graph Generator", style=style, size=(520, 390)) + + i = wx.IconFromBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) + self.SetIcon(i) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.CreateStatusBar() + + self.mainSizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.mainSizer) + + sFit = Fit.getInstance() + fit = sFit.getFit(self.mainFrame.getActiveFit()) + self.fits = [fit] if fit is not None else [] + self.fitList = FitList(self) + self.fitList.SetMinSize((270, -1)) + + self.fitList.fitList.update(self.fits) + + self.graphSelection = wx.Choice(self, wx.ID_ANY, style=0) + self.mainSizer.Add(self.graphSelection, 0, wx.EXPAND) + + self.figure = Figure(figsize=(4, 3)) + + rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() + clr = [c / 255. for c in rgbtuple] + self.figure.set_facecolor(clr) + self.figure.set_edgecolor(clr) + + self.canvas = Canvas(self, -1, self.figure) + self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) + + self.subplot = self.figure.add_subplot(111) + self.subplot.grid(True) + + self.mainSizer.Add(self.canvas, 1, wx.EXPAND) + self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND) + + self.gridPanel = wx.Panel(self) + self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) + + dummyBox = wx.BoxSizer(wx.VERTICAL) + self.gridPanel.SetSizer(dummyBox) + + self.gridSizer = wx.FlexGridSizer(0, 4) + self.gridSizer.AddGrowableCol(1) + dummyBox.Add(self.gridSizer, 0, wx.EXPAND) + + for view in Graph.views: + view = view() + self.graphSelection.Append(view.name, view) + + self.graphSelection.SetSelection(0) + self.fields = {} + self.select(0) + self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + self.mainSizer.Add(self.sl1, 0, wx.EXPAND) + self.mainSizer.Add(self.fitList, 0, wx.EXPAND) + + self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) + self.mainFrame.Bind(GE.FIT_CHANGED, self.draw) + self.Bind(wx.EVT_CLOSE, self.close) + + self.Fit() + self.SetMinSize(self.GetSize()) + + def handleDrag(self, type, fitID): + if type == "fit": + self.AppendFitToList(fitID) + + def close(self, event): + self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) + self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw) + event.Skip() + + def getView(self): + return self.graphSelection.GetClientData(self.graphSelection.GetSelection()) + + def getValues(self): + values = {} + for fieldName, field in self.fields.iteritems(): + values[fieldName] = field.GetValue() + + return values + + def select(self, index): + view = self.getView() + icons = view.getIcons() + labels = view.getLabels() + sizer = self.gridSizer + self.gridPanel.DestroyChildren() + self.fields.clear() + + # Setup textboxes + for field, defaultVal in view.getFields().iteritems(): + + textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) + self.fields[field] = textBox + textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) + sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) + if defaultVal is not None: + if not isinstance(defaultVal, basestring): + defaultVal = ("%f" % defaultVal).rstrip("0") + if defaultVal[-1:] == ".": + defaultVal = defaultVal + "0" + + textBox.ChangeValue(defaultVal) + + imgLabelSizer = wx.BoxSizer(wx.HORIZONTAL) + if icons: + icon = icons.get(field) + if icon is not None: + static = wx.StaticBitmap(self.gridPanel) + static.SetBitmap(icon) + imgLabelSizer.Add(static, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 1) + + if labels: + label = labels.get(field) + label = label if label is not None else field + else: + label = field + + imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, + wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) + sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) + self.draw() + + def draw(self, event=None): + values = self.getValues() + view = self.getView() + self.subplot.clear() + self.subplot.grid(True) + legend = [] + + for fit in self.fits: + try: + success, status = view.getPoints(fit, values) + if not success: + # TODO: Add a pwetty statys bar to report errors with + self.SetStatusText(status) + return + + x, y = success, status + + self.subplot.plot(x, y) + legend.append(fit.name) + except: + self.SetStatusText("Invalid values in '%s'" % fit.name) + self.canvas.draw() + return + + if self.legendFix and len(legend) > 0: + leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) + for t in leg.get_texts(): + t.set_fontsize('small') + + for l in leg.get_lines(): + l.set_linewidth(1) + + elif not self.legendFix and len(legend) > 0: + leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) + for t in leg.get_texts(): + t.set_fontsize('small') + + for l in leg.get_lines(): + l.set_linewidth(1) + + self.canvas.draw() + self.SetStatusText("") + if event is not None: + event.Skip() + + def onFieldChanged(self, event): + self.draw() + + def AppendFitToList(self, fitID): + sFit = Fit.getInstance() + fit = sFit.getFit(fitID) + if fit not in self.fits: + self.fits.append(fit) + + self.fitList.fitList.update(self.fits) + self.draw() + + def removeItem(self, event): + row, _ = self.fitList.fitList.HitTest(event.Position) + if row != -1: + del self.fits[row] + self.fitList.fitList.update(self.fits) + self.draw() + + +class FitList(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent) + self.mainSizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.mainSizer) + + self.fitList = FitDisplay(self) + self.mainSizer.Add(self.fitList, 1, wx.EXPAND) + fitToolTip = wx.ToolTip("Drag a fit into this list to graph it") + self.fitList.SetToolTip(fitToolTip) + + +class FitDisplay(gui.display.Display): + DEFAULT_COLS = ["Base Icon", + "Base Name"] + + def __init__(self, parent): + gui.display.Display.__init__(self, parent) diff --git a/gui/itemStats.py b/gui/itemStats.py index fe56f3a09..a7c008b12 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -64,7 +64,8 @@ class ItemStatsDialog(wx.Dialog): itmContext = fullContext[1] except IndexError: itmContext = None - item = getattr(victim, "item", None) if srcContext.lower() not in ("projectedcharge", "fittingcharge") else getattr(victim, "charge", None) + item = getattr(victim, "item", None) if srcContext.lower() not in ( + "projectedcharge", "fittingcharge") else getattr(victim, "charge", None) if item is None: sMkt = Market.getInstance() item = sMkt.getItem(victim.ID) @@ -76,7 +77,8 @@ class ItemStatsDialog(wx.Dialog): itemImg = BitmapLoader.getBitmap(iconFile, "icons") if itemImg is not None: self.SetIcon(wx.IconFromBitmap(itemImg)) - self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, " (%d)" % item.ID if config.debug else "")) + self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, + " (%d)" % item.ID if config.debug else "")) self.SetMinSize((300, 200)) if "wxGTK" in wx.PlatformInfo: # GTK has huge tab widgets, give it a bit more room @@ -232,7 +234,8 @@ class ItemDescription(wx.Panel): desc = re.sub("<( *)font( *)color( *)=(.*?)>(?P.*?)<( *)/( *)font( *)>", "\g", desc) # Strip URLs desc = re.sub("<( *)a(.*?)>(?P.*?)<( *)/( *)a( *)>", "\g", desc) - desc = "" + desc + "" + desc = "" + desc + "" self.description.SetPage(desc) @@ -240,12 +243,13 @@ class ItemDescription(wx.Panel): self.Layout() -class ItemParams (wx.Panel): +class ItemParams(wx.Panel): def __init__(self, parent, stuff, item, context=None): wx.Panel.__init__(self, parent) mainSizer = wx.BoxSizer(wx.VERTICAL) - self.paramList = AutoListCtrl(self, wx.ID_ANY, style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) + self.paramList = AutoListCtrl(self, wx.ID_ANY, + style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0) self.SetSizer(mainSizer) @@ -263,10 +267,12 @@ class ItemParams (wx.Panel): self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) - self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, wx.DefaultSize, 0) + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, wx.DefaultSize, + 0) bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, u"Export Item Stats", wx.DefaultPosition, wx.DefaultSize, 0) + self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, u"Export Item Stats", wx.DefaultPosition, wx.DefaultSize, + 0) bSizer.Add(self.exportStatsBtn, 0, wx.ALIGN_CENTER_VERTICAL) if stuff is not None: @@ -470,7 +476,8 @@ class ItemParams (wx.Panel): trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName), "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), - "Modifier Percent": (lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), + "Modifier Percent": ( + lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), "Volume": (lambda: value, u"m\u00B3"), "Sizeclass": (lambda: value, ""), "Absolute Percent": (lambda: (value * 100), unitName), @@ -508,7 +515,8 @@ class ItemCompare(wx.Panel): self.currentSort = None self.sortReverse = False self.item = item - self.items = sorted(items, key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else None) + self.items = sorted(items, + key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else None) self.attrs = {} # get a dict of attrName: attrInfo of all unique attributes across all items @@ -661,7 +669,7 @@ class ItemCompare(wx.Panel): trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName), "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), "Modifier Percent": ( - lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), + lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), "Volume": (lambda: value, u"m\u00B3"), "Sizeclass": (lambda: value, ""), "Absolute Percent": (lambda: (value * 100), unitName), @@ -719,14 +727,15 @@ class ItemRequirements(wx.Panel): self.skillIdHistory.append(skill.ID) -class ItemEffects (wx.Panel): +class ItemEffects(wx.Panel): def __init__(self, parent, stuff, item): wx.Panel.__init__(self, parent) self.item = item mainSizer = wx.BoxSizer(wx.VERTICAL) - self.effectList = AutoListCtrl(self, wx.ID_ANY, style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) + self.effectList = AutoListCtrl(self, wx.ID_ANY, + style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER) mainSizer.Add(self.effectList, 1, wx.ALL | wx.EXPAND, 0) self.SetSizer(mainSizer) @@ -1033,7 +1042,8 @@ class ItemAffectedBy(wx.Panel): else: item = afflictor.item - items[attrName].append((type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) + items[attrName].append( + (type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) # Make sure projected fits are on top rootOrder = container.keys() @@ -1234,7 +1244,8 @@ class ItemAffectedBy(wx.Panel): else: penalized = "" - attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized, attrIcon)) + attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, + attrAmount, penalized, attrIcon)) attrSorted = sorted(attributes, key=lambda attribName: attribName[0]) for attr in attrSorted: @@ -1242,9 +1253,11 @@ class ItemAffectedBy(wx.Panel): if self.showRealNames: display = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized) - saved = "%s %s %.2f %s" % ((displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized) + saved = "%s %s %.2f %s" % ( + (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized) else: - display = "%s %s %.2f %s" % ((displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized) + display = "%s %s %.2f %s" % ( + (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized) saved = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized) treeitem = self.affectedBy.AppendItem(child, display, attrIcon) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 69bdbe3e7..8f2cf73e7 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -81,6 +81,7 @@ if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION try: from gui.propertyEditor import AttributeEditor + disableOverrideEditor = False except ImportError as e: print("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message) @@ -240,7 +241,8 @@ class MainFrame(wx.Frame): def LoadPreviousOpenFits(self): sFit = Fit.getInstance() - self.prevOpenFits = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) + self.prevOpenFits = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", + {"enabled": False, "pyfaOpenFits": []}) fits = self.prevOpenFits['pyfaOpenFits'] # Remove any fits that cause exception when fetching (non-existent fits) @@ -259,8 +261,10 @@ class MainFrame(wx.Frame): OpenFitsThread(fits, self.closeWaitDialog) def LoadMainFrameAttribs(self): - mainFrameDefaultAttribs = {"wnd_width": 1000, "wnd_height": 700, "wnd_maximized": False, "browser_width": 300, "market_height": 0, "fitting_height": -200} - self.mainFrameAttribs = SettingsProvider.getInstance().getSettings("pyfaMainWindowAttribs", mainFrameDefaultAttribs) + mainFrameDefaultAttribs = {"wnd_width": 1000, "wnd_height": 700, "wnd_maximized": False, "browser_width": 300, + "market_height": 0, "fitting_height": -200} + self.mainFrameAttribs = SettingsProvider.getInstance().getSettings("pyfaMainWindowAttribs", + mainFrameDefaultAttribs) if self.mainFrameAttribs["wnd_maximized"]: width = mainFrameDefaultAttribs["wnd_width"] @@ -623,7 +627,8 @@ class MainFrame(wx.Frame): ModifiedAttributeDict.OVERRIDES = not ModifiedAttributeDict.OVERRIDES wx.PostEvent(self, GE.FitChanged(fitID=self.getActiveFit())) menu = self.GetMenuBar() - menu.SetLabel(menu.toggleOverridesId, "Turn Overrides Off" if ModifiedAttributeDict.OVERRIDES else "Turn Overrides On") + menu.SetLabel(menu.toggleOverridesId, + "Turn Overrides Off" if ModifiedAttributeDict.OVERRIDES else "Turn Overrides On") def saveChar(self, event): sChr = Character.getInstance() diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 2de7f4b80..527f701b1 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -27,7 +27,6 @@ import gui.graphFrame import gui.globalEvents as GE from gui.bitmapLoader import BitmapLoader - if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): from service.crest import CrestModes @@ -158,7 +157,8 @@ class MainMenuBar(wx.MenuBar): helpMenu.Append(wx.ID_ABOUT) if config.debug: - helpMenu.Append(self.mainFrame.widgetInspectMenuID, "Open Widgets Inspect tool", "Open Widgets Inspect tool") + helpMenu.Append(self.mainFrame.widgetInspectMenuID, "Open Widgets Inspect tool", + "Open Widgets Inspect tool") self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index 33db5d501..7efefb7a5 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -1,462 +1,461 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -import wx -from service.market import Market -from service.attribute import Attribute -import gui.display as d -import gui.PFSearchBox as SBox -from gui.cachingImageList import CachingImageList -from gui.contextMenu import ContextMenu -from gui.bitmapLoader import BitmapLoader - - -ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent() - -RECENTLY_USED_MODULES = -2 -MAX_RECENTLY_USED_MODULES = 20 - - -class MarketBrowser(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - vbox = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(vbox) - - # Add a search box on top - self.search = SearchBox(self) - vbox.Add(self.search, 0, wx.EXPAND) - - self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) - vbox.Add(self.splitter, 1, wx.EXPAND) - - # Grab market service instance and create child objects - self.sMkt = Market.getInstance() - self.searchMode = False - self.marketView = MarketTree(self.splitter, self) - self.itemView = ItemView(self.splitter, self) - - self.splitter.SplitHorizontally(self.marketView, self.itemView) - self.splitter.SetMinimumPaneSize(250) - - # Setup our buttons for metaGroup selection - # Same fix as for search box on macs, - # need some pixels of extra space or everything clips and is ugly - p = wx.Panel(self) - box = wx.BoxSizer(wx.HORIZONTAL) - p.SetSizer(box) - vbox.Add(p, 0, wx.EXPAND) - self.metaButtons = [] - for name in self.sMkt.META_MAP.keys(): - btn = wx.ToggleButton(p, wx.ID_ANY, name.capitalize(), style=wx.BU_EXACTFIT) - setattr(self, name, btn) - box.Add(btn, 1, wx.ALIGN_CENTER) - btn.Bind(wx.EVT_TOGGLEBUTTON, self.toggleMetaButton) - btn.metaName = name - self.metaButtons.append(btn) - # Make itemview to set toggles according to list contents - self.itemView.setToggles() - - p.SetMinSize((wx.SIZE_AUTO_WIDTH, btn.GetSize()[1] + 5)) - - def toggleMetaButton(self, event): - """Process clicks on toggle buttons""" - ctrl = wx.GetMouseState().CmdDown() - ebtn = event.EventObject - if not ctrl: - for btn in self.metaButtons: - if btn.Enabled: - if btn == ebtn: - btn.SetValue(True) - else: - btn.SetValue(False) - else: - # Note: using the 'wrong' value for clicked button might seem weird, - # But the button is toggled by wx and we should deal with it - activeBtns = set() - for btn in self.metaButtons: - if (btn.GetValue() is True and btn != ebtn) or (btn.GetValue() is False and btn == ebtn): - activeBtns.add(btn) - # Do 'nothing' if we're trying to turn last active button off - if len(activeBtns) == 1 and activeBtns.pop() == ebtn: - # Keep button in the same state - ebtn.SetValue(True) - return - # Leave old unfiltered list contents, just re-filter them and show - self.itemView.filterItemStore() - - def jump(self, item): - self.marketView.jump(item) - - -class SearchBox(SBox.PFSearchBox): - def __init__(self, parent, **kwargs): - SBox.PFSearchBox.__init__(self, parent, **kwargs) - cancelBitmap = BitmapLoader.getBitmap("fit_delete_small", "gui") - searchBitmap = BitmapLoader.getBitmap("fsearch_small", "gui") - self.SetSearchBitmap(searchBitmap) - self.SetCancelBitmap(cancelBitmap) - self.ShowSearchButton() - self.ShowCancelButton() - - -class MarketTree(wx.TreeCtrl): - def __init__(self, parent, marketBrowser): - wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) - self.root = self.AddRoot("root") - - self.imageList = CachingImageList(16, 16) - self.SetImageList(self.imageList) - - self.sMkt = marketBrowser.sMkt - self.marketBrowser = marketBrowser - - # Form market tree root - sMkt = self.sMkt - for mktGrp in sMkt.getMarketRoot(): - iconId = self.addImage(sMkt.getIconByMarketGroup(mktGrp)) - childId = self.AppendItem(self.root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) - # All market groups which were never expanded are dummies, here we assume - # that all root market groups are expandable - self.AppendItem(childId, "dummy") - self.SortChildren(self.root) - - # Add recently used modules node - rumIconId = self.addImage("market_small", "gui") - self.AppendItem(self.root, "Recently Used Modules", rumIconId, data=wx.TreeItemData(RECENTLY_USED_MODULES)) - - # Bind our lookup method to when the tree gets expanded - self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) - - def addImage(self, iconFile, location="icons"): - if iconFile is None: - return -1 - return self.imageList.GetImageIndex(iconFile, location) - - def expandLookup(self, event): - """Process market tree expands""" - root = event.Item - child = self.GetFirstChild(root)[0] - # If child of given market group is a dummy - if self.GetItemText(child) == "dummy": - # Delete it - self.Delete(child) - # And add real market group contents - sMkt = self.sMkt - currentMktGrp = sMkt.getMarketGroup(self.GetPyData(root), eager="children") - for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): - # If market should have items but it doesn't, do not show it - if sMkt.marketGroupValidityCheck(childMktGrp) is False: - continue - iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp)) - try: - childId = self.AppendItem(root, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) - except: - continue - if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: - self.AppendItem(childId, "dummy") - - self.SortChildren(root) - - def jump(self, item): - """Open market group and meta tab of given item""" - self.marketBrowser.searchMode = False - sMkt = self.sMkt - mg = sMkt.getMarketGroupByItem(item) - metaId = sMkt.getMetaGroupIdByItem(item) - - jumpList = [] - while mg is not None: - jumpList.append(mg.ID) - mg = mg.parent - - for id in sMkt.ROOT_MARKET_GROUPS: - if id in jumpList: - jumpList = jumpList[:jumpList.index(id) + 1] - - item = self.root - for i in range(len(jumpList) - 1, -1, -1): - target = jumpList[i] - child, cookie = self.GetFirstChild(item) - while self.GetItemPyData(child) != target: - child, cookie = self.GetNextChild(item, cookie) - - item = child - self.Expand(item) - - self.SelectItem(item) - self.marketBrowser.itemView.selectionMade(forcedMetaSelect=metaId) - - -class ItemView(d.Display): - DEFAULT_COLS = ["Base Icon", - "Base Name", - "attr:power,,,True", - "attr:cpu,,,True"] - - def __init__(self, parent, marketBrowser): - d.Display.__init__(self, parent) - marketBrowser.Bind(wx.EVT_TREE_SEL_CHANGED, self.selectionMade) - - self.unfilteredStore = set() - self.filteredStore = set() - self.recentlyUsedModules = set() - self.sMkt = marketBrowser.sMkt - self.searchMode = marketBrowser.searchMode - - self.marketBrowser = marketBrowser - self.marketView = marketBrowser.marketView - - # Make sure our search actually does interesting stuff - self.marketBrowser.search.Bind(SBox.EVT_TEXT_ENTER, self.scheduleSearch) - self.marketBrowser.search.Bind(SBox.EVT_SEARCH_BTN, self.scheduleSearch) - self.marketBrowser.search.Bind(SBox.EVT_CANCEL_BTN, self.clearSearch) - self.marketBrowser.search.Bind(SBox.EVT_TEXT, self.scheduleSearch) - - # Make sure WE do interesting stuff too - self.Bind(wx.EVT_CONTEXT_MENU, self.contextMenu) - self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated) - self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) - - # Make reverse map, used by sorter - self.metaMap = self.makeReverseMetaMap() - - # Fill up recently used modules set - for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: - self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) - - def startDrag(self, event): - row = self.GetFirstSelected() - - if row != -1: - data = wx.PyTextDataObject() - data.SetText("market:" + str(self.active[row].ID)) - - dropSource = wx.DropSource(self) - dropSource.SetData(data) - dropSource.DoDragDrop() - - def itemActivated(self, event=None): - # Check if something is selected, if so, spawn the menu for it - sel = self.GetFirstSelected() - if sel == -1: - return - - if self.mainFrame.getActiveFit(): - - self.storeRecentlyUsedMarketItem(self.active[sel].ID) - self.recentlyUsedModules = set() - for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: - self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) - - wx.PostEvent(self.mainFrame, ItemSelected(itemID=self.active[sel].ID)) - - def storeRecentlyUsedMarketItem(self, itemID): - if len(self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]) > MAX_RECENTLY_USED_MODULES: - self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].pop(0) - - self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].append(itemID) - - def selectionMade(self, event=None, forcedMetaSelect=None): - self.marketBrowser.searchMode = False - # Grab the threeview selection and check if it's fine - sel = self.marketView.GetSelection() - if sel.IsOk(): - # Get data field of the selected item (which is a marketGroup ID if anything was selected) - seldata = self.marketView.GetPyData(sel) - if seldata is not None and seldata != RECENTLY_USED_MODULES: - # If market group treeview item doesn't have children (other market groups or dummies), - # then it should have items in it and we want to request them - if self.marketView.ItemHasChildren(sel) is False: - sMkt = self.sMkt - # Get current market group - mg = sMkt.getMarketGroup(seldata, eager=("items", "items.metaGroup")) - # Get all its items - items = sMkt.getItemsByMarketGroup(mg) - else: - items = set() - else: - # If method was called but selection wasn't actually made or we have a hit on recently used modules - if seldata == RECENTLY_USED_MODULES: - items = self.recentlyUsedModules - else: - items = set() - - # Fill store - self.updateItemStore(items) - - # Set toggle buttons / use search mode flag if recently used modules category is selected (in order to have all modules listed and not filtered) - if seldata is not RECENTLY_USED_MODULES: - self.setToggles(forcedMetaSelect=forcedMetaSelect) - else: - self.marketBrowser.searchMode = True - self.setToggles() - - # Update filtered items - self.filterItemStore() - - def updateItemStore(self, items): - self.unfilteredStore = items - - def filterItemStore(self): - sMkt = self.sMkt - selectedMetas = set() - for btn in self.marketBrowser.metaButtons: - if btn.GetValue(): - selectedMetas.update(sMkt.META_MAP[btn.metaName]) - self.filteredStore = sMkt.filterItemsByMeta(self.unfilteredStore, selectedMetas) - self.update(list(self.filteredStore)) - - def setToggles(self, forcedMetaSelect=None): - metaIDs = set() - sMkt = self.sMkt - for item in self.unfilteredStore: - metaIDs.add(sMkt.getMetaGroupIdByItem(item)) - anySelection = False - for btn in self.marketBrowser.metaButtons: - btnMetas = sMkt.META_MAP[btn.metaName] - if len(metaIDs.intersection(btnMetas)) > 0: - btn.Enable(True) - # Select all available buttons if we're searching - if self.marketBrowser.searchMode is True: - btn.SetValue(True) - # Select explicitly requested button - if forcedMetaSelect is not None: - btn.SetValue(True if forcedMetaSelect in btnMetas else False) - else: - btn.Enable(False) - btn.SetValue(False) - if btn.GetValue(): - anySelection = True - # If no buttons are pressed, press first active - if anySelection is False: - for btn in self.marketBrowser.metaButtons: - if btn.Enabled: - btn.SetValue(True) - break - - def scheduleSearch(self, event=None): - search = self.marketBrowser.search.GetLineText(0) - # Make sure we do not count wildcard as search symbol - realsearch = search.replace("*", "") - # Re-select market group if search query has zero length - if len(realsearch) == 0: - self.selectionMade() - return - # Show nothing if query is too short - elif len(realsearch) < 3: - self.clearSearch() - return - - self.marketBrowser.searchMode = True - self.sMkt.searchItems(search, self.populateSearch) - - def clearSearch(self, event=None): - # Wipe item store and update everything to accomodate with it - # If clearSearch was generated by SearchCtrl's Cancel button, clear the content also - - if event: - self.marketBrowser.search.Clear() - - self.marketBrowser.searchMode = False - self.updateItemStore(set()) - self.setToggles() - self.filterItemStore() - - def populateSearch(self, items): - # If we're no longer searching, dump the results - if self.marketBrowser.searchMode is False: - return - self.updateItemStore(items) - self.setToggles() - self.filterItemStore() - - def itemSort(self, item): - sMkt = self.sMkt - catname = sMkt.getCategoryByItem(item).name - try: - mktgrpid = sMkt.getMarketGroupByItem(item).ID - except AttributeError: - mktgrpid = None - print("unable to find market group for", item.name) - parentname = sMkt.getParentItemByItem(item).name - # Get position of market group - metagrpid = sMkt.getMetaGroupIdByItem(item) - metatab = self.metaMap.get(metagrpid) - metalvl = self.metalvls.get(item.ID, 0) - return (catname, mktgrpid, parentname, metatab, metalvl, item.name) - - def contextMenu(self, event): - # Check if something is selected, if so, spawn the menu for it - sel = self.GetFirstSelected() - if sel == -1: - return - - item = self.active[sel] - - sMkt = self.sMkt - sourceContext = "marketItemGroup" if self.marketBrowser.searchMode is False else "marketItemMisc" - itemContext = sMkt.getCategoryByItem(item).name - - menu = ContextMenu.getMenu((item,), (sourceContext, itemContext)) - self.PopupMenu(menu) - - def populate(self, items): - if len(items) > 0: - # Get dictionary with meta level attribute - sAttr = Attribute.getInstance() - attrs = sAttr.getAttributeInfo("metaLevel") - sMkt = self.sMkt - self.metalvls = sMkt.directAttrRequest(items, attrs) - # Clear selection - self.deselectItems() - # Perform sorting, using item's meta levels besides other stuff - items.sort(key=self.itemSort) - # Mark current item list as active - self.active = items - # Show them - d.Display.populate(self, items) - - def refresh(self, items): - if len(items) > 1: - # Get dictionary with meta level attribute - sAttr = Attribute.getInstance() - attrs = sAttr.getAttributeInfo("metaLevel") - sMkt = self.sMkt - self.metalvls = sMkt.directAttrRequest(items, attrs) - # Re-sort stuff - items.sort(key=self.itemSort) - - for i, item in enumerate(items[:9]): - # set shortcut info for first 9 modules - item.marketShortcut = i + 1 - - d.Display.refresh(self, items) - - def makeReverseMetaMap(self): - """ - Form map which tells in which tab items of given metagroup are located - """ - revmap = {} - i = 0 - for mgids in self.sMkt.META_MAP.itervalues(): - for mgid in mgids: - revmap[mgid] = i - i += 1 - return revmap +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +import wx +from service.market import Market +from service.attribute import Attribute +import gui.display as d +import gui.PFSearchBox as SBox +from gui.cachingImageList import CachingImageList +from gui.contextMenu import ContextMenu +from gui.bitmapLoader import BitmapLoader + +ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent() + +RECENTLY_USED_MODULES = -2 +MAX_RECENTLY_USED_MODULES = 20 + + +class MarketBrowser(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent) + vbox = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(vbox) + + # Add a search box on top + self.search = SearchBox(self) + vbox.Add(self.search, 0, wx.EXPAND) + + self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) + vbox.Add(self.splitter, 1, wx.EXPAND) + + # Grab market service instance and create child objects + self.sMkt = Market.getInstance() + self.searchMode = False + self.marketView = MarketTree(self.splitter, self) + self.itemView = ItemView(self.splitter, self) + + self.splitter.SplitHorizontally(self.marketView, self.itemView) + self.splitter.SetMinimumPaneSize(250) + + # Setup our buttons for metaGroup selection + # Same fix as for search box on macs, + # need some pixels of extra space or everything clips and is ugly + p = wx.Panel(self) + box = wx.BoxSizer(wx.HORIZONTAL) + p.SetSizer(box) + vbox.Add(p, 0, wx.EXPAND) + self.metaButtons = [] + for name in self.sMkt.META_MAP.keys(): + btn = wx.ToggleButton(p, wx.ID_ANY, name.capitalize(), style=wx.BU_EXACTFIT) + setattr(self, name, btn) + box.Add(btn, 1, wx.ALIGN_CENTER) + btn.Bind(wx.EVT_TOGGLEBUTTON, self.toggleMetaButton) + btn.metaName = name + self.metaButtons.append(btn) + # Make itemview to set toggles according to list contents + self.itemView.setToggles() + + p.SetMinSize((wx.SIZE_AUTO_WIDTH, btn.GetSize()[1] + 5)) + + def toggleMetaButton(self, event): + """Process clicks on toggle buttons""" + ctrl = wx.GetMouseState().CmdDown() + ebtn = event.EventObject + if not ctrl: + for btn in self.metaButtons: + if btn.Enabled: + if btn == ebtn: + btn.SetValue(True) + else: + btn.SetValue(False) + else: + # Note: using the 'wrong' value for clicked button might seem weird, + # But the button is toggled by wx and we should deal with it + activeBtns = set() + for btn in self.metaButtons: + if (btn.GetValue() is True and btn != ebtn) or (btn.GetValue() is False and btn == ebtn): + activeBtns.add(btn) + # Do 'nothing' if we're trying to turn last active button off + if len(activeBtns) == 1 and activeBtns.pop() == ebtn: + # Keep button in the same state + ebtn.SetValue(True) + return + # Leave old unfiltered list contents, just re-filter them and show + self.itemView.filterItemStore() + + def jump(self, item): + self.marketView.jump(item) + + +class SearchBox(SBox.PFSearchBox): + def __init__(self, parent, **kwargs): + SBox.PFSearchBox.__init__(self, parent, **kwargs) + cancelBitmap = BitmapLoader.getBitmap("fit_delete_small", "gui") + searchBitmap = BitmapLoader.getBitmap("fsearch_small", "gui") + self.SetSearchBitmap(searchBitmap) + self.SetCancelBitmap(cancelBitmap) + self.ShowSearchButton() + self.ShowCancelButton() + + +class MarketTree(wx.TreeCtrl): + def __init__(self, parent, marketBrowser): + wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) + self.root = self.AddRoot("root") + + self.imageList = CachingImageList(16, 16) + self.SetImageList(self.imageList) + + self.sMkt = marketBrowser.sMkt + self.marketBrowser = marketBrowser + + # Form market tree root + sMkt = self.sMkt + for mktGrp in sMkt.getMarketRoot(): + iconId = self.addImage(sMkt.getIconByMarketGroup(mktGrp)) + childId = self.AppendItem(self.root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) + # All market groups which were never expanded are dummies, here we assume + # that all root market groups are expandable + self.AppendItem(childId, "dummy") + self.SortChildren(self.root) + + # Add recently used modules node + rumIconId = self.addImage("market_small", "gui") + self.AppendItem(self.root, "Recently Used Modules", rumIconId, data=wx.TreeItemData(RECENTLY_USED_MODULES)) + + # Bind our lookup method to when the tree gets expanded + self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) + + def addImage(self, iconFile, location="icons"): + if iconFile is None: + return -1 + return self.imageList.GetImageIndex(iconFile, location) + + def expandLookup(self, event): + """Process market tree expands""" + root = event.Item + child = self.GetFirstChild(root)[0] + # If child of given market group is a dummy + if self.GetItemText(child) == "dummy": + # Delete it + self.Delete(child) + # And add real market group contents + sMkt = self.sMkt + currentMktGrp = sMkt.getMarketGroup(self.GetPyData(root), eager="children") + for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): + # If market should have items but it doesn't, do not show it + if sMkt.marketGroupValidityCheck(childMktGrp) is False: + continue + iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp)) + try: + childId = self.AppendItem(root, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) + except: + continue + if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: + self.AppendItem(childId, "dummy") + + self.SortChildren(root) + + def jump(self, item): + """Open market group and meta tab of given item""" + self.marketBrowser.searchMode = False + sMkt = self.sMkt + mg = sMkt.getMarketGroupByItem(item) + metaId = sMkt.getMetaGroupIdByItem(item) + + jumpList = [] + while mg is not None: + jumpList.append(mg.ID) + mg = mg.parent + + for id in sMkt.ROOT_MARKET_GROUPS: + if id in jumpList: + jumpList = jumpList[:jumpList.index(id) + 1] + + item = self.root + for i in range(len(jumpList) - 1, -1, -1): + target = jumpList[i] + child, cookie = self.GetFirstChild(item) + while self.GetItemPyData(child) != target: + child, cookie = self.GetNextChild(item, cookie) + + item = child + self.Expand(item) + + self.SelectItem(item) + self.marketBrowser.itemView.selectionMade(forcedMetaSelect=metaId) + + +class ItemView(d.Display): + DEFAULT_COLS = ["Base Icon", + "Base Name", + "attr:power,,,True", + "attr:cpu,,,True"] + + def __init__(self, parent, marketBrowser): + d.Display.__init__(self, parent) + marketBrowser.Bind(wx.EVT_TREE_SEL_CHANGED, self.selectionMade) + + self.unfilteredStore = set() + self.filteredStore = set() + self.recentlyUsedModules = set() + self.sMkt = marketBrowser.sMkt + self.searchMode = marketBrowser.searchMode + + self.marketBrowser = marketBrowser + self.marketView = marketBrowser.marketView + + # Make sure our search actually does interesting stuff + self.marketBrowser.search.Bind(SBox.EVT_TEXT_ENTER, self.scheduleSearch) + self.marketBrowser.search.Bind(SBox.EVT_SEARCH_BTN, self.scheduleSearch) + self.marketBrowser.search.Bind(SBox.EVT_CANCEL_BTN, self.clearSearch) + self.marketBrowser.search.Bind(SBox.EVT_TEXT, self.scheduleSearch) + + # Make sure WE do interesting stuff too + self.Bind(wx.EVT_CONTEXT_MENU, self.contextMenu) + self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated) + self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag) + + # Make reverse map, used by sorter + self.metaMap = self.makeReverseMetaMap() + + # Fill up recently used modules set + for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: + self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) + + def startDrag(self, event): + row = self.GetFirstSelected() + + if row != -1: + data = wx.PyTextDataObject() + data.SetText("market:" + str(self.active[row].ID)) + + dropSource = wx.DropSource(self) + dropSource.SetData(data) + dropSource.DoDragDrop() + + def itemActivated(self, event=None): + # Check if something is selected, if so, spawn the menu for it + sel = self.GetFirstSelected() + if sel == -1: + return + + if self.mainFrame.getActiveFit(): + + self.storeRecentlyUsedMarketItem(self.active[sel].ID) + self.recentlyUsedModules = set() + for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: + self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) + + wx.PostEvent(self.mainFrame, ItemSelected(itemID=self.active[sel].ID)) + + def storeRecentlyUsedMarketItem(self, itemID): + if len(self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]) > MAX_RECENTLY_USED_MODULES: + self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].pop(0) + + self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].append(itemID) + + def selectionMade(self, event=None, forcedMetaSelect=None): + self.marketBrowser.searchMode = False + # Grab the threeview selection and check if it's fine + sel = self.marketView.GetSelection() + if sel.IsOk(): + # Get data field of the selected item (which is a marketGroup ID if anything was selected) + seldata = self.marketView.GetPyData(sel) + if seldata is not None and seldata != RECENTLY_USED_MODULES: + # If market group treeview item doesn't have children (other market groups or dummies), + # then it should have items in it and we want to request them + if self.marketView.ItemHasChildren(sel) is False: + sMkt = self.sMkt + # Get current market group + mg = sMkt.getMarketGroup(seldata, eager=("items", "items.metaGroup")) + # Get all its items + items = sMkt.getItemsByMarketGroup(mg) + else: + items = set() + else: + # If method was called but selection wasn't actually made or we have a hit on recently used modules + if seldata == RECENTLY_USED_MODULES: + items = self.recentlyUsedModules + else: + items = set() + + # Fill store + self.updateItemStore(items) + + # Set toggle buttons / use search mode flag if recently used modules category is selected (in order to have all modules listed and not filtered) + if seldata is not RECENTLY_USED_MODULES: + self.setToggles(forcedMetaSelect=forcedMetaSelect) + else: + self.marketBrowser.searchMode = True + self.setToggles() + + # Update filtered items + self.filterItemStore() + + def updateItemStore(self, items): + self.unfilteredStore = items + + def filterItemStore(self): + sMkt = self.sMkt + selectedMetas = set() + for btn in self.marketBrowser.metaButtons: + if btn.GetValue(): + selectedMetas.update(sMkt.META_MAP[btn.metaName]) + self.filteredStore = sMkt.filterItemsByMeta(self.unfilteredStore, selectedMetas) + self.update(list(self.filteredStore)) + + def setToggles(self, forcedMetaSelect=None): + metaIDs = set() + sMkt = self.sMkt + for item in self.unfilteredStore: + metaIDs.add(sMkt.getMetaGroupIdByItem(item)) + anySelection = False + for btn in self.marketBrowser.metaButtons: + btnMetas = sMkt.META_MAP[btn.metaName] + if len(metaIDs.intersection(btnMetas)) > 0: + btn.Enable(True) + # Select all available buttons if we're searching + if self.marketBrowser.searchMode is True: + btn.SetValue(True) + # Select explicitly requested button + if forcedMetaSelect is not None: + btn.SetValue(True if forcedMetaSelect in btnMetas else False) + else: + btn.Enable(False) + btn.SetValue(False) + if btn.GetValue(): + anySelection = True + # If no buttons are pressed, press first active + if anySelection is False: + for btn in self.marketBrowser.metaButtons: + if btn.Enabled: + btn.SetValue(True) + break + + def scheduleSearch(self, event=None): + search = self.marketBrowser.search.GetLineText(0) + # Make sure we do not count wildcard as search symbol + realsearch = search.replace("*", "") + # Re-select market group if search query has zero length + if len(realsearch) == 0: + self.selectionMade() + return + # Show nothing if query is too short + elif len(realsearch) < 3: + self.clearSearch() + return + + self.marketBrowser.searchMode = True + self.sMkt.searchItems(search, self.populateSearch) + + def clearSearch(self, event=None): + # Wipe item store and update everything to accomodate with it + # If clearSearch was generated by SearchCtrl's Cancel button, clear the content also + + if event: + self.marketBrowser.search.Clear() + + self.marketBrowser.searchMode = False + self.updateItemStore(set()) + self.setToggles() + self.filterItemStore() + + def populateSearch(self, items): + # If we're no longer searching, dump the results + if self.marketBrowser.searchMode is False: + return + self.updateItemStore(items) + self.setToggles() + self.filterItemStore() + + def itemSort(self, item): + sMkt = self.sMkt + catname = sMkt.getCategoryByItem(item).name + try: + mktgrpid = sMkt.getMarketGroupByItem(item).ID + except AttributeError: + mktgrpid = None + print("unable to find market group for", item.name) + parentname = sMkt.getParentItemByItem(item).name + # Get position of market group + metagrpid = sMkt.getMetaGroupIdByItem(item) + metatab = self.metaMap.get(metagrpid) + metalvl = self.metalvls.get(item.ID, 0) + return (catname, mktgrpid, parentname, metatab, metalvl, item.name) + + def contextMenu(self, event): + # Check if something is selected, if so, spawn the menu for it + sel = self.GetFirstSelected() + if sel == -1: + return + + item = self.active[sel] + + sMkt = self.sMkt + sourceContext = "marketItemGroup" if self.marketBrowser.searchMode is False else "marketItemMisc" + itemContext = sMkt.getCategoryByItem(item).name + + menu = ContextMenu.getMenu((item,), (sourceContext, itemContext)) + self.PopupMenu(menu) + + def populate(self, items): + if len(items) > 0: + # Get dictionary with meta level attribute + sAttr = Attribute.getInstance() + attrs = sAttr.getAttributeInfo("metaLevel") + sMkt = self.sMkt + self.metalvls = sMkt.directAttrRequest(items, attrs) + # Clear selection + self.deselectItems() + # Perform sorting, using item's meta levels besides other stuff + items.sort(key=self.itemSort) + # Mark current item list as active + self.active = items + # Show them + d.Display.populate(self, items) + + def refresh(self, items): + if len(items) > 1: + # Get dictionary with meta level attribute + sAttr = Attribute.getInstance() + attrs = sAttr.getAttributeInfo("metaLevel") + sMkt = self.sMkt + self.metalvls = sMkt.directAttrRequest(items, attrs) + # Re-sort stuff + items.sort(key=self.itemSort) + + for i, item in enumerate(items[:9]): + # set shortcut info for first 9 modules + item.marketShortcut = i + 1 + + d.Display.refresh(self, items) + + def makeReverseMetaMap(self): + """ + Form map which tells in which tab items of given metagroup are located + """ + revmap = {} + i = 0 + for mgids in self.sMkt.META_MAP.itervalues(): + for mgid in mgids: + revmap[mgid] = i + i += 1 + return revmap diff --git a/gui/patternEditor.py b/gui/patternEditor.py index 8005d19aa..9d11a5ab9 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -164,18 +164,18 @@ class DmgPatternEditorDlg(wx.Dialog): ("Export", wx.ART_FILE_SAVE_AS, "to")) for name, art, direction in importExport: - bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) - btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) + bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) + btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - btn.SetMinSize(btn.GetSize()) - btn.SetMaxSize(btn.GetSize()) + btn.SetMinSize(btn.GetSize()) + btn.SetMaxSize(btn.GetSize()) - btn.Layout() - setattr(self, name, btn) - btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) - footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) - btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower()))) + btn.Layout() + setattr(self, name, btn) + btn.Enable(True) + btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) + footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) + btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower()))) self.Layout() bsize = self.GetBestSize() @@ -196,10 +196,10 @@ class DmgPatternEditorDlg(wx.Dialog): p = self.entityEditor.getActiveEntity() total = sum(map(lambda attr: getattr(self, "%sEdit" % attr).GetValue(), self.DAMAGE_TYPES)) for type_ in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit" % type_) - percObj = getattr(self, "%sPerc" % type_) - setattr(p, "%sAmount" % type_, editObj.GetValue()) - percObj.SetLabel("%.1f%%" % (float(editObj.GetValue()) * 100 / total if total > 0 else 0)) + editObj = getattr(self, "%sEdit" % type_) + percObj = getattr(self, "%sPerc" % type_) + setattr(p, "%sAmount" % type_, editObj.GetValue()) + percObj.SetLabel("%.1f%%" % (float(editObj.GetValue()) * 100 / total if total > 0 else 0)) self.totSizer.Layout() diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index dd257302d..1129545a6 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -25,7 +25,8 @@ logger = logging.getLogger(__name__) class AttributeEditor(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, wx.ID_ANY, title="Attribute Editor", pos=wx.DefaultPosition, - size=wx.Size(650, 600), style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.TAB_TRAVERSAL) + size=wx.Size(650, 600), + style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.TAB_TRAVERSAL) i = wx.IconFromBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) self.SetIcon(i) @@ -54,7 +55,8 @@ class AttributeEditor(wx.Frame): mainSizer = wx.BoxSizer(wx.HORIZONTAL) leftSizer = wx.BoxSizer(wx.VERTICAL) - leftPanel = wx.Panel(panel, wx.ID_ANY, style=wx.DOUBLE_BORDER if 'wxMSW' in wx.PlatformInfo else wx.SIMPLE_BORDER) + leftPanel = wx.Panel(panel, wx.ID_ANY, + style=wx.DOUBLE_BORDER if 'wxMSW' in wx.PlatformInfo else wx.SIMPLE_BORDER) self.searchBox = SearchBox(leftPanel) self.itemView = ItemView(leftPanel) @@ -66,7 +68,8 @@ class AttributeEditor(wx.Frame): mainSizer.Add(leftPanel, 1, wx.ALL | wx.EXPAND, 5) rightSizer = wx.BoxSizer(wx.VERTICAL) - self.btnRemoveOverrides = wx.Button(panel, wx.ID_ANY, u"Remove Overides for Item", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnRemoveOverrides = wx.Button(panel, wx.ID_ANY, u"Remove Overides for Item", wx.DefaultPosition, + wx.DefaultSize, 0) self.pg = AttributeGrid(panel) rightSizer.Add(self.pg, 1, wx.ALL | wx.EXPAND, 5) rightSizer.Add(self.btnRemoveOverrides, 0, wx.ALL | wx.EXPAND, 5) @@ -202,7 +205,8 @@ class ItemView(d.Display): class AttributeGrid(wxpg.PropertyGrid): def __init__(self, parent): - wxpg.PropertyGrid.__init__(self, parent, style=wxpg.PG_HIDE_MARGIN | wxpg.PG_HIDE_CATEGORIES | wxpg.PG_BOLD_MODIFIED | wxpg.PG_TOOLTIPS) + wxpg.PropertyGrid.__init__(self, parent, + style=wxpg.PG_HIDE_MARGIN | wxpg.PG_HIDE_CATEGORIES | wxpg.PG_BOLD_MODIFIED | wxpg.PG_TOOLTIPS) self.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS) self.item = None diff --git a/gui/pyfatogglepanel.py b/gui/pyfatogglepanel.py index d34985115..db37216ba 100644 --- a/gui/pyfatogglepanel.py +++ b/gui/pyfatogglepanel.py @@ -27,7 +27,8 @@ from gui.bitmapLoader import BitmapLoader class TogglePanel(wx.Panel): def __init__(self, parent, forceLayout=-1): - wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(-1, -1), + style=wx.TAB_TRAVERSAL) self._toggle = 1 self.parent = parent diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index 83fb4327d..84f7bf98b 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -79,7 +79,6 @@ class TargetResistsEntityEditor(EntityEditor): class ResistsEditorDlg(wx.Dialog): - DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") def __init__(self, parent): @@ -120,7 +119,8 @@ class ResistsEditorDlg(wx.Dialog): setattr(self, "%sEdit" % type_, wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, defSize)) editObj = getattr(self, "%sEdit" % type_) resistEditSizer.Add(editObj, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) - resistEditSizer.Add(wx.StaticText(self, wx.ID_ANY, u"%", wx.DefaultPosition, wx.DefaultSize, 0), 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) + resistEditSizer.Add(wx.StaticText(self, wx.ID_ANY, u"%", wx.DefaultPosition, wx.DefaultSize, 0), 0, + wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated) # Color we use to reset invalid value color diff --git a/gui/setEditor.py b/gui/setEditor.py index 380c7986a..0118fbe03 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -146,17 +146,17 @@ class ImplantSetEditorDlg(wx.Dialog): ("Export", wx.ART_FILE_SAVE_AS, "to")) for name, art, direction in importExport: - bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) - btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) + bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) + btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - btn.SetMinSize(btn.GetSize()) - btn.SetMaxSize(btn.GetSize()) + btn.SetMinSize(btn.GetSize()) + btn.SetMaxSize(btn.GetSize()) - btn.Layout() - setattr(self, name, btn) - btn.Enable(True) - btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction)) - footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) + btn.Layout() + setattr(self, name, btn) + btn.Enable(True) + btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction)) + footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5) diff --git a/gui/sfBrowserItem.py b/gui/sfBrowserItem.py index 9b12fcf01..047c04bc6 100644 --- a/gui/sfBrowserItem.py +++ b/gui/sfBrowserItem.py @@ -13,7 +13,8 @@ BTN_DISABLED = 8 class PFBaseButton(object): - def __init__(self, normalBitmap=wx.NullBitmap, label="", callback=None, hoverBitmap=None, disabledBitmap=None, show=True): + def __init__(self, normalBitmap=wx.NullBitmap, label="", callback=None, hoverBitmap=None, disabledBitmap=None, + show=True): self.normalBmp = normalBitmap self.dropShadowOpacity = 0.2 diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 8c27965b9..6a15678e2 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -20,7 +20,6 @@ from gui.PFListPane import PFListPane from gui.contextMenu import ContextMenu from gui.bitmapLoader import BitmapLoader - FitRenamed, EVT_FIT_RENAMED = wx.lib.newevent.NewEvent() FitSelected, EVT_FIT_SELECTED = wx.lib.newevent.NewEvent() FitRemoved, EVT_FIT_REMOVED = wx.lib.newevent.NewEvent() @@ -67,7 +66,8 @@ class PFWidgetsContainer(PFListPane): class RaceSelector(wx.Window): - def __init__(self, parent, id=wx.ID_ANY, label="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, layout=wx.VERTICAL, animate=False): + def __init__(self, parent, id=wx.ID_ANY, label="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, + layout=wx.VERTICAL, animate=False): wx.Window.__init__(self, parent, id, pos=pos, size=size, style=style) self.animTimerID = wx.NewId() @@ -344,11 +344,16 @@ class NavigationPanel(SFItem.SFBrowserItem): self.switchBmp = self.AdjustChannels(self.switchBmpH) self.newBmp = self.AdjustChannels(self.newBmpH) - self.toolbar.AddButton(self.resetBmp, "Ship groups", clickCallback=self.OnHistoryReset, hoverBitmap=self.resetBmpH) + self.toolbar.AddButton(self.resetBmp, "Ship groups", clickCallback=self.OnHistoryReset, + hoverBitmap=self.resetBmpH) self.toolbar.AddButton(self.rewBmp, "Back", clickCallback=self.OnHistoryBack, hoverBitmap=self.rewBmpH) - self.btnNew = self.toolbar.AddButton(self.newBmp, "New fitting", clickCallback=self.OnNewFitting, hoverBitmap=self.newBmpH, show=False) - self.btnSwitch = self.toolbar.AddButton(self.switchBmpD, "Hide empty ship groups", clickCallback=self.ToggleEmptyGroupsView, hoverBitmap=self.switchBmpH, show=False) - self.toolbar.AddButton(self.searchBmp, "Search fittings", clickCallback=self.ToggleSearchBox, hoverBitmap=self.searchBmpH) + self.btnNew = self.toolbar.AddButton(self.newBmp, "New fitting", clickCallback=self.OnNewFitting, + hoverBitmap=self.newBmpH, show=False) + self.btnSwitch = self.toolbar.AddButton(self.switchBmpD, "Hide empty ship groups", + clickCallback=self.ToggleEmptyGroupsView, hoverBitmap=self.switchBmpH, + show=False) + self.toolbar.AddButton(self.searchBmp, "Search fittings", clickCallback=self.ToggleSearchBox, + hoverBitmap=self.searchBmpH) self.padding = 4 self.lastSearch = "" @@ -357,7 +362,9 @@ class NavigationPanel(SFItem.SFBrowserItem): self.fontSmall = wx.Font(fonts.SMALL, wx.SWISS, wx.NORMAL, wx.NORMAL) w, h = size - self.BrowserSearchBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1), wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) + self.BrowserSearchBox = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, + (-1, h - 2 if 'wxGTK' in wx.PlatformInfo else -1), + wx.TE_PROCESS_ENTER | (wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0)) self.BrowserSearchBox.Show(False) self.BrowserSearchBox.Bind(wx.EVT_TEXT_ENTER, self.OnBrowserSearchBoxEnter) @@ -882,7 +889,9 @@ class ShipBrowser(wx.Panel): for ship in ships: shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits - self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, len(sFit.getFitsWithShip(ship.ID))), ship.race)) + self.lpane.AddWidget( + ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, len(sFit.getFitsWithShip(ship.ID))), + ship.race)) for ID, name, shipID, shipName, booster, timestamp in fitList: ship = sMkt.getItem(shipID) @@ -923,7 +932,8 @@ class ShipBrowser(wx.Panel): if fits: for fit in fits: - shipTrait = fit.ship.traits.traitText if (fit.ship.traits is not None) else "" # empty string if no traits + shipTrait = fit.ship.traits.traitText if ( + fit.ship.traits is not None) else "" # empty string if no traits self.lpane.AddWidget(FitItem( self.lpane, @@ -1067,6 +1077,7 @@ class CategoryItem(SFItem.SFBrowserItem): mdc.DrawText(categoryName, self.catx, self.caty) + # ============================================================================= # Waiting for total #fits impl in eos/service # @@ -1140,7 +1151,8 @@ class ShipItem(SFItem.SFBrowserItem): self.editWidth = 150 self.padding = 4 - self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s fit" % self.shipName, wx.DefaultPosition, (120, -1), wx.TE_PROCESS_ENTER) + self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s fit" % self.shipName, wx.DefaultPosition, (120, -1), + wx.TE_PROCESS_ENTER) self.tcFitName.Show(False) self.newBtn = self.toolbar.AddButton(self.newBmp, "New", self.newBtnCB) @@ -1331,7 +1343,8 @@ class ShipItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontBig) - psname = drawUtils.GetPartialText(mdc, shipName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + psname = drawUtils.GetPartialText(mdc, shipName, + self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) mdc.DrawText(psname, self.textStartx, self.shipNamey) @@ -1353,7 +1366,8 @@ class ShipItem(SFItem.SFBrowserItem): class PFBitmapFrame(wx.Frame): def __init__(self, parent, pos, bitmap): - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=pos, size=wx.DefaultSize, style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=pos, size=wx.DefaultSize, + style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) img = bitmap.ConvertToImage() img = img.ConvertToGreyscale() bitmap = wx.BitmapFromImage(img) @@ -1409,7 +1423,8 @@ class PFBitmapFrame(wx.Frame): class FitItem(SFItem.SFBrowserItem): - def __init__(self, parent, fitID=None, shipFittingInfo=("Test", "TestTrait", "cnc's avatar", 0, 0), shipID=None, itemData=None, + def __init__(self, parent, fitID=None, shipFittingInfo=("Test", "TestTrait", "cnc's avatar", 0, 0), shipID=None, + itemData=None, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 40), style=0): @@ -1487,7 +1502,8 @@ class FitItem(SFItem.SFBrowserItem): self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.renameBtnCB) self.toolbar.AddButton(self.deleteBmp, "Delete", self.deleteBtnCB) - self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fitName, wx.DefaultPosition, (self.editWidth, -1), wx.TE_PROCESS_ENTER) + self.tcFitName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fitName, wx.DefaultPosition, (self.editWidth, -1), + wx.TE_PROCESS_ENTER) if self.shipBrowser.fitIDMustEditName != self.fitID: self.tcFitName.Show(False) @@ -1852,7 +1868,8 @@ class FitItem(SFItem.SFBrowserItem): fitDate = time.localtime(self.timestamp) fitLocalDate = "%d/%02d/%02d %02d:%02d" % (fitDate[0], fitDate[1], fitDate[2], fitDate[3], fitDate[4]) - pfdate = drawUtils.GetPartialText(mdc, fitLocalDate, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + pfdate = drawUtils.GetPartialText(mdc, fitLocalDate, + self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) mdc.DrawText(pfdate, self.textStartx, self.timestampy) @@ -1861,7 +1878,8 @@ class FitItem(SFItem.SFBrowserItem): mdc.SetFont(self.fontBig) - psname = drawUtils.GetPartialText(mdc, self.fitName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + psname = drawUtils.GetPartialText(mdc, self.fitName, + self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) mdc.DrawText(psname, self.textStartx, self.fitNamey) diff --git a/gui/statsPane.py b/gui/statsPane.py index 9645f06dd..5d20f22ef 100644 --- a/gui/statsPane.py +++ b/gui/statsPane.py @@ -81,7 +81,8 @@ class StatsPane(wx.Panel): mainSizer.Add(tp, 0, wx.EXPAND | wx.LEFT, 3) if i < maxviews - 1: - mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, style=wx.HORIZONTAL), 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 2) + mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, style=wx.HORIZONTAL), 0, + wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 2) i += 1 tp.OnStateChange(tp.GetBestSize()) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 7b148c546..8a88f9e17 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -23,79 +23,89 @@ import config import dateutil.parser from service import settings -class UpdateDialog(wx.Dialog): +class UpdateDialog(wx.Dialog): def __init__(self, parent, release): - wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "Pyfa Update", pos = wx.DefaultPosition, size = wx.Size( 400,300 ), style = wx.DEFAULT_DIALOG_STYLE ) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Pyfa Update", pos=wx.DefaultPosition, + size=wx.Size(400, 300), style=wx.DEFAULT_DIALOG_STYLE) self.UpdateSettings = settings.UpdateSettings.getInstance() self.releaseInfo = release - self.SetSizeHintsSz( wx.DefaultSize, wx.DefaultSize ) + self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - headSizer = wx.BoxSizer( wx.HORIZONTAL ) + headSizer = wx.BoxSizer(wx.HORIZONTAL) - self.headingText = wx.StaticText( self, wx.ID_ANY, "Pyfa Update Available!", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) - self.headingText.Wrap( -1 ) - self.headingText.SetFont( wx.Font( 14, 74, 90, 92, False) ) + self.headingText = wx.StaticText(self, wx.ID_ANY, "Pyfa Update Available!", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_CENTRE) + self.headingText.Wrap(-1) + self.headingText.SetFont(wx.Font(14, 74, 90, 92, False)) - headSizer.Add( self.headingText, 1, wx.ALL, 5 ) - mainSizer.Add( headSizer, 0, wx.EXPAND, 5 ) + headSizer.Add(self.headingText, 1, wx.ALL, 5) + mainSizer.Add(headSizer, 0, wx.EXPAND, 5) - mainSizer.Add( wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND |wx.ALL, 5 ) + mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND | wx.ALL, 5) - versionSizer = wx.BoxSizer( wx.HORIZONTAL ) + versionSizer = wx.BoxSizer(wx.HORIZONTAL) - if(self.releaseInfo['prerelease']): - self.releaseText = wx.StaticText( self, wx.ID_ANY, "Pre-release", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.releaseText.SetFont( wx.Font( 12, 74, 90, 92, False) ) - self.releaseText.SetForegroundColour( wx.Colour( 230, 0, 0 ) ) + if (self.releaseInfo['prerelease']): + self.releaseText = wx.StaticText(self, wx.ID_ANY, "Pre-release", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) + self.releaseText.SetFont(wx.Font(12, 74, 90, 92, False)) + self.releaseText.SetForegroundColour(wx.Colour(230, 0, 0)) else: - self.releaseText = wx.StaticText( self, wx.ID_ANY, "Stable", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT ) - self.releaseText.SetFont( wx.Font( 12, 74, 90, 90, False) ) + self.releaseText = wx.StaticText(self, wx.ID_ANY, "Stable", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) + self.releaseText.SetFont(wx.Font(12, 74, 90, 90, False)) - self.releaseText.Wrap( -1 ) + self.releaseText.Wrap(-1) - versionSizer.Add( self.releaseText, 1, wx.ALL, 5 ) + versionSizer.Add(self.releaseText, 1, wx.ALL, 5) - self.versionText = wx.StaticText( self, wx.ID_ANY, self.releaseInfo['tag_name'], wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_LEFT ) - self.versionText.Wrap( -1 ) - self.versionText.SetFont( wx.Font( 12, 74, 90, 90, False) ) + self.versionText = wx.StaticText(self, wx.ID_ANY, self.releaseInfo['tag_name'], wx.DefaultPosition, + wx.DefaultSize, wx.ALIGN_LEFT) + self.versionText.Wrap(-1) + self.versionText.SetFont(wx.Font(12, 74, 90, 90, False)) - versionSizer.Add( self.versionText, 1, wx.ALL, 5 ) - versionSizer.AddSpacer( ( 15, 5), 0, wx.EXPAND, 5 ) + versionSizer.Add(self.versionText, 1, wx.ALL, 5) + versionSizer.AddSpacer((15, 5), 0, wx.EXPAND, 5) - mainSizer.Add( versionSizer, 0, wx.EXPAND, 5 ) - mainSizer.AddSpacer( ( 0, 5), 0, wx.EXPAND, 5 ) + mainSizer.Add(versionSizer, 0, wx.EXPAND, 5) + mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) releaseDate = dateutil.parser.parse(self.releaseInfo['published_at']) - notesSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.notesTextCtrl = wx.TextCtrl( self, wx.ID_ANY, str(releaseDate.date())+":\n\n"+self.releaseInfo['body'], wx.DefaultPosition, wx.DefaultSize, wx.TE_AUTO_URL|wx.TE_MULTILINE|wx.TE_READONLY|wx.DOUBLE_BORDER|wx.TRANSPARENT_WINDOW ) + notesSizer = wx.BoxSizer(wx.HORIZONTAL) + self.notesTextCtrl = wx.TextCtrl(self, wx.ID_ANY, str(releaseDate.date()) + ":\n\n" + self.releaseInfo['body'], + wx.DefaultPosition, wx.DefaultSize, + wx.TE_AUTO_URL | wx.TE_MULTILINE | wx.TE_READONLY | wx.DOUBLE_BORDER | wx.TRANSPARENT_WINDOW) - notesSizer.Add( self.notesTextCtrl, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 5 ) - mainSizer.Add( notesSizer, 1, wx.EXPAND, 5 ) + notesSizer.Add(self.notesTextCtrl, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) + mainSizer.Add(notesSizer, 1, wx.EXPAND, 5) - self.supressCheckbox = wx.CheckBox( self, wx.ID_ANY, "Don't remind me again for this release", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.supressCheckbox = wx.CheckBox(self, wx.ID_ANY, "Don't remind me again for this release", + wx.DefaultPosition, wx.DefaultSize, 0) self.supressCheckbox.Bind(wx.EVT_CHECKBOX, self.SuppressChange) - mainSizer.Add( self.supressCheckbox, 0, wx.ALL, 5 ) - mainSizer.Add( wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND |wx.ALL, 5 ) + mainSizer.Add(self.supressCheckbox, 0, wx.ALL, 5) + mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND | wx.ALL, 5) - actionSizer = wx.BoxSizer( wx.HORIZONTAL ) + actionSizer = wx.BoxSizer(wx.HORIZONTAL) - goSizer = wx.BoxSizer( wx.VERTICAL ) - self.downloadButton = wx.Button( self, wx.ID_ANY, "Download", wx.DefaultPosition, wx.DefaultSize, 0 ) + goSizer = wx.BoxSizer(wx.VERTICAL) + self.downloadButton = wx.Button(self, wx.ID_ANY, "Download", wx.DefaultPosition, wx.DefaultSize, 0) self.downloadButton.Bind(wx.EVT_BUTTON, self.OnDownload) - goSizer.Add( self.downloadButton, 0, wx.ALL, 5 ) - actionSizer.Add( goSizer, 1, wx.EXPAND, 5 ) + goSizer.Add(self.downloadButton, 0, wx.ALL, 5) + actionSizer.Add(goSizer, 1, wx.EXPAND, 5) self.closeButton = wx.Button(self, wx.ID_CLOSE) self.closeButton.Bind(wx.EVT_BUTTON, self.OnClose) - actionSizer.Add( self.closeButton, 0, wx.ALL, 5 ) - mainSizer.Add( actionSizer, 0, wx.EXPAND, 5 ) + actionSizer.Add(self.closeButton, 0, wx.ALL, 5) + mainSizer.Add(actionSizer, 0, wx.EXPAND, 5) - self.SetSizer( mainSizer ) + self.SetSizer(mainSizer) self.Layout() # Handle use-case of suppressing a release, then a new version becoming available. @@ -104,7 +114,7 @@ class UpdateDialog(wx.Dialog): # safely reset this setting self.UpdateSettings.set('version', None) - self.Centre( wx.BOTH ) + self.Centre(wx.BOTH) def OnClose(self, e): self.Close() @@ -116,5 +126,5 @@ class UpdateDialog(wx.Dialog): self.UpdateSettings.set('version', None) def OnDownload(self, e): - wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/'+self.releaseInfo['tag_name']) + wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/' + self.releaseInfo['tag_name']) self.OnClose(e) diff --git a/gui/utils/fonts.py b/gui/utils/fonts.py index 0ee52fb7f..eb2dd95f5 100644 --- a/gui/utils/fonts.py +++ b/gui/utils/fonts.py @@ -5,11 +5,9 @@ different wxPython versions import wx - if 'wxMac' in wx.PlatformInfo: sizes = (10, 11, 12) else: sizes = (7, 8, 9) - SMALL, NORMAL, BIG = sizes diff --git a/service/attribute.py b/service/attribute.py index 12b9b264b..41d10b80a 100644 --- a/service/attribute.py +++ b/service/attribute.py @@ -19,8 +19,10 @@ import eos.db + class Attribute(): instance = None + @classmethod def getInstance(cls): if cls.instance is None: diff --git a/service/character.py b/service/character.py index f93345224..3851c44bb 100644 --- a/service/character.py +++ b/service/character.py @@ -40,6 +40,7 @@ from eos.saveddata.fighter import Fighter as es_Fighter logger = logging.getLogger(__name__) + class CharacterImportThread(threading.Thread): def __init__(self, paths, callback): threading.Thread.__init__(self) @@ -80,6 +81,7 @@ class CharacterImportThread(threading.Thread): wx.CallAfter(self.callback) + class SkillBackupThread(threading.Thread): def __init__(self, path, saveFmt, activeFit, callback): threading.Thread.__init__(self) @@ -100,11 +102,12 @@ class SkillBackupThread(threading.Thread): with gzip.open(path, mode='wb') as backupFile: backupFile.write(backupData) else: - with open(path, mode='w',encoding='utf-8') as backupFile: + with open(path, mode='w', encoding='utf-8') as backupFile: backupFile.write(backupData) wx.CallAfter(self.callback) + class Character(object): instance = None skillReqsDict = {} @@ -151,7 +154,7 @@ class Character(object): for s in self.skillReqsDict['skills']: skillKey = str(s["skillID"]) + "::" + s["skill"] + "::" + str(int(s["level"])) if skillKey in skillsSeen: - pass # Duplicate skills confuse EVEMon + pass # Duplicate skills confuse EVEMon else: skillsSeen.add(skillKey) entry = ElementTree.SubElement(root, "entry") @@ -230,7 +233,7 @@ class Character(object): group = eos.db.getGroup(groupID) skills = [] for skill in group.items: - if skill.published == True: + if skill.published is True: skills.append((skill.ID, skill.name)) return skills @@ -305,7 +308,7 @@ class Character(object): if char.name == charName: charID = char.characterID - if charID == None: + if charID is None: return sheet = auth.character(charID).CharacterSheet() diff --git a/service/crest.py b/service/crest.py index edb6af5c5..4af0ccb89 100644 --- a/service/crest.py +++ b/service/crest.py @@ -16,16 +16,18 @@ from service.pycrest.eve import EVE logger = logging.getLogger(__name__) + class Servers(Enum): TQ = 0 SISI = 1 + class CrestModes(Enum): IMPLICIT = 0 USER = 1 -class Crest(): +class Crest(): clientIDs = { Servers.TQ: 'f9be379951c046339dc13a00e6be7704', Servers.SISI: 'af87365240d644f7950af563b8418bad' @@ -36,9 +38,10 @@ class Crest(): clientTest = True _instance = None + @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = Crest() return cls._instance @@ -74,7 +77,8 @@ class Crest(): # Base EVE connection that is copied to all characters self.eve = EVE( - client_id=self.settings.get('clientID') if self.settings.get('mode') == CrestModes.USER else self.clientIDs.get(self.settings.get('server')), + client_id=self.settings.get('clientID') if self.settings.get( + 'mode') == CrestModes.USER else self.clientIDs.get(self.settings.get('server')), api_key=self.settings.get('clientSecret') if self.settings.get('mode') == CrestModes.USER else None, redirect_uri=self.clientCallback, testing=self.isTestServer @@ -134,16 +138,16 @@ class Crest(): def getFittings(self, charID): char = self.getCrestCharacter(charID) - return char.eve.get('%scharacters/%d/fittings/'%(char.eve._authed_endpoint,char.ID)) + return char.eve.get('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID)) def postFitting(self, charID, json): - #@todo: new fitting ID can be recovered from Location header, ie: Location -> https://api-sisi.testeveonline.com/characters/1611853631/fittings/37486494/ + # @todo: new fitting ID can be recovered from Location header, ie: Location -> https://api-sisi.testeveonline.com/characters/1611853631/fittings/37486494/ char = self.getCrestCharacter(charID) - return char.eve.post('%scharacters/%d/fittings/'%(char.eve._authed_endpoint,char.ID), data=json) + return char.eve.post('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID), data=json) def delFitting(self, charID, fittingID): char = self.getCrestCharacter(charID) - return char.eve.delete('%scharacters/%d/fittings/%d/'%(char.eve._authed_endpoint, char.ID, fittingID)) + return char.eve.delete('%scharacters/%d/fittings/%d/' % (char.eve._authed_endpoint, char.ID, fittingID)) def logout(self): """Logout of implicit character""" @@ -160,7 +164,8 @@ class Crest(): logging.debug("Starting server") if self.httpd: self.stopServer() - time.sleep(1) # we need this to ensure that the previous get_request finishes, and then the socket will close + time.sleep(1) + # we need this to ensure that the previous get_request finishes, and then the socket will close self.httpd = StoppableHTTPServer(('', 6461), AuthHandler) thread.start_new_thread(self.httpd.serve, (self.handleLogin,)) @@ -175,7 +180,7 @@ class Crest(): logger.warn("OAUTH state mismatch") return - logger.debug("Handling CREST login with: %s"%message) + logger.debug("Handling CREST login with: %s" % message) if 'access_token' in message: # implicit eve = copy.deepcopy(self.eve) @@ -193,7 +198,7 @@ class Crest(): self.implicitCharacter = CrestChar(info['CharacterID'], info['CharacterName']) self.implicitCharacter.eve = eve - #self.implicitCharacter.fetchImage() + # self.implicitCharacter.fetchImage() wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.IMPLICIT)) elif 'code' in message: diff --git a/service/damagePattern.py b/service/damagePattern.py index ac5fc8b44..2343693d3 100644 --- a/service/damagePattern.py +++ b/service/damagePattern.py @@ -22,12 +22,14 @@ import copy import eos.db from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern + class ImportError(Exception): - pass + pass class DamagePattern(): instance = None + @classmethod def getInstance(cls): if cls.instance is None: diff --git a/service/eveapi.py b/service/eveapi.py index 0b02f2159..d1c9835ae 100644 --- a/service/eveapi.py +++ b/service/eveapi.py @@ -174,6 +174,7 @@ proxySSL = False _default_useragent = "eveapi.py/1.3" _useragent = None # use set_user_agent() to set this. + # ----------------------------------------------------------------------------- @@ -309,6 +310,7 @@ def _ParseXML(response, fromContext, storeFunc): return result + # ----------------------------------------------------------------------------- # API Classes # ----------------------------------------------------------------------------- @@ -414,7 +416,8 @@ class _RootContext(_Context): if retrieve_fallback: # implementor is handling fallbacks... try: - return _ParseXML(response, True, store and (lambda obj: cache.store(self._host, path, kw, response, obj))) + return _ParseXML(response, True, + store and (lambda obj: cache.store(self._host, path, kw, response, obj))) except Error as e: response = retrieve_fallback(self._host, path, kw, reason=e) if response is not None: @@ -563,7 +566,9 @@ class _Parser(object): if not self.container._cols or (numAttr > numCols): # the row data contains more attributes than were defined. self.container._cols = attributes[0::2] - self.container.append([_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)]) + self.container.append( + [_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)] + ) # this._isrow = True @@ -675,9 +680,11 @@ class _Parser(object): # into a Rowset, adding the sibling element and this one. rs = Rowset() rs.__catch = rs._name = this._name - row = [_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)] + [getattr(this, col) for col in attributes2] + row = [_castfunc(attributes[i], attributes[i + 1]) for i in xrange(0, len(attributes), 2)] + \ + [getattr(this, col) for col in attributes2] rs.append(row) - row = [getattr(sibling, attributes[i]) for i in xrange(0, len(attributes), 2)] + [getattr(sibling, col) for col in attributes2] + row = [getattr(sibling, attributes[i]) for i in xrange(0, len(attributes), 2)] + \ + [getattr(sibling, col) for col in attributes2] rs.append(row) rs._cols = [attributes[i] for i in xrange(0, len(attributes), 2)] + [col for col in attributes2] setattr(self.container, this._name, rs) diff --git a/service/fit.py b/service/fit.py index 877ce4d94..61b8ccc3b 100644 --- a/service/fit.py +++ b/service/fit.py @@ -17,39 +17,30 @@ # along with pyfa. If not, see . # =============================================================================== -import locale import copy -import threading import logging -import wx -from codecs import open - -import xml.parsers.expat import eos.db -from eos.types import State, Slot, Module, Drone, Fighter, Fit as FitType - -from eos.saveddata.ship import Ship as es_Ship -from eos.saveddata.citadel import Citadel as es_Citadel -from eos.saveddata.implant import Implant as es_Implant from eos.saveddata.booster import Booster as es_Booster -from eos.saveddata.module import Module as es_Module -from eos.saveddata.fighter import Fighter as es_Fighter -from eos.saveddata.drone import Drone as es_Drone from eos.saveddata.cargo import Cargo as es_Cargo -from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern - -from service.market import Market -from service.damagePattern import DamagePattern -from service.character import Character from eos.saveddata.character import Character as saveddata_Character +from eos.saveddata.citadel import Citadel as es_Citadel +from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern +from eos.saveddata.drone import Drone as es_Drone +from eos.saveddata.fighter import Fighter as es_Fighter +from eos.saveddata.implant import Implant as es_Implant +from eos.saveddata.module import Module as es_Module +from eos.saveddata.ship import Ship as es_Ship +from eos.types import State, Slot, Fit as FitType +from service.character import Character +from service.damagePattern import DamagePattern from service.fleet import Fleet +from service.market import Market from service.settings import SettingsProvider logger = logging.getLogger(__name__) - class Fit(object): instance = None diff --git a/service/fleet.py b/service/fleet.py index a3f255336..fe59e8af8 100644 --- a/service/fleet.py +++ b/service/fleet.py @@ -1,217 +1,218 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -import copy -import eos.db -from eos.saveddata.fleet import Fleet as Fleet_ -from eos.saveddata.fleet import Fleet as Wing -from eos.saveddata.fleet import Fleet as Squad - -class Fleet(object): - instance = None - @classmethod - def getInstance(cls): - if cls.instance is None: - cls.instance = Fleet() - - return cls.instance - - def __init__(self): - pass - - def getFleetList(self): - fleetList = [] - fleets = eos.db.getFleetList() - for fleet in fleets: - fleetList.append((fleet.ID, fleet.name, fleet.count())) - - return fleetList - - def getFleetByID(self, ID): - f = eos.db.getFleet(ID) - return f - - def addFleet(self): - f = Fleet_() - eos.db.save(f) - return f - - def renameFleet(self, fleet, newName): - fleet.name = newName - eos.db.commit() - - def copyFleet(self, fleet): - newFleet = copy.deepcopy(fleet) - eos.db.save(newFleet) - return newFleet - - def copyFleetByID(self, ID): - fleet = self.getFleetByID(ID) - return self.copyFleet(fleet) - - def deleteFleet(self, fleet): - eos.db.remove(fleet) - - def deleteFleetByID(self, ID): - fleet = self.getFleetByID(ID) - self.deleteFleet(fleet) - - def makeLinearFleet(self, fit): - f = Fleet_() - w = Wing() - f.wings.append(w) - s = Squad() - w.squads.append(s) - s.members.append(fit) - fit.fleet = f - eos.db.save(f) - - def setLinearFleetCom(self, boostee, booster): - #if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.wing.gang.leader is not None and booster is None: - try: - squad.wing.gang.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.wing.gang.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - def setLinearWingCom(self, boostee, booster): - #if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.wing.leader is not None and booster is None: - try: - squad.wing.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.wing.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - def setLinearSquadCom(self, boostee, booster): - #if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.leader is not None and booster is None: - try: - squad.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - - def getLinearFleet(self, fit): - sqIDs = eos.db.getSquadsIDsWithFitID(fit.ID) - if len(sqIDs) != 1: - return None - s = eos.db.getSquad(sqIDs[0]) - if len(s.members) != 1: - return None - w = s.wing - if len(w.squads) != 1: - return None - f = w.gang - if len(f.wings) != 1: - return None - return f - - def removeAssociatedFleetData(self, fit): - squadIDs = set(eos.db.getSquadsIDsWithFitID(fit.ID)) - if len(squadIDs) == 0: - return - squads = list(eos.db.getSquad(sqID) for sqID in squadIDs) - wingIDs = set(squad.wing.ID for squad in squads) - fleetIDs = set(squad.wing.gang.ID for squad in squads) - for fleetID in fleetIDs: - fleet = eos.db.getFleet(fleetID) - for wing in fleet.wings: - wingIDs.add(wing.ID) - for wingID in wingIDs: - wing = eos.db.getWing(wingID) - for squad in wing.squads: - squadIDs.add(squad.ID) - for squadID in squadIDs: - squad = eos.db.getSquad(squadID) - if squad.leader is not None: - try: - squad.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(squad) - for wingID in wingIDs: - wing = eos.db.getWing(wingID) - if wing.leader is not None: - try: - wing.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(wing) - for fleetID in fleetIDs: - fleet = eos.db.getFleet(fleetID) - if fleet.leader is not None: - try: - fleet.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(fleet) - fit.fleet = None - return - - def anyBoosters(self, squad): - wing = squad.wing - fleet = wing.gang - if squad.leader is None and wing.leader is None and fleet.leader is None: - return False - return True - - def loadLinearFleet(self, fit): - if self.getLinearFleet(fit) is None: - return None - squadID = eos.db.getSquadsIDsWithFitID(fit.ID)[0] - s = eos.db.getSquad(squadID) - w = s.wing - f = w.gang - return (f.leader, w.leader, s.leader) +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +import copy +import eos.db +from eos.saveddata.fleet import Fleet as Fleet_ +from eos.saveddata.fleet import Fleet as Wing +from eos.saveddata.fleet import Fleet as Squad + + +class Fleet(object): + instance = None + + @classmethod + def getInstance(cls): + if cls.instance is None: + cls.instance = Fleet() + + return cls.instance + + def __init__(self): + pass + + def getFleetList(self): + fleetList = [] + fleets = eos.db.getFleetList() + for fleet in fleets: + fleetList.append((fleet.ID, fleet.name, fleet.count())) + + return fleetList + + def getFleetByID(self, ID): + f = eos.db.getFleet(ID) + return f + + def addFleet(self): + f = Fleet_() + eos.db.save(f) + return f + + def renameFleet(self, fleet, newName): + fleet.name = newName + eos.db.commit() + + def copyFleet(self, fleet): + newFleet = copy.deepcopy(fleet) + eos.db.save(newFleet) + return newFleet + + def copyFleetByID(self, ID): + fleet = self.getFleetByID(ID) + return self.copyFleet(fleet) + + def deleteFleet(self, fleet): + eos.db.remove(fleet) + + def deleteFleetByID(self, ID): + fleet = self.getFleetByID(ID) + self.deleteFleet(fleet) + + def makeLinearFleet(self, fit): + f = Fleet_() + w = Wing() + f.wings.append(w) + s = Squad() + w.squads.append(s) + s.members.append(fit) + fit.fleet = f + eos.db.save(f) + + def setLinearFleetCom(self, boostee, booster): + # if boostee == booster: + # return + if self.getLinearFleet(boostee) is None: + self.removeAssociatedFleetData(boostee) + self.makeLinearFleet(boostee) + squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) + squad = eos.db.getSquad(squadIDs.pop()) + if squad.wing.gang.leader is not None and booster is None: + try: + squad.wing.gang.leader.boostsFits.remove(boostee.ID) + except KeyError: + pass + squad.wing.gang.leader = booster + if self.anyBoosters(squad) is False: + self.removeAssociatedFleetData(boostee) + from service.fit import Fit + sFit = Fit.getInstance() + sFit.recalc(boostee, withBoosters=True) + + def setLinearWingCom(self, boostee, booster): + # if boostee == booster: + # return + if self.getLinearFleet(boostee) is None: + self.removeAssociatedFleetData(boostee) + self.makeLinearFleet(boostee) + squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) + squad = eos.db.getSquad(squadIDs.pop()) + if squad.wing.leader is not None and booster is None: + try: + squad.wing.leader.boostsFits.remove(boostee.ID) + except KeyError: + pass + squad.wing.leader = booster + if self.anyBoosters(squad) is False: + self.removeAssociatedFleetData(boostee) + from service.fit import Fit + sFit = Fit.getInstance() + sFit.recalc(boostee, withBoosters=True) + + def setLinearSquadCom(self, boostee, booster): + # if boostee == booster: + # return + if self.getLinearFleet(boostee) is None: + self.removeAssociatedFleetData(boostee) + self.makeLinearFleet(boostee) + squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) + squad = eos.db.getSquad(squadIDs.pop()) + if squad.leader is not None and booster is None: + try: + squad.leader.boostsFits.remove(boostee.ID) + except KeyError: + pass + squad.leader = booster + if self.anyBoosters(squad) is False: + self.removeAssociatedFleetData(boostee) + from service.fit import Fit + sFit = Fit.getInstance() + sFit.recalc(boostee, withBoosters=True) + + def getLinearFleet(self, fit): + sqIDs = eos.db.getSquadsIDsWithFitID(fit.ID) + if len(sqIDs) != 1: + return None + s = eos.db.getSquad(sqIDs[0]) + if len(s.members) != 1: + return None + w = s.wing + if len(w.squads) != 1: + return None + f = w.gang + if len(f.wings) != 1: + return None + return f + + def removeAssociatedFleetData(self, fit): + squadIDs = set(eos.db.getSquadsIDsWithFitID(fit.ID)) + if len(squadIDs) == 0: + return + squads = list(eos.db.getSquad(sqID) for sqID in squadIDs) + wingIDs = set(squad.wing.ID for squad in squads) + fleetIDs = set(squad.wing.gang.ID for squad in squads) + for fleetID in fleetIDs: + fleet = eos.db.getFleet(fleetID) + for wing in fleet.wings: + wingIDs.add(wing.ID) + for wingID in wingIDs: + wing = eos.db.getWing(wingID) + for squad in wing.squads: + squadIDs.add(squad.ID) + for squadID in squadIDs: + squad = eos.db.getSquad(squadID) + if squad.leader is not None: + try: + squad.leader.boostsFits.remove(fit.ID) + except KeyError: + pass + eos.db.remove(squad) + for wingID in wingIDs: + wing = eos.db.getWing(wingID) + if wing.leader is not None: + try: + wing.leader.boostsFits.remove(fit.ID) + except KeyError: + pass + eos.db.remove(wing) + for fleetID in fleetIDs: + fleet = eos.db.getFleet(fleetID) + if fleet.leader is not None: + try: + fleet.leader.boostsFits.remove(fit.ID) + except KeyError: + pass + eos.db.remove(fleet) + fit.fleet = None + return + + def anyBoosters(self, squad): + wing = squad.wing + fleet = wing.gang + if squad.leader is None and wing.leader is None and fleet.leader is None: + return False + return True + + def loadLinearFleet(self, fit): + if self.getLinearFleet(fit) is None: + return None + squadID = eos.db.getSquadsIDsWithFitID(fit.ID)[0] + s = eos.db.getSquad(squadID) + w = s.wing + f = w.gang + return (f.leader, w.leader, s.leader) diff --git a/service/implantSet.py b/service/implantSet.py index add0808cc..f7df17bab 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -24,9 +24,11 @@ from service.market import Market from eos.saveddata.implant import Implant as es_Implant from eos.saveddata.implantSet import ImplantSet as es_ImplantSet + class ImportError(Exception): pass + class ImplantSets(object): instance = None diff --git a/service/market.py b/service/market.py index 4ea0ee5ca..30972795e 100644 --- a/service/market.py +++ b/service/market.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,7 +15,7 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# =============================================================================== import re import threading @@ -43,6 +43,7 @@ logger = logging.getLogger(__name__) # Event which tells threads dependent on Market that it's initialized mktRdy = threading.Event() + class ShipBrowserWorkerThread(threading.Thread): def run(self): self.queue = Queue.Queue() @@ -73,6 +74,7 @@ class ShipBrowserWorkerThread(threading.Thread): except: pass + class PriceWorkerThread(threading.Thread): def run(self): self.queue = Queue.Queue() @@ -107,6 +109,7 @@ class PriceWorkerThread(threading.Thread): self.wait[itemID] = [] self.wait[itemID].append(callback) + class SearchWorkerThread(threading.Thread): def run(self): self.cv = threading.Condition() @@ -131,7 +134,7 @@ class SearchWorkerThread(threading.Thread): elif filterOn: # filter by selected categories filter = e_Category.name.in_(filterOn) else: - filter=None + filter = None results = eos.db.searchItems(request, where=filter, join=(e_Item.group, e_Group.category), @@ -150,15 +153,18 @@ class SearchWorkerThread(threading.Thread): self.cv.notify() self.cv.release() + class Market(): instance = None + def __init__(self): self.priceCache = {} - #Init recently used module storage + # Init recently used module storage serviceMarketRecentlyUsedModules = {"pyfaMarketRecentlyUsedModules": []} - self.serviceMarketRecentlyUsedModules = SettingsProvider.getInstance().getSettings("pyfaMarketRecentlyUsedModules", serviceMarketRecentlyUsedModules) + self.serviceMarketRecentlyUsedModules = SettingsProvider.getInstance().getSettings( + "pyfaMarketRecentlyUsedModules", serviceMarketRecentlyUsedModules) # Start price fetcher self.priceWorkerThread = PriceWorkerThread() @@ -188,36 +194,37 @@ class Market(): self.les_grp.description = "" self.les_grp.icon = None self.ITEMS_FORCEGROUP = { - "Opux Luxury Yacht": self.les_grp, # One of those is wedding present at CCP fanfest, another was hijacked from ISD guy during an event + "Opux Luxury Yacht": self.les_grp, + # One of those is wedding present at CCP fanfest, another was hijacked from ISD guy during an event "Silver Magnate": self.les_grp, # Amarr Championship prize "Gold Magnate": self.les_grp, # Amarr Championship prize "Armageddon Imperial Issue": self.les_grp, # Amarr Championship prize - "Apocalypse Imperial Issue": self.les_grp, # Amarr Championship prize - "Guardian-Vexor": self.les_grp, # Illegal rewards for the Gallente Frontier Tour Lines event arc - "Megathron Federate Issue": self.les_grp, # Reward during Crielere event + "Apocalypse Imperial Issue": self.les_grp, # Amarr Championship prize + "Guardian-Vexor": self.les_grp, # Illegal rewards for the Gallente Frontier Tour Lines event arc + "Megathron Federate Issue": self.les_grp, # Reward during Crielere event "Raven State Issue": self.les_grp, # AT4 prize - "Tempest Tribal Issue": self.les_grp, # AT4 prize - "Apotheosis": self.les_grp, # 5th EVE anniversary present - "Zephyr": self.les_grp, # 2010 new year gift - "Primae": self.les_grp, # Promotion of planetary interaction - "Freki": self.les_grp, # AT7 prize - "Mimir": self.les_grp, # AT7 prize - "Utu": self.les_grp, # AT8 prize - "Adrestia": self.les_grp, # AT8 prize - "Echelon": self.les_grp, # 2011 new year gift - "Malice": self.les_grp, # AT9 prize - "Vangel": self.les_grp, # AT9 prize - "Cambion": self.les_grp, # AT10 prize - "Etana": self.les_grp, # AT10 prize - "Chremoas": self.les_grp, # AT11 prize :( - "Moracha": self.les_grp, # AT11 prize - "Stratios Emergency Responder": self.les_grp, # Issued for Somer Blink lottery - "Miasmos Quafe Ultra Edition": self.les_grp, # Gift to people who purchased FF HD stream + "Tempest Tribal Issue": self.les_grp, # AT4 prize + "Apotheosis": self.les_grp, # 5th EVE anniversary present + "Zephyr": self.les_grp, # 2010 new year gift + "Primae": self.les_grp, # Promotion of planetary interaction + "Freki": self.les_grp, # AT7 prize + "Mimir": self.les_grp, # AT7 prize + "Utu": self.les_grp, # AT8 prize + "Adrestia": self.les_grp, # AT8 prize + "Echelon": self.les_grp, # 2011 new year gift + "Malice": self.les_grp, # AT9 prize + "Vangel": self.les_grp, # AT9 prize + "Cambion": self.les_grp, # AT10 prize + "Etana": self.les_grp, # AT10 prize + "Chremoas": self.les_grp, # AT11 prize :( + "Moracha": self.les_grp, # AT11 prize + "Stratios Emergency Responder": self.les_grp, # Issued for Somer Blink lottery + "Miasmos Quafe Ultra Edition": self.les_grp, # Gift to people who purchased FF HD stream "InterBus Shuttle": self.les_grp, - "Leopard": self.les_grp, # 2013 new year gift - "Whiptail": self.les_grp, # AT12 prize - "Chameleon": self.les_grp, # AT12 prize - "Victorieux Luxury Yacht": self.les_grp, # Worlds Collide prize \o/ chinese getting owned + "Leopard": self.les_grp, # 2013 new year gift + "Whiptail": self.les_grp, # AT12 prize + "Chameleon": self.les_grp, # AT12 prize + "Victorieux Luxury Yacht": self.les_grp, # Worlds Collide prize \o/ chinese getting owned "Imp": self.les_grp, # AT13 prize "Fiend": self.les_grp, # AT13 prize } @@ -228,8 +235,8 @@ class Market(): # List of items which are forcibly published or hidden self.ITEMS_FORCEPUBLISHED = { - "Data Subverter I": False, # Not used in EVE, probably will appear with Dust link - "QA Cross Protocol Analyzer": False, # QA modules used by CCP internally + "Data Subverter I": False, # Not used in EVE, probably will appear with Dust link + "QA Cross Protocol Analyzer": False, # QA modules used by CCP internally "QA Damage Module": False, "QA ECCM": False, "QA Immunity Module": False, @@ -265,7 +272,7 @@ class Market(): # List of groups which are forcibly published self.GROUPS_FORCEPUBLISHED = { - "Prototype Exploration Ship": False } # We moved the only ship from this group to other group anyway + "Prototype Exploration Ship": False} # We moved the only ship from this group to other group anyway # Dictionary of items with forced meta groups, uses following format: # Item name: (metagroup name, parent type name) @@ -274,75 +281,96 @@ class Market(): "'Wild' Miner I": ("Storyline", "Miner I"), "Medium Nano Armor Repair Unit I": ("Tech I", "Medium Armor Repairer I"), "Large 'Reprieve' Vestment Reconstructer I": ("Storyline", "Large Armor Repairer I"), - "Khanid Navy Torpedo Launcher": ("Faction", "Torpedo Launcher I"),} + "Khanid Navy Torpedo Launcher": ("Faction", "Torpedo Launcher I"), } # Parent type name: set(item names) self.ITEMS_FORCEDMETAGROUP_R = {} for item, value in self.ITEMS_FORCEDMETAGROUP.items(): parent = value[1] - if not parent in self.ITEMS_FORCEDMETAGROUP_R: + if parent not in self.ITEMS_FORCEDMETAGROUP_R: self.ITEMS_FORCEDMETAGROUP_R[parent] = set() self.ITEMS_FORCEDMETAGROUP_R[parent].add(item) # Dictionary of items with forced market group (service assumes they have no # market group assigned in db, otherwise they'll appear in both original and forced groups) self.ITEMS_FORCEDMARKETGROUP = { - "'Alpha' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "'Codex' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "'Daemon' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "'Libram' Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "Advanced Cerebral Accelerator": 977, # Implants & Boosters > Booster - "Civilian Damage Control": 615, # Ship Equipment > Hull & Armor > Damage Controls - "Civilian EM Ward Field": 1695, # Ship Equipment > Shield > Shield Hardeners > EM Shield Hardeners - "Civilian Explosive Deflection Field": 1694, # Ship Equipment > Shield > Shield Hardeners > Explosive Shield Hardeners - "Civilian Hobgoblin": 837, # Drones > Combat Drones > Light Scout Drones - "Civilian Kinetic Deflection Field": 1693, # Ship Equipment > Shield > Shield Hardeners > Kinetic Shield Hardeners - "Civilian Light Missile Launcher": 640, # Ship Equipment > Turrets & Bays > Missile Launchers > Light Missile Launchers - "Civilian Scourge Light Missile": 920, # Ammunition & Charges > Missiles > Light Missiles > Standard Light Missiles - "Civilian Small Remote Armor Repairer": 1059, # Ship Equipment > Hull & Armor > Remote Armor Repairers > Small - "Civilian Small Remote Shield Booster": 603, # Ship Equipment > Shield > Remote Shield Boosters > Small - "Civilian Stasis Webifier": 683, # Ship Equipment > Electronic Warfare > Stasis Webifiers - "Civilian Thermic Dissipation Field": 1692, # Ship Equipment > Shield > Shield Hardeners > Thermal Shield Hardeners - "Civilian Warp Disruptor": 1935, # Ship Equipment > Electronic Warfare > Warp Disruptors - "Hardwiring - Zainou 'Sharpshooter' ZMX10": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX100": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX1000": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX11": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX110": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Hardwiring - Zainou 'Sharpshooter' ZMX1100": 1493, # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 - "Nugoehuvi Synth Blue Pill Booster": 977, # Implants & Boosters > Booster - "Prototype Cerebral Accelerator": 977, # Implants & Boosters > Booster - "Prototype Iris Probe Launcher": 712, # Ship Equipment > Turrets & Bays > Scan Probe Launchers - "Shadow": 1310, # Drones > Combat Drones > Fighter Bombers - "Sleeper Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "Standard Cerebral Accelerator": 977, # Implants & Boosters > Booster - "Talocan Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "Terran Data Analyzer I": 714, # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners - "Tetrimon Data Analyzer I": 714 # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "'Alpha' Data Analyzer I": 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "'Codex' Data Analyzer I": 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "'Daemon' Data Analyzer I": 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "'Libram' Data Analyzer I": 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "Advanced Cerebral Accelerator": 977, # Implants & Boosters > Booster + "Civilian Damage Control": 615, # Ship Equipment > Hull & Armor > Damage Controls + "Civilian EM Ward Field": 1695, # Ship Equipment > Shield > Shield Hardeners > EM Shield Hardeners + "Civilian Explosive Deflection Field": 1694, + # Ship Equipment > Shield > Shield Hardeners > Explosive Shield Hardeners + "Civilian Hobgoblin": 837, # Drones > Combat Drones > Light Scout Drones + "Civilian Kinetic Deflection Field": 1693, + # Ship Equipment > Shield > Shield Hardeners > Kinetic Shield Hardeners + "Civilian Light Missile Launcher": 640, + # Ship Equipment > Turrets & Bays > Missile Launchers > Light Missile Launchers + "Civilian Scourge Light Missile": 920, + # Ammunition & Charges > Missiles > Light Missiles > Standard Light Missiles + "Civilian Small Remote Armor Repairer": 1059, + # Ship Equipment > Hull & Armor > Remote Armor Repairers > Small + "Civilian Small Remote Shield Booster": 603, # Ship Equipment > Shield > Remote Shield Boosters > Small + "Civilian Stasis Webifier": 683, # Ship Equipment > Electronic Warfare > Stasis Webifiers + "Civilian Thermic Dissipation Field": 1692, + # Ship Equipment > Shield > Shield Hardeners > Thermal Shield Hardeners + "Civilian Warp Disruptor": 1935, # Ship Equipment > Electronic Warfare > Warp Disruptors + "Hardwiring - Zainou 'Sharpshooter' ZMX10": 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX100": 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX1000": 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX11": 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX110": 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Hardwiring - Zainou 'Sharpshooter' ZMX1100": 1493, + # Implants & Boosters > Implants > Skill Hardwiring > Missile Implants > Implant Slot 06 + "Nugoehuvi Synth Blue Pill Booster": 977, # Implants & Boosters > Booster + "Prototype Cerebral Accelerator": 977, # Implants & Boosters > Booster + "Prototype Iris Probe Launcher": 712, # Ship Equipment > Turrets & Bays > Scan Probe Launchers + "Shadow": 1310, # Drones > Combat Drones > Fighter Bombers + "Sleeper Data Analyzer I": 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "Standard Cerebral Accelerator": 977, # Implants & Boosters > Booster + "Talocan Data Analyzer I": 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "Terran Data Analyzer I": 714, + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners + "Tetrimon Data Analyzer I": 714 + # Ship Equipment > Electronics and Sensor Upgrades > Scanners > Data and Composition Scanners } self.ITEMS_FORCEDMARKETGROUP_R = self.__makeRevDict(self.ITEMS_FORCEDMARKETGROUP) self.FORCEDMARKETGROUP = { - 685: False, # Ship Equipment > Electronic Warfare > ECCM - 681: False, # Ship Equipment > Electronic Warfare > Sensor Backup Arrays + 685: False, # Ship Equipment > Electronic Warfare > ECCM + 681: False, # Ship Equipment > Electronic Warfare > Sensor Backup Arrays } # Misc definitions # 0 is for items w/o meta group - self.META_MAP = OrderedDict([("normal", frozenset((0, 1, 2, 14))), + self.META_MAP = OrderedDict([("normal", frozenset((0, 1, 2, 14))), ("faction", frozenset((4, 3))), ("complex", frozenset((6,))), ("officer", frozenset((5,)))]) - self.SEARCH_CATEGORIES = ("Drone", "Module", "Subsystem", "Charge", "Implant", "Deployable", "Fighter", "Structure", "Structure Module") + self.SEARCH_CATEGORIES = ( + "Drone", "Module", "Subsystem", "Charge", "Implant", "Deployable", "Fighter", "Structure", "Structure Module") self.SEARCH_GROUPS = ("Ice Product",) - self.ROOT_MARKET_GROUPS = (9, # Modules + self.ROOT_MARKET_GROUPS = (9, # Modules 1111, # Rigs - 157, # Drones - 11, # Ammo + 157, # Drones + 11, # Ammo 1112, # Subsystems - 24, # Implants & Boosters - 404, # Deployables + 24, # Implants & Boosters + 404, # Deployables 2202, # Structure Equipment - 2203 # Structure Modifications + 2203 # Structure Modifications ) # Tell other threads that Market is at their service mktRdy.set() @@ -560,7 +588,8 @@ class Market(): groupItems = set(group.items) if hasattr(group, 'addItems'): groupItems.update(group.addItems) - items = set(filter(lambda item: self.getPublicityByItem(item) and self.getGroupByItem(item) == group, groupItems)) + items = set( + filter(lambda item: self.getPublicityByItem(item) and self.getGroupByItem(item) == group, groupItems)) return items def getItemsByMarketGroup(self, mg, vars=True): diff --git a/service/network.py b/service/network.py index 48769a48b..481c77a41 100644 --- a/service/network.py +++ b/service/network.py @@ -29,21 +29,27 @@ from service.settings import NetworkSettings timeout = 3 socket.setdefaulttimeout(timeout) + class Error(StandardError): def __init__(self, msg=None): self.message = msg + class RequestError(StandardError): pass + class AuthenticationError(StandardError): - pass + pass + class ServerError(StandardError): - pass + pass + class TimeoutError(StandardError): - pass + pass + class Network(): # Request constants - every request must supply this, as it is checked if @@ -73,7 +79,8 @@ class Network(): raise Error("Access not enabled - please enable in Preferences > Network") # Set up some things for the request - versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion) + versionString = "{0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, + config.expansionVersion) headers = {"User-Agent": "pyfa {0} (Python-urllib2)".format(versionString)} proxy = NetworkSettings.getInstance().getProxySettings() diff --git a/service/port.py b/service/port.py index 3f2751b4f..d6645ed23 100644 --- a/service/port.py +++ b/service/port.py @@ -171,6 +171,7 @@ class Port(object): return fits """Service which houses all import/export format functions""" + @classmethod def exportCrest(cls, ofit, callback=None): # A few notes: @@ -379,6 +380,7 @@ class Port(object): if len(s) > 10: return s[:10] + "..." return s + logger.exception("Couldn't import ship data %r", [logtransform(s) for s in info]) return None @@ -821,7 +823,8 @@ class Port(object): slot = module.slot if slot not in stuff: stuff[slot] = [] - curr = module.item.name if module.item else ("[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "") + curr = module.item.name if module.item else ( + "[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "") if module.charge and sFit.serviceFittingOptions["exportCharges"]: curr += ", %s" % module.charge.name if module.state == State.OFFLINE: @@ -1075,6 +1078,7 @@ class FitBackupThread(threading.Thread): # Send done signal to GUI wx.CallAfter(self.callback, -1) + class FitImportThread(threading.Thread): def __init__(self, paths, callback): threading.Thread.__init__(self) diff --git a/service/prefetch.py b/service/prefetch.py index c3705dd54..8d8348322 100644 --- a/service/prefetch.py +++ b/service/prefetch.py @@ -1,70 +1,72 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -import threading -import os - -import config -from eos import db -from eos.db import migration -from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues -from eos.saveddata.character import Character as es_Character - -class PrefetchThread(threading.Thread): - def run(self): - # We're a daemon thread, as such, interpreter might get shut down while we do stuff - # Make sure we don't throw tracebacks to console - try: - es_Character.setSkillList(db.getItemsByCategory("Skill", eager=("effects", "attributes", "attributes.info.icon", "attributes.info.unit", "icon"))) - except: - pass - - -prefetch = PrefetchThread() -prefetch.daemon = True -prefetch.start() - -# The following code does not belong here, however until we rebuild skeletons -# to include modified pyfa.py, this is the best place to put it. See GH issue -# #176 -# @ todo: move this to pyfa.py - -# Make sure the saveddata db exists -if config.savePath and not os.path.exists(config.savePath): - os.mkdir(config.savePath) - -if config.saveDB and os.path.isfile(config.saveDB): - # If database exists, run migration after init'd database - db.saveddata_meta.create_all() - migration.update(db.saveddata_engine) - # Import default database values - # Import values that must exist otherwise Pyfa breaks - DefaultDatabaseValues.importRequiredDefaults() -elif config.saveDB: - # If database does not exist, do not worry about migration. Simply - # create and set version - db.saveddata_meta.create_all() - db.saveddata_engine.execute('PRAGMA user_version = {}'.format(migration.getAppVersion())) - # Import default database values - # Import values that must exist otherwise Pyfa breaks - DefaultDatabaseValues.importRequiredDefaults() - # Import default values for damage profiles - DefaultDatabaseValues.importDamageProfileDefaults() - # Import default values for target resist profiles - DefaultDatabaseValues.importResistProfileDefaults() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +import threading +import os + +import config +from eos import db +from eos.db import migration +from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues +from eos.saveddata.character import Character as es_Character + + +class PrefetchThread(threading.Thread): + def run(self): + # We're a daemon thread, as such, interpreter might get shut down while we do stuff + # Make sure we don't throw tracebacks to console + try: + es_Character.setSkillList(db.getItemsByCategory("Skill", eager=( + "effects", "attributes", "attributes.info.icon", "attributes.info.unit", "icon"))) + except: + pass + + +prefetch = PrefetchThread() +prefetch.daemon = True +prefetch.start() + +# The following code does not belong here, however until we rebuild skeletons +# to include modified pyfa.py, this is the best place to put it. See GH issue +# #176 +# @ todo: move this to pyfa.py + +# Make sure the saveddata db exists +if config.savePath and not os.path.exists(config.savePath): + os.mkdir(config.savePath) + +if config.saveDB and os.path.isfile(config.saveDB): + # If database exists, run migration after init'd database + db.saveddata_meta.create_all() + migration.update(db.saveddata_engine) + # Import default database values + # Import values that must exist otherwise Pyfa breaks + DefaultDatabaseValues.importRequiredDefaults() +elif config.saveDB: + # If database does not exist, do not worry about migration. Simply + # create and set version + db.saveddata_meta.create_all() + db.saveddata_engine.execute('PRAGMA user_version = {}'.format(migration.getAppVersion())) + # Import default database values + # Import values that must exist otherwise Pyfa breaks + DefaultDatabaseValues.importRequiredDefaults() + # Import default values for damage profiles + DefaultDatabaseValues.importDamageProfileDefaults() + # Import default values for target resist profiles + DefaultDatabaseValues.importResistProfileDefaults() diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index a46ad6c91..2322c704e 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -22,7 +22,6 @@ try: except ImportError: # pragma: no cover import cPickle as pickle - logger = logging.getLogger("pycrest.eve") cache_re = re.compile(r'max-age=([0-9]+)') diff --git a/service/pycrest/weak_ciphers.py b/service/pycrest/weak_ciphers.py index ad0adec03..e3a82ca15 100644 --- a/service/pycrest/weak_ciphers.py +++ b/service/pycrest/weak_ciphers.py @@ -67,7 +67,7 @@ class WeakCiphersHTTPSConnection(urllib3.connection.VerifiedHTTPSConnection): # warnings.warn(( 'System time is way off (before {0}). This will probably ' 'lead to SSL verification errors').format( - urllib3.connection.RECENT_DATE), + urllib3.connection.RECENT_DATE), SystemTimeWarning ) @@ -103,13 +103,11 @@ class WeakCiphersHTTPSConnection(urllib3.connection.VerifiedHTTPSConnection): # class WeakCiphersHTTPSConnectionPool( - urllib3.connectionpool.HTTPSConnectionPool): - + urllib3.connectionpool.HTTPSConnectionPool): ConnectionCls = WeakCiphersHTTPSConnection class WeakCiphersPoolManager(urllib3.poolmanager.PoolManager): - def _new_pool(self, scheme, host, port): if scheme == 'https': return WeakCiphersHTTPSConnectionPool(host, port, **(self.connection_pool_kw)) diff --git a/service/server.py b/service/server.py index 49e193cee..98bd138f1 100644 --- a/service/server.py +++ b/service/server.py @@ -77,7 +77,6 @@ class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler): # http://code.activestate.com/recipes/425210-simple-stoppable-server-using-socket-timeout/ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): - def server_bind(self): BaseHTTPServer.HTTPServer.server_bind(self) self.settings = CRESTSettings.getInstance() diff --git a/service/settings.py b/service/settings.py index 579d484c2..68c1a2105 100644 --- a/service/settings.py +++ b/service/settings.py @@ -119,9 +119,9 @@ class NetworkSettings(object): _instance = None # constants for serviceNetworkDefaultSettings["mode"] parameter - PROXY_MODE_NONE = 0 # 0 - No proxy + PROXY_MODE_NONE = 0 # 0 - No proxy PROXY_MODE_AUTODETECT = 1 # 1 - Auto-detected proxy settings - PROXY_MODE_MANUAL = 2 # 2 - Manual proxy settings + PROXY_MODE_MANUAL = 2 # 2 - Manual proxy settings @classmethod def getInstance(cls): @@ -254,8 +254,15 @@ class HTMLExportSettings(object): return cls._instance def __init__(self): - serviceHTMLExportDefaultSettings = {"enabled": False, "path": config.pyfaPath + os.sep + 'pyfaFits.html', "minimal": False} - self.serviceHTMLExportSettings = SettingsProvider.getInstance().getSettings("pyfaServiceHTMLExportSettings", serviceHTMLExportDefaultSettings) + serviceHTMLExportDefaultSettings = { + "enabled": False, + "path": config.pyfaPath + os.sep + 'pyfaFits.html', + "minimal": False + } + self.serviceHTMLExportSettings = SettingsProvider.getInstance().getSettings( + "pyfaServiceHTMLExportSettings", + serviceHTMLExportDefaultSettings + ) def getEnabled(self): return self.serviceHTMLExportSettings["enabled"] @@ -295,7 +302,10 @@ class UpdateSettings(object): # prerelease - If True, suppress prerelease notifications # version - Set to release tag that user does not want notifications for serviceUpdateDefaultSettings = {"prerelease": True, 'version': None} - self.serviceUpdateSettings = SettingsProvider.getInstance().getSettings("pyfaServiceUpdateSettings", serviceUpdateDefaultSettings) + self.serviceUpdateSettings = SettingsProvider.getInstance().getSettings( + "pyfaServiceUpdateSettings", + serviceUpdateDefaultSettings + ) def get(self, type): return self.serviceUpdateSettings[type] @@ -315,13 +325,15 @@ class CRESTSettings(object): return cls._instance def __init__(self): - # mode # 0 - Implicit authentication # 1 - User-supplied client details serviceCRESTDefaultSettings = {"mode": 0, "server": 0, "clientID": "", "clientSecret": "", "timeout": 60} - self.serviceCRESTSettings = SettingsProvider.getInstance().getSettings("pyfaServiceCRESTSettings", serviceCRESTDefaultSettings) + self.serviceCRESTSettings = SettingsProvider.getInstance().getSettings( + "pyfaServiceCRESTSettings", + serviceCRESTDefaultSettings + ) def get(self, type): return self.serviceCRESTSettings[type] @@ -329,5 +341,4 @@ class CRESTSettings(object): def set(self, type, value): self.serviceCRESTSettings[type] = value - # @todo: migrate fit settings (from fit service) here? diff --git a/service/targetResists.py b/service/targetResists.py index dc208e78a..2a4863c5d 100644 --- a/service/targetResists.py +++ b/service/targetResists.py @@ -24,10 +24,12 @@ from eos.saveddata.targetResists import TargetResists as es_TargetResists class ImportError(Exception): - pass + pass + class TargetResists(object): instance = None + @classmethod def getInstance(cls): if cls.instance is None: diff --git a/service/update.py b/service/update.py index 2b594bbf8..05f05efd7 100644 --- a/service/update.py +++ b/service/update.py @@ -42,7 +42,10 @@ class CheckUpdateThread(threading.Thread): try: response = network.request('https://api.github.com/repos/pyfa-org/Pyfa/releases', network.UPDATE) jsonResponse = json.loads(response.read()) - jsonResponse.sort(key=lambda x: calendar.timegm(dateutil.parser.parse(x['published_at']).utctimetuple()), reverse=True) + jsonResponse.sort( + key=lambda x: calendar.timegm(dateutil.parser.parse(x['published_at']).utctimetuple()), + reverse=True + ) for release in jsonResponse: # Suppress pre releases @@ -63,7 +66,9 @@ class CheckUpdateThread(threading.Thread): else: rVersion = release['tag_name'].replace('v', '', 1) - if config.tag is 'git' and not release['prerelease'] and self.versiontuple(rVersion) >= self.versiontuple(config.version): + if config.tag is 'git' and \ + not release['prerelease'] and \ + self.versiontuple(rVersion) >= self.versiontuple(config.version): wx.CallAfter(self.callback, release) # git (dev/Singularity) -> Stable elif config.expansionName is not "Singularity": if release['prerelease']: diff --git a/tox.ini b/tox.ini index 3ce2756f3..5eae895b9 100644 --- a/tox.ini +++ b/tox.ini @@ -12,4 +12,4 @@ commands = py.test -vv --cov Pyfa tests/ [testenv:pep8] deps = flake8 -commands = flake8 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,venv,tests,.tox,build,dist,__init__.py --ignore=E501,E731 gui_service gui eos utils config.py pyfa.py +commands = flake8 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,venv,tests,.tox,build,dist,__init__.py --ignore=E501,E731 service gui eos utils config.py pyfa.py From 28404cd8bb48c8bedc0974cf0d13c3080fe89198 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Tue, 13 Dec 2016 23:31:39 -0800 Subject: [PATCH 13/53] bajillion pep8 fixes to pass tox --- eos/db/gamedata/item.py | 4 +- eos/db/gamedata/queries.py | 8 +- eos/db/migrations/upgrade1.py | 12 +- eos/db/migrations/upgrade10.py | 1 + eos/db/migrations/upgrade11.py | 10 +- eos/db/migrations/upgrade12.py | 10 +- eos/db/migrations/upgrade13.py | 3 +- eos/db/migrations/upgrade14.py | 6 +- eos/db/migrations/upgrade15.py | 2 +- eos/db/migrations/upgrade16.py | 1 + eos/db/migrations/upgrade17.py | 5 +- eos/db/migrations/upgrade18.py | 46 +- eos/db/migrations/upgrade19.py | 8 +- eos/db/migrations/upgrade2.py | 1 + eos/db/migrations/upgrade3.py | 1 + eos/db/migrations/upgrade4.py | 10 +- eos/db/migrations/upgrade5.py | 1 + eos/db/migrations/upgrade6.py | 4 +- eos/db/migrations/upgrade7.py | 7 +- eos/db/migrations/upgrade8.py | 12 +- eos/db/migrations/upgrade9.py | 4 +- eos/db/saveddata/fighter.py | 2 +- eos/db/saveddata/fit.py | 216 +-- eos/db/saveddata/loadDefaultDatabaseValues.py | 1 + eos/db/saveddata/queries.py | 3 - eos/effects/adaptivearmorhardener.py | 69 +- eos/effects/angelsetbonus.py | 4 +- eos/effects/armoredcommanddurationbonus.py | 5 +- eos/effects/armoredcommandmindlink.py | 17 +- eos/effects/armoredcommandstrengthbonus.py | 14 +- eos/effects/caldarishipewstrengthcb.py | 6 +- eos/effects/chargebonuswarfarecharge.py | 59 +- eos/effects/commandburstaoebonus.py | 2 + eos/effects/commandburstaoerolebonus.py | 5 +- eos/effects/commandburstreloadtimebonus.py | 2 + eos/effects/commandprocessoreffect.py | 8 +- eos/effects/commandshipmultirelayeffect.py | 7 +- .../elitebonuscommanddestroyerarmored1.py | 17 +- .../elitebonuscommanddestroyerinfo1.py | 17 +- .../elitebonuscommanddestroyershield1.py | 17 +- .../elitebonuscommanddestroyerskirmish1.py | 17 +- .../elitebonuscommandshiparmoredcs3.py | 17 +- .../elitebonuscommandshipinformationcs3.py | 17 +- eos/effects/elitebonuscommandshipsiegecs3.py | 17 +- .../elitebonuscommandshipskirmishcs3.py | 17 +- .../elitebonuscoveropsnosneutfalloff1.py | 6 +- ...bonuslogisticremotearmorrepairduration3.py | 5 +- ...ogisticremotearmorrepairoptimalfalloff1.py | 10 +- eos/effects/energyneutralizerfalloff.py | 4 +- eos/effects/entityenergyneutralizerfalloff.py | 4 +- ...ceharvestingdroneoperationdurationbonus.py | 5 +- eos/effects/iceharvestingdronespecbonus.py | 8 +- eos/effects/industrialbonusdronedamage.py | 2 +- .../informationcommanddurationbonus.py | 5 +- eos/effects/informationcommandmindlink.py | 17 +- .../informationcommandstrengthbonus.py | 14 +- .../invulnerabilitycoredurationbonus.py | 8 +- eos/effects/miningdronespecbonus.py | 8 +- eos/effects/miningforemanburstbonusics2.py | 17 +- .../miningforemanburstbonusorecapital2.py | 17 +- eos/effects/miningforemandurationbonus.py | 5 +- eos/effects/miningforemanmindlink.py | 17 +- eos/effects/miningforemanstrengthbonus.py | 14 +- eos/effects/mininginfomultiplier.py | 5 +- eos/effects/modedamptdresistspostdiv.py | 1 + eos/effects/modulebonusafterburner.py | 2 +- ...modulebonusancillaryremotearmorrepairer.py | 3 +- ...modulebonusancillaryremoteshieldbooster.py | 3 +- eos/effects/modulebonusmicrowarpdrive.py | 3 +- eos/effects/modulebonuswarfarelinkarmor.py | 1 + eos/effects/modulebonuswarfarelinkinfo.py | 1 + eos/effects/modulebonuswarfarelinkmining.py | 1 + eos/effects/modulebonuswarfarelinkshield.py | 1 + eos/effects/modulebonuswarfarelinkskirmish.py | 1 + eos/effects/moduletitaneffectgenerator.py | 12 +- eos/effects/noscpuneedbonuseffect.py | 5 +- eos/effects/powerbooster.py | 3 +- eos/effects/remotehullrepairentity.py | 3 +- eos/effects/remotehullrepairfalloff.py | 3 +- eos/effects/remotewebifierentity.py | 3 +- eos/effects/remotewebifierfalloff.py | 3 +- ...olebonusremotearmorrepairoptimalfalloff.py | 9 +- eos/effects/shieldcommandburstbonusics3.py | 17 +- .../shieldcommandburstbonusorecapital3.py | 17 +- eos/effects/shieldcommanddurationbonus.py | 5 +- eos/effects/shieldcommandmindlink.py | 17 +- eos/effects/shieldcommandstrengthbonus.py | 14 +- .../shipbonuscarriera4warfarelinksbonus.py | 22 +- .../shipbonuscarrierc4warfarelinksbonus.py | 22 +- .../shipbonuscarrierg4warfarelinksbonus.py | 22 +- .../shipbonuscarrierm4warfarelinksbonus.py | 22 +- eos/effects/shipbonuscloakcpumc2.py | 5 +- eos/effects/shipbonuscloakcpumf1.py | 5 +- .../shipbonusdronehpdamageminingics4.py | 1 - ...shipbonusdronehpdamageminingorecapital4.py | 1 - .../shipbonusdroneiceharvestingrole.py | 5 +- eos/effects/shipbonusdronerepairmc1.py | 11 +- ...pbonusforceauxiliarya4warfarelinksbonus.py | 22 +- ...pbonusforceauxiliaryc4warfarelinksbonus.py | 22 +- ...pbonusforceauxiliaryg4warfarelinksbonus.py | 22 +- ...pbonusforceauxiliarym4warfarelinksbonus.py | 22 +- ...shipbonusminingdroneamountpercentrookie.py | 1 - eos/effects/shipbonusnosoptimalfalloffac2.py | 8 +- eos/effects/shipbonussetfalloffaf2.py | 5 +- ...hipbonussupercarriera5warfarelinksbonus.py | 22 +- ...hipbonussupercarrierc5warfarelinksbonus.py | 22 +- ...hipbonussupercarrierg5warfarelinksbonus.py | 22 +- ...hipbonussupercarrierm5warfarelinksbonus.py | 22 +- .../shipmodesmallmissiledamagepostdiv.py | 6 +- eos/effects/skirmishcommanddurationbonus.py | 5 +- eos/effects/skirmishcommandmindlink.py | 17 +- eos/effects/skirmishcommandstrengthbonus.py | 14 +- ...spatialphenomenagenerationdurationbonus.py | 5 +- ...samarrdefensive2remotearmorrepairamount.py | 2 + ...systembonusamarrdefensivearmoredwarfare.py | 22 +- ...embonusamarrdefensiveinformationwarfare.py | 22 +- ...ystembonusamarrdefensiveskirmishwarfare.py | 22 +- ...defensive2remoteshieldtransporteramount.py | 2 + ...bonuscaldaridefensiveinformationwarfare.py | 22 +- ...systembonuscaldaridefensivesiegewarfare.py | 22 +- ...tembonuscaldaridefensiveskirmishwarfare.py | 22 +- ...llentedefensive2remotearmorrepairamount.py | 2 + ...tembonusgallentedefensivearmoredwarfare.py | 22 +- ...onusgallentedefensiveinformationwarfare.py | 22 +- ...embonusgallentedefensiveskirmishwarfare.py | 22 +- ...defensive2remoteshieldtransporteramount.py | 2 + ...tembonusminmatardefensivearmoredwarfare.py | 22 +- ...ystembonusminmatardefensivesiegewarfare.py | 22 +- ...embonusminmatardefensiveskirmishwarfare.py | 22 +- eos/effects/systemarmorrepairamount.py | 4 +- .../systemshieldrepairamountshieldskills.py | 4 +- eos/effects/zcolinorcasurveyscannerbonus.py | 5 +- eos/effects/zcolinorcatractorrangebonus.py | 5 +- eos/effects/zcolinorcatractorvelocitybonus.py | 3 +- eos/graph/fitDps.py | 365 +++-- eos/modifiedAttributeDict.py | 4 +- eos/saveddata/booster.py | 3 +- eos/saveddata/fighter.py | 7 +- eos/saveddata/fighterAbility.py | 8 +- eos/saveddata/fit.py | 6 +- eos/saveddata/module.py | 1450 ++++++++--------- eos/saveddata/targetResists.py | 7 +- gui/builtinContextMenus/__init__.py | 2 +- gui/builtinContextMenus/amount.py | 11 +- gui/builtinContextMenus/cargo.py | 3 +- .../changeAffectingSkills.py | 3 +- gui/builtinContextMenus/damagePattern.py | 7 +- gui/builtinContextMenus/droneRemoveStack.py | 2 + gui/builtinContextMenus/droneSplit.py | 2 - gui/builtinContextMenus/factorReload.py | 71 +- gui/builtinContextMenus/fighterAbilities.py | 2 + gui/builtinContextMenus/implantSets.py | 6 +- gui/builtinContextMenus/itemRemove.py | 8 +- gui/builtinContextMenus/marketJump.py | 2 +- gui/builtinContextMenus/metaSwap.py | 3 +- gui/builtinContextMenus/moduleAmmoPicker.py | 6 +- .../moduleGlobalAmmoPicker.py | 5 +- gui/builtinContextMenus/openFit.py | 1 + gui/builtinContextMenus/priceClear.py | 1 + gui/builtinContextMenus/shipJump.py | 3 +- gui/builtinContextMenus/tacticalMode.py | 1 + gui/builtinContextMenus/targetResists.py | 11 +- gui/builtinContextMenus/whProjector.py | 3 +- gui/builtinPreferenceViews/__init__.py | 3 +- gui/builtinPreferenceViews/dummyView.py | 189 ++- .../pyfaCrestPreferences.py | 19 +- .../pyfaGaugePreferences.py | 37 +- .../pyfaGeneralPreferences.py | 30 +- .../pyfaHTMLExportPreferences.py | 14 +- .../pyfaNetworkPreferences.py | 12 +- .../pyfaUpdatePreferences.py | 9 +- gui/builtinStatsViews/firepowerViewFull.py | 17 +- gui/builtinStatsViews/miningyieldViewFull.py | 18 +- gui/builtinStatsViews/rechargeViewFull.py | 3 +- gui/builtinStatsViews/resistancesViewFull.py | 90 +- gui/builtinStatsViews/resourcesViewFull.py | 15 +- .../targetingMiscViewFull.py | 18 +- gui/builtinViewColumns/baseIcon.py | 91 +- gui/builtinViewColumns/baseName.py | 3 +- gui/builtinViewColumns/misc.py | 1102 +++++++------ gui/builtinViewColumns/propertyDisplay.py | 2 - gui/builtinViewColumns/state.py | 6 +- gui/builtinViews/entityEditor.py | 14 +- gui/builtinViews/fittingView.py | 27 +- gui/builtinViews/fleetView.py | 6 +- gui/builtinViews/implantEditor.py | 11 +- gui/characterEditor.py | 4 +- gui/commandView.py | 1 - gui/fighterView.py | 4 +- gui/itemStats.py | 18 +- gui/mainFrame.py | 6 +- gui/projectedView.py | 4 +- gui/shipBrowser.py | 4 +- gui/updateDialog.py | 2 - gui/utils/animEffects.py | 1 - gui/utils/compat.py | 3 +- gui/utils/drawUtils.py | 1 - gui/utils/exportHtml.py | 17 +- gui/utils/numberFormatter.py | 10 +- pyfa.py | 2 +- service/fit.py | 8 +- service/market.py | 21 +- service/port.py | 6 +- service/prefetch.py | 6 +- service/pycrest/weak_ciphers.py | 3 +- tox.ini | 2 +- 206 files changed, 3128 insertions(+), 2391 deletions(-) diff --git a/eos/db/gamedata/item.py b/eos/db/gamedata/item.py index a23c3a786..f0100e761 100644 --- a/eos/db/gamedata/item.py +++ b/eos/db/gamedata/item.py @@ -39,8 +39,8 @@ items_table = Table("invtypes", gamedata_meta, Column("iconID", Integer, ForeignKey("icons.iconID")), Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True)) -from .metaGroup import metatypes_table -from .traits import traits_table +from .metaGroup import metatypes_table # noqa +from .traits import traits_table # noqa mapper(Item, items_table, properties={"group": relation(Group, backref="items"), diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py index 66c5ac9b4..3a036488d 100644 --- a/eos/db/gamedata/queries.py +++ b/eos/db/gamedata/queries.py @@ -21,7 +21,9 @@ from sqlalchemy.orm import join, exc from sqlalchemy.sql import and_, or_, select import eos.config -from eos.gamedata import Item, Attribute +# TODO: Unsure which item the code below needs :( +# from eos.gamedata import Item +from eos.gamedata import Attribute from eos.db import gamedata_session from eos.db.gamedata.metaGroup import metatypes_table, items_table from eos.db.util import processEager, processWhere @@ -282,8 +284,8 @@ def directAttributeRequest(itemIDs, attrIDs): raise TypeError("All itemIDs must be integer") q = select((Item.typeID, Attribute.attributeID, Attribute.value), - and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)), - from_obj=[join(Attribute, Item)]) + and_(Attribute.attributeID.in_(attrIDs), Item.typeID.in_(itemIDs)), + from_obj=[join(Attribute, Item)]) result = gamedata_session.execute(q).fetchall() return result diff --git a/eos/db/migrations/upgrade1.py b/eos/db/migrations/upgrade1.py index fc2148734..5eb3478c6 100644 --- a/eos/db/migrations/upgrade1.py +++ b/eos/db/migrations/upgrade1.py @@ -45,7 +45,7 @@ CONVERSIONS = { 8746, # Quantum Co-Processor 8745, # Photonic CPU Enhancer 15425, # Naiyon's Modified Co-Processor (never existed but convert - # anyway as some fits may include it) + # anyway as some fits may include it) ], 8748: [ # Upgraded Co-Processor 8747, # Nanomechanical CPU Enhancer I @@ -70,7 +70,7 @@ CONVERSIONS = { 16543, # Micro 'Vigor' Core Augmentation ], 8089: [ # Compact Light Missile Launcher - 8093, # Prototype 'Arbalest' Light Missile Launcher + 8093, # Prototype 'Arbalest' Light Missile Launcher ], 8091: [ # Ample Light Missile Launcher 7993, # Experimental TE-2100 Light Missile Launcher @@ -82,6 +82,7 @@ CONVERSIONS = { ] } + def upgrade(saveddata_engine): # Update fits schema to include target resists attribute try: @@ -92,6 +93,7 @@ def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade10.py b/eos/db/migrations/upgrade10.py index 0bfb0f0ee..1ceeb59e8 100644 --- a/eos/db/migrations/upgrade10.py +++ b/eos/db/migrations/upgrade10.py @@ -6,6 +6,7 @@ Migration 10 import sqlalchemy + def upgrade(saveddata_engine): # Update projectedFits schema to include active attribute try: diff --git a/eos/db/migrations/upgrade11.py b/eos/db/migrations/upgrade11.py index 7265e064a..475537b01 100644 --- a/eos/db/migrations/upgrade11.py +++ b/eos/db/migrations/upgrade11.py @@ -7,7 +7,6 @@ Migration 11 modules with their new replacements """ - CONVERSIONS = { 16467: ( # Medium Gremlin Compact Energy Neutralizer 16471, # Medium Unstable Power Fluctuator I @@ -106,11 +105,12 @@ CONVERSIONS = { ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade12.py b/eos/db/migrations/upgrade12.py index dc0eee511..6e3a1d73b 100644 --- a/eos/db/migrations/upgrade12.py +++ b/eos/db/migrations/upgrade12.py @@ -7,7 +7,6 @@ Migration 12 modules with their new replacements """ - CONVERSIONS = { 16457: ( # Crosslink Compact Ballistic Control System 16459, # Muon Coil Bolt Array I @@ -330,11 +329,12 @@ CONVERSIONS = { ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade13.py b/eos/db/migrations/upgrade13.py index 047dc7129..b8af898e8 100644 --- a/eos/db/migrations/upgrade13.py +++ b/eos/db/migrations/upgrade13.py @@ -6,10 +6,11 @@ Migration 13 import sqlalchemy + def upgrade(saveddata_engine): # Update fits schema to include implant location attribute try: saveddata_engine.execute("SELECT implantLocation FROM fits LIMIT 1") except sqlalchemy.exc.DatabaseError: saveddata_engine.execute("ALTER TABLE fits ADD COLUMN implantLocation INTEGER;") - saveddata_engine.execute("UPDATE fits SET implantLocation = 0") \ No newline at end of file + saveddata_engine.execute("UPDATE fits SET implantLocation = 0") diff --git a/eos/db/migrations/upgrade14.py b/eos/db/migrations/upgrade14.py index 8b39947b2..c62afa6d1 100644 --- a/eos/db/migrations/upgrade14.py +++ b/eos/db/migrations/upgrade14.py @@ -6,8 +6,10 @@ Migration 14 import sqlalchemy + def upgrade(saveddata_engine): - if saveddata_engine.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='fighters'").scalar() == 'fighters': + if saveddata_engine.execute( + "SELECT name FROM sqlite_master WHERE type='table' AND name='fighters'").scalar() == 'fighters': # Fighters table exists try: saveddata_engine.execute("SELECT active FROM fighters LIMIT 1") @@ -16,4 +18,4 @@ def upgrade(saveddata_engine): # (they will be recreated) saveddata_engine.execute("DROP TABLE fighters") - saveddata_engine.execute("DROP TABLE fightersAbilities") \ No newline at end of file + saveddata_engine.execute("DROP TABLE fightersAbilities") diff --git a/eos/db/migrations/upgrade15.py b/eos/db/migrations/upgrade15.py index d3c57a957..13852b035 100644 --- a/eos/db/migrations/upgrade15.py +++ b/eos/db/migrations/upgrade15.py @@ -6,8 +6,8 @@ Migration 15 import sqlalchemy -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): sql = """ DELETE FROM modules WHERE ID IN ( diff --git a/eos/db/migrations/upgrade16.py b/eos/db/migrations/upgrade16.py index 7dfaac97c..15f9b8786 100644 --- a/eos/db/migrations/upgrade16.py +++ b/eos/db/migrations/upgrade16.py @@ -6,6 +6,7 @@ Migration 16 import sqlalchemy + def upgrade(saveddata_engine): # Update fits schema to include notes attribute try: diff --git a/eos/db/migrations/upgrade17.py b/eos/db/migrations/upgrade17.py index c3d996899..1d6a4cff8 100644 --- a/eos/db/migrations/upgrade17.py +++ b/eos/db/migrations/upgrade17.py @@ -33,7 +33,8 @@ def upgrade(saveddata_engine): inserts.append({"boosterID": value, "boostedID": boosted, "active": 1}) try: - saveddata_session.execute(commandFits_table.insert(), {"boosterID": value, "boostedID": boosted, "active": 1}) - except Exception, e: + saveddata_session.execute(commandFits_table.insert(), + {"boosterID": value, "boostedID": boosted, "active": 1}) + except Exception: pass saveddata_session.commit() diff --git a/eos/db/migrations/upgrade18.py b/eos/db/migrations/upgrade18.py index fadc65134..4a13b7d57 100644 --- a/eos/db/migrations/upgrade18.py +++ b/eos/db/migrations/upgrade18.py @@ -4,27 +4,26 @@ Migration 8 - Converts modules from old Warfare Links to Command Modules """ - CONVERSIONS = { 42526: ( # Armor Command Burst I - 20069, # Armored Warfare Link - Damage Control I - 20409, # Armored Warfare Link - Passive Defense I - 22227, # Armored Warfare Link - Rapid Repair I + 20069, # Armored Warfare Link - Damage Control I + 20409, # Armored Warfare Link - Passive Defense I + 22227, # Armored Warfare Link - Rapid Repair I ), 43552: ( # Armor Command Burst II - 4264, # Armored Warfare Link - Damage Control II - 4266, # Armored Warfare Link - Passive Defense II - 4266, # Armored Warfare Link - Rapid Repair II + 4264, # Armored Warfare Link - Damage Control II + 4266, # Armored Warfare Link - Passive Defense II + 4266, # Armored Warfare Link - Rapid Repair II ), 42527: ( # Information Command Burst I - 11052, # Information Warfare Link - Sensor Integrity I - 20405, # Information Warfare Link - Recon Operation I - 20406, # Information Warfare Link - Electronic Superiority I + 11052, # Information Warfare Link - Sensor Integrity I + 20405, # Information Warfare Link - Recon Operation I + 20406, # Information Warfare Link - Electronic Superiority I ), 43554: ( # Information Command Burst II - 4268, # Information Warfare Link - Electronic Superiority II - 4270, # Information Warfare Link - Recon Operation II - 4272, # Information Warfare Link - Sensor Integrity II + 4268, # Information Warfare Link - Electronic Superiority II + 4270, # Information Warfare Link - Recon Operation II + 4272, # Information Warfare Link - Sensor Integrity II ), 42529: ( # Shield Command Burst I 20124, # Siege Warfare Link - Active Shielding I @@ -34,17 +33,17 @@ CONVERSIONS = { 43555: ( # Shield Command Burst II 4280, # Siege Warfare Link - Active Shielding II 4282, # Siege Warfare Link - Shield Efficiency II - 4284 # Siege Warfare Link - Shield Harmonizing II + 4284 # Siege Warfare Link - Shield Harmonizing II ), 42530: ( # Skirmish Command Burst I - 11017, # Skirmish Warfare Link - Interdiction Maneuvers I - 20070, # Skirmish Warfare Link - Evasive Maneuvers I - 20408, # Skirmish Warfare Link - Rapid Deployment I + 11017, # Skirmish Warfare Link - Interdiction Maneuvers I + 20070, # Skirmish Warfare Link - Evasive Maneuvers I + 20408, # Skirmish Warfare Link - Rapid Deployment I ), 43556: ( # Skirmish Command Burst II 4286, # Skirmish Warfare Link - Evasive Maneuvers II 4288, # Skirmish Warfare Link - Interdiction Maneuvers II - 4290 # Skirmish Warfare Link - Rapid Deployment II + 4290 # Skirmish Warfare Link - Rapid Deployment II ), 42528: ( # Mining Foreman Burst I 22553, # Mining Foreman Link - Harvester Capacitor Efficiency I @@ -54,15 +53,16 @@ CONVERSIONS = { 43551: ( # Mining Foreman Burst II 4274, # Mining Foreman Link - Harvester Capacitor Efficiency II 4276, # Mining Foreman Link - Laser Optimization II - 4278 # Mining Foreman Link - Mining Laser Field Enhancement II + 4278 # Mining Foreman Link - Mining Laser Field Enhancement II ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade19.py b/eos/db/migrations/upgrade19.py index 4d93edc7c..090f07dda 100644 --- a/eos/db/migrations/upgrade19.py +++ b/eos/db/migrations/upgrade19.py @@ -11,10 +11,10 @@ def upgrade(saveddata_engine): from eos.db import saveddata_session sql = """ - DELETE FROM commandFits - WHERE boosterID NOT IN (select ID from fits) - OR boostedID NOT IN (select ID from fits) - """ + DELETE FROM commandFits + WHERE boosterID NOT IN (select ID from fits) + OR boostedID NOT IN (select ID from fits) + """ saveddata_session.execute(sql) saveddata_session.commit() diff --git a/eos/db/migrations/upgrade2.py b/eos/db/migrations/upgrade2.py index 917a5218e..526177896 100644 --- a/eos/db/migrations/upgrade2.py +++ b/eos/db/migrations/upgrade2.py @@ -6,6 +6,7 @@ Migration 2 import sqlalchemy + def upgrade(saveddata_engine): # Update characters schema to include default chars try: diff --git a/eos/db/migrations/upgrade3.py b/eos/db/migrations/upgrade3.py index 0350ded72..ccc481047 100644 --- a/eos/db/migrations/upgrade3.py +++ b/eos/db/migrations/upgrade3.py @@ -6,6 +6,7 @@ Migration 3 import sqlalchemy + def upgrade(saveddata_engine): try: saveddata_engine.execute("SELECT modeID FROM fits LIMIT 1") diff --git a/eos/db/migrations/upgrade4.py b/eos/db/migrations/upgrade4.py index 87906cffc..f8c670684 100644 --- a/eos/db/migrations/upgrade4.py +++ b/eos/db/migrations/upgrade4.py @@ -10,7 +10,6 @@ Migration 4 and output of itemDiff.py """ - CONVERSIONS = { 506: ( # 'Basic' Capacitor Power Relay 8205, # Alpha Reactor Control: Capacitor Power Relay @@ -131,11 +130,12 @@ CONVERSIONS = { ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade5.py b/eos/db/migrations/upgrade5.py index cf6a3385d..1f5201cf5 100644 --- a/eos/db/migrations/upgrade5.py +++ b/eos/db/migrations/upgrade5.py @@ -4,5 +4,6 @@ Migration 5 Simply deletes damage profiles with a blank name. See GH issue #256 """ + def upgrade(saveddata_engine): saveddata_engine.execute('DELETE FROM damagePatterns WHERE name LIKE ?', ("",)) diff --git a/eos/db/migrations/upgrade6.py b/eos/db/migrations/upgrade6.py index ee8a091e6..724a94008 100644 --- a/eos/db/migrations/upgrade6.py +++ b/eos/db/migrations/upgrade6.py @@ -4,6 +4,8 @@ Migration 6 Overwrites damage profile 0 to reset bad uniform values (bad values set with bug) """ + def upgrade(saveddata_engine): saveddata_engine.execute('DELETE FROM damagePatterns WHERE name LIKE ? OR ID LIKE ?', ("Uniform", "1")) - saveddata_engine.execute('INSERT INTO damagePatterns VALUES (?, ?, ?, ?, ?, ?, ?)', (1, "Uniform", 25, 25, 25, 25, None)) + saveddata_engine.execute('INSERT INTO damagePatterns VALUES (?, ?, ?, ?, ?, ?, ?)', + (1, "Uniform", 25, 25, 25, 25, None)) diff --git a/eos/db/migrations/upgrade7.py b/eos/db/migrations/upgrade7.py index 226f84b3a..fbb74f910 100644 --- a/eos/db/migrations/upgrade7.py +++ b/eos/db/migrations/upgrade7.py @@ -8,17 +8,16 @@ Migration 7 Pyfa. """ - CONVERSIONS = { 640: ( # Scorpion 4005, # Scorpion Ishukone Watch ) } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert ships for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "fits" SET "shipID" = ? WHERE "shipID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "fits" SET "shipID" = ? WHERE "shipID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade8.py b/eos/db/migrations/upgrade8.py index 9d2c04321..19185443b 100644 --- a/eos/db/migrations/upgrade8.py +++ b/eos/db/migrations/upgrade8.py @@ -7,7 +7,6 @@ Migration 8 modules with their new replacements """ - CONVERSIONS = { 8529: ( # Large F-S9 Regolith Compact Shield Extender 8409, # Large Subordinate Screen Stabilizer I @@ -71,15 +70,16 @@ CONVERSIONS = { 11321, # 800mm Reinforced Nanofiber Plates I ), 11317: ( # 800mm Rolled Tungsten Compact Plates - 11315, # 800mm Reinforced Titanium Plates I + 11315, # 800mm Reinforced Titanium Plates I ), } -def upgrade(saveddata_engine): +def upgrade(saveddata_engine): # Convert modules for replacement_item, list in CONVERSIONS.iteritems(): for retired_item in list: - saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) - + saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) + saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', + (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade9.py b/eos/db/migrations/upgrade9.py index ae7b66ad5..a2f5b6148 100644 --- a/eos/db/migrations/upgrade9.py +++ b/eos/db/migrations/upgrade9.py @@ -16,8 +16,10 @@ CREATE TABLE boostersTemp ( ) """ + def upgrade(saveddata_engine): saveddata_engine.execute(tmpTable) - saveddata_engine.execute("INSERT INTO boostersTemp (ID, itemID, fitID, active) SELECT ID, itemID, fitID, active FROM boosters") + saveddata_engine.execute( + "INSERT INTO boostersTemp (ID, itemID, fitID, active) SELECT ID, itemID, fitID, active FROM boosters") saveddata_engine.execute("DROP TABLE boosters") saveddata_engine.execute("ALTER TABLE boostersTemp RENAME TO boosters") diff --git a/eos/db/saveddata/fighter.py b/eos/db/saveddata/fighter.py index 7074369da..179edab11 100644 --- a/eos/db/saveddata/fighter.py +++ b/eos/db/saveddata/fighter.py @@ -18,7 +18,7 @@ # =============================================================================== from sqlalchemy import Table, Column, Integer, ForeignKey, Boolean -from sqlalchemy.orm import * +from sqlalchemy.orm import mapper, relation from eos.db import saveddata_meta from eos.types import Fighter, Fit diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py index 4e0afeb28..c698a4ec7 100644 --- a/eos/db/saveddata/fit.py +++ b/eos/db/saveddata/fit.py @@ -17,11 +17,11 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy import * from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.orm import * from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.sql import and_ +from sqlalchemy.orm import relation, reconstructor, mapper, relationship +from sqlalchemy import ForeignKey, Column, Integer, String, Table, Boolean from eos.db import saveddata_meta from eos.db import saveddata_session @@ -30,8 +30,9 @@ from eos.db.saveddata.drone import drones_table from eos.db.saveddata.fighter import fighters_table from eos.db.saveddata.implant import fitImplants_table from eos.db.saveddata.module import modules_table -from eos.effectHandlerHelpers import HandledModuleList, HandledImplantBoosterList, HandledProjectedModList, HandledDroneCargoList, HandledProjectedDroneList -from eos.types import Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, \ +from eos.effectHandlerHelpers import HandledModuleList, HandledImplantBoosterList, HandledProjectedModList, \ + HandledDroneCargoList, HandledProjectedDroneList +from eos.types import Fit as es_Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, \ TargetResists, ImplantLocation fits_table = Table("fits", saveddata_meta, @@ -46,7 +47,7 @@ fits_table = Table("fits", saveddata_meta, Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True), Column("modeID", Integer, nullable=True), Column("implantLocation", Integer, nullable=False, default=ImplantLocation.FIT), - Column("notes", String, nullable = True), + Column("notes", String, nullable=True), ) projectedFits_table = Table("projectedFits", saveddata_meta, @@ -62,6 +63,7 @@ commandFits_table = Table("commandFits", saveddata_meta, Column("active", Boolean, nullable=False, default=1) ) + class ProjectedFit(object): def __init__(self, sourceID, source_fit, amount=1, active=True): self.sourceID = sourceID @@ -92,6 +94,7 @@ class ProjectedFit(object): self.sourceID, self.victimID, self.amount, self.active, hex(id(self)) ) + class CommandFit(object): def __init__(self, boosterID, booster_fit, active=True): self.boosterID = boosterID @@ -111,116 +114,117 @@ class CommandFit(object): self.boosterID, self.boostedID, self.active, hex(id(self)) ) -Fit._Fit__projectedFits = association_proxy( + +es_Fit._Fit__projectedFits = association_proxy( "victimOf", # look at the victimOf association... "source_fit", # .. and return the source fits creator=lambda sourceID, source_fit: ProjectedFit(sourceID, source_fit) ) -Fit._Fit__commandFits = association_proxy( +es_Fit._Fit__commandFits = association_proxy( "boostedOf", # look at the boostedOf association... "booster_fit", # .. and return the booster fit creator=lambda boosterID, booster_fit: CommandFit(boosterID, booster_fit) ) -mapper(Fit, fits_table, - properties={ - "_Fit__modules": relation( - Module, - collection_class=HandledModuleList, - primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False), - order_by=modules_table.c.position, - cascade='all, delete, delete-orphan'), - "_Fit__projectedModules": relation( - Module, - collection_class=HandledProjectedModList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)), - "owner": relation( - User, - backref="fits"), - "itemID": fits_table.c.shipID, - "shipID": fits_table.c.shipID, - "_Fit__boosters": relation( - Booster, - collection_class=HandledImplantBoosterList, - cascade='all, delete, delete-orphan', - single_parent=True), - "_Fit__drones": relation( - Drone, - collection_class=HandledDroneCargoList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), - "_Fit__fighters": relation( - Fighter, - collection_class=HandledDroneCargoList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)), - "_Fit__cargo": relation( - Cargo, - collection_class=HandledDroneCargoList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(cargo_table.c.fitID == fits_table.c.ID)), - "_Fit__projectedDrones": relation( - Drone, - collection_class=HandledProjectedDroneList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), - "_Fit__projectedFighters": relation( - Fighter, - collection_class=HandledProjectedDroneList, - cascade='all, delete, delete-orphan', - single_parent=True, - primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), - "_Fit__implants": relation( - Implant, - collection_class=HandledImplantBoosterList, - cascade='all, delete, delete-orphan', - backref='fit', - single_parent=True, - primaryjoin=fitImplants_table.c.fitID == fits_table.c.ID, - secondaryjoin=fitImplants_table.c.implantID == Implant.ID, - secondary=fitImplants_table), - "_Fit__character": relation( - Character, - backref="fits"), - "_Fit__damagePattern": relation(DamagePattern), - "_Fit__targetResists": relation(TargetResists), - "projectedOnto": relationship( - ProjectedFit, - primaryjoin=projectedFits_table.c.sourceID == fits_table.c.ID, - backref='source_fit', - collection_class=attribute_mapped_collection('victimID'), - cascade='all, delete, delete-orphan'), - "victimOf": relationship( - ProjectedFit, - primaryjoin=fits_table.c.ID == projectedFits_table.c.victimID, - backref='victim_fit', - collection_class=attribute_mapped_collection('sourceID'), - cascade='all, delete, delete-orphan'), - "boostedOnto": relationship( - CommandFit, - primaryjoin=commandFits_table.c.boosterID == fits_table.c.ID, - backref='booster_fit', - collection_class=attribute_mapped_collection('boostedID'), - cascade='all, delete, delete-orphan'), - "boostedOf": relationship( - CommandFit, - primaryjoin=fits_table.c.ID == commandFits_table.c.boostedID, - backref='boosted_fit', - collection_class=attribute_mapped_collection('boosterID'), - cascade='all, delete, delete-orphan'), - } -) +mapper(es_Fit, fits_table, + properties={ + "_Fit__modules": relation( + Module, + collection_class=HandledModuleList, + primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False), # noqa + order_by=modules_table.c.position, + cascade='all, delete, delete-orphan'), + "_Fit__projectedModules": relation( + Module, + collection_class=HandledProjectedModList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)), # noqa + "owner": relation( + User, + backref="fits"), + "itemID": fits_table.c.shipID, + "shipID": fits_table.c.shipID, + "_Fit__boosters": relation( + Booster, + collection_class=HandledImplantBoosterList, + cascade='all, delete, delete-orphan', + single_parent=True), + "_Fit__drones": relation( + Drone, + collection_class=HandledDroneCargoList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), # noqa + "_Fit__fighters": relation( + Fighter, + collection_class=HandledDroneCargoList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)), # noqa + "_Fit__cargo": relation( + Cargo, + collection_class=HandledDroneCargoList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(cargo_table.c.fitID == fits_table.c.ID)), + "_Fit__projectedDrones": relation( + Drone, + collection_class=HandledProjectedDroneList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), # noqa + "_Fit__projectedFighters": relation( + Fighter, + collection_class=HandledProjectedDroneList, + cascade='all, delete, delete-orphan', + single_parent=True, + primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), # noqa + "_Fit__implants": relation( + Implant, + collection_class=HandledImplantBoosterList, + cascade='all, delete, delete-orphan', + backref='fit', + single_parent=True, + primaryjoin=fitImplants_table.c.fitID == fits_table.c.ID, + secondaryjoin=fitImplants_table.c.implantID == Implant.ID, + secondary=fitImplants_table), + "_Fit__character": relation( + Character, + backref="fits"), + "_Fit__damagePattern": relation(DamagePattern), + "_Fit__targetResists": relation(TargetResists), + "projectedOnto": relationship( + ProjectedFit, + primaryjoin=projectedFits_table.c.sourceID == fits_table.c.ID, + backref='source_fit', + collection_class=attribute_mapped_collection('victimID'), + cascade='all, delete, delete-orphan'), + "victimOf": relationship( + ProjectedFit, + primaryjoin=fits_table.c.ID == projectedFits_table.c.victimID, + backref='victim_fit', + collection_class=attribute_mapped_collection('sourceID'), + cascade='all, delete, delete-orphan'), + "boostedOnto": relationship( + CommandFit, + primaryjoin=commandFits_table.c.boosterID == fits_table.c.ID, + backref='booster_fit', + collection_class=attribute_mapped_collection('boostedID'), + cascade='all, delete, delete-orphan'), + "boostedOf": relationship( + CommandFit, + primaryjoin=fits_table.c.ID == commandFits_table.c.boostedID, + backref='boosted_fit', + collection_class=attribute_mapped_collection('boosterID'), + cascade='all, delete, delete-orphan'), + } + ) mapper(ProjectedFit, projectedFits_table, - properties={ - "_ProjectedFit__amount": projectedFits_table.c.amount, - } -) + properties={ + "_ProjectedFit__amount": projectedFits_table.c.amount, + } + ) -mapper(CommandFit, commandFits_table) \ No newline at end of file +mapper(CommandFit, commandFits_table) diff --git a/eos/db/saveddata/loadDefaultDatabaseValues.py b/eos/db/saveddata/loadDefaultDatabaseValues.py index 6769ed71c..29534bc5e 100644 --- a/eos/db/saveddata/loadDefaultDatabaseValues.py +++ b/eos/db/saveddata/loadDefaultDatabaseValues.py @@ -21,6 +21,7 @@ import eos.db from eos.saveddata.damagePattern import DamagePattern as es_DamagePattern from eos.saveddata.targetResists import TargetResists as es_TargetResists + class ImportError(Exception): pass diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index bcecb3b69..07060d7c9 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -34,7 +34,6 @@ if configVal is True: itemCache = {} queryCache = {} - def cachedQuery(type, amount, *keywords): itemCache[type] = localItemCache = weakref.WeakValueDictionary() queryCache[type] = typeQueryCache = {} @@ -93,7 +92,6 @@ if configVal is True: return deco - def removeCachedEntry(type, ID): if type not in queryCache: return @@ -123,7 +121,6 @@ else: return deco - def removeCachedEntry(*args, **kwargs): return diff --git a/eos/effects/adaptivearmorhardener.py b/eos/effects/adaptivearmorhardener.py index bf20478c2..33e9f143c 100644 --- a/eos/effects/adaptivearmorhardener.py +++ b/eos/effects/adaptivearmorhardener.py @@ -8,13 +8,15 @@ logger = logging.getLogger(__name__) runTime = "late" type = "active" + + def handler(fit, module, context): damagePattern = fit.damagePattern # Skip if there is no damage pattern. Example: projected ships or fleet boosters if damagePattern: - #logger.debug("Damage Pattern: %f/%f/%f/%f", damagePattern.emAmount, damagePattern.thermalAmount, damagePattern.kineticAmount, damagePattern.explosiveAmount) - #logger.debug("Original Armor Resists: %f/%f/%f/%f", fit.ship.getModifiedItemAttr('armorEmDamageResonance'), fit.ship.getModifiedItemAttr('armorThermalDamageResonance'), fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance')) + # logger.debug("Damage Pattern: %f/%f/%f/%f", damagePattern.emAmount, damagePattern.thermalAmount, damagePattern.kineticAmount, damagePattern.explosiveAmount) + # logger.debug("Original Armor Resists: %f/%f/%f/%f", fit.ship.getModifiedItemAttr('armorEmDamageResonance'), fit.ship.getModifiedItemAttr('armorThermalDamageResonance'), fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance')) # Populate a tuple with the damage profile modified by current armor resists. baseDamageTaken = ( @@ -23,35 +25,36 @@ def handler(fit, module, context): damagePattern.kineticAmount * fit.ship.getModifiedItemAttr('armorKineticDamageResonance'), damagePattern.explosiveAmount * fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'), ) - #logger.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + # logger.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) - resistanceShiftAmount = module.getModifiedItemAttr('resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction + resistanceShiftAmount = module.getModifiedItemAttr( + 'resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction RAHResistance = [ - module.getModifiedItemAttr('armorEmDamageResonance'), - module.getModifiedItemAttr('armorThermalDamageResonance'), - module.getModifiedItemAttr('armorKineticDamageResonance'), + module.getModifiedItemAttr('armorEmDamageResonance'), + module.getModifiedItemAttr('armorThermalDamageResonance'), + module.getModifiedItemAttr('armorKineticDamageResonance'), module.getModifiedItemAttr('armorExplosiveDamageResonance'), ] - + # Simulate RAH cycles until the RAH either stops changing or enters a loop. # The number of iterations is limited to prevent an infinite loop if something goes wrong. cycleList = [] loopStart = -20 for num in range(50): - #logger.debug("Starting cycle %d.", num) + # logger.debug("Starting cycle %d.", num) # The strange order is to emulate the ingame sorting when different types have taken the same amount of damage. # This doesn't take into account stacking penalties. In a few cases fitting a Damage Control causes an inaccurate result. damagePattern_tuples = [ (0, baseDamageTaken[0] * RAHResistance[0], RAHResistance[0]), - (3, baseDamageTaken[3] * RAHResistance[3], RAHResistance[3]), + (3, baseDamageTaken[3] * RAHResistance[3], RAHResistance[3]), (2, baseDamageTaken[2] * RAHResistance[2], RAHResistance[2]), (1, baseDamageTaken[1] * RAHResistance[1], RAHResistance[1]), ] - #logger.debug("Damage taken this cycle: %f/%f/%f/%f", damagePattern_tuples[0][1], damagePattern_tuples[3][1], damagePattern_tuples[2][1], damagePattern_tuples[1][1]) - + # logger.debug("Damage taken this cycle: %f/%f/%f/%f", damagePattern_tuples[0][1], damagePattern_tuples[3][1], damagePattern_tuples[2][1], damagePattern_tuples[1][1]) + # Sort the tuple to drop the highest damage value to the bottom sortedDamagePattern_tuples = sorted(damagePattern_tuples, key=lambda damagePattern: damagePattern[1]) - + if sortedDamagePattern_tuples[2][1] == 0: # One damage type: the top damage type takes from the other three # Since the resistances not taking damage will end up going to the type taking damage we just do the whole thing at once. @@ -72,41 +75,47 @@ def handler(fit, module, context): change1 = min(resistanceShiftAmount, 1 - sortedDamagePattern_tuples[1][2]) change2 = -(change0 + change1) / 2 change3 = -(change0 + change1) / 2 - + RAHResistance[sortedDamagePattern_tuples[0][0]] = sortedDamagePattern_tuples[0][2] + change0 RAHResistance[sortedDamagePattern_tuples[1][0]] = sortedDamagePattern_tuples[1][2] + change1 RAHResistance[sortedDamagePattern_tuples[2][0]] = sortedDamagePattern_tuples[2][2] + change2 RAHResistance[sortedDamagePattern_tuples[3][0]] = sortedDamagePattern_tuples[3][2] + change3 - #logger.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) - + # logger.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3]) + # See if the current RAH profile has been encountered before, indicating a loop. for i, val in enumerate(cycleList): - tolerance = 1e-06 - if abs(RAHResistance[0] - val[0]) <= tolerance and abs(RAHResistance[1] - val[1]) <= tolerance and abs(RAHResistance[2] - val[2]) <= tolerance and abs(RAHResistance[3] - val[3]) <= tolerance: + tolerance = 1e-06 + if abs(RAHResistance[0] - val[0]) <= tolerance and \ + abs(RAHResistance[1] - val[1]) <= tolerance and \ + abs(RAHResistance[2] - val[2]) <= tolerance and \ + abs(RAHResistance[3] - val[3]) <= tolerance: loopStart = i - #logger.debug("Loop found: %d-%d", loopStart, num) + # logger.debug("Loop found: %d-%d", loopStart, num) break - if loopStart >= 0: break - + if loopStart >= 0: + break + cycleList.append(list(RAHResistance)) - + if loopStart < 0: - logger.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) - - # Average the profiles in the RAH loop, or the last 20 if it didn't find a loop. + logger.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: %f/%f/%f/%f", + baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3]) + + # Average the profiles in the RAH loop, or the last 20 if it didn't find a loop. loopCycles = cycleList[loopStart:] numCycles = len(loopCycles) average = [0, 0, 0, 0] for cycle in loopCycles: for i in range(4): average[i] += cycle[i] - + for i in range(4): average[i] = round(average[i] / numCycles, 3) - + # Set the new resistances - #logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) - for i, attr in enumerate(('armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance', 'armorExplosiveDamageResonance')): + # logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3]) + for i, attr in enumerate(( + 'armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance', + 'armorExplosiveDamageResonance')): module.increaseItemAttr(attr, average[i] - module.getModifiedItemAttr(attr)) fit.ship.multiplyItemAttr(attr, average[i], stackingPenalties=True, penaltyGroup="preMul") - \ No newline at end of file diff --git a/eos/effects/angelsetbonus.py b/eos/effects/angelsetbonus.py index 9057b5876..1591e9be1 100644 --- a/eos/effects/angelsetbonus.py +++ b/eos/effects/angelsetbonus.py @@ -8,7 +8,7 @@ type = "passive" def handler(fit, implant, context): fit.appliedImplants.filteredItemMultiply( - lambda - implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and "implantSetAngel" in implant.itemModifiedAttributes, + lambda implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and + "implantSetAngel" in implant.itemModifiedAttributes, "signatureRadiusBonus", implant.getModifiedItemAttr("implantSetAngel")) diff --git a/eos/effects/armoredcommanddurationbonus.py b/eos/effects/armoredcommanddurationbonus.py index 031c6d0ef..171dfbbb2 100644 --- a/eos/effects/armoredcommanddurationbonus.py +++ b/eos/effects/armoredcommanddurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Armored Command type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/armoredcommandmindlink.py b/eos/effects/armoredcommandmindlink.py index 832bc2678..2e36ed6f0 100644 --- a/eos/effects/armoredcommandmindlink.py +++ b/eos/effects/armoredcommandmindlink.py @@ -5,9 +5,16 @@ # Implant: Federation Navy Command Mindlink # Implant: Imperial Navy Command Mindlink type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/armoredcommandstrengthbonus.py b/eos/effects/armoredcommandstrengthbonus.py index 363c4f1a8..6bc5e365e 100644 --- a/eos/effects/armoredcommandstrengthbonus.py +++ b/eos/effects/armoredcommandstrengthbonus.py @@ -3,9 +3,15 @@ # Used by: # Skill: Armored Command Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/caldarishipewstrengthcb.py b/eos/effects/caldarishipewstrengthcb.py index b362382d1..48bdccb60 100644 --- a/eos/effects/caldarishipewstrengthcb.py +++ b/eos/effects/caldarishipewstrengthcb.py @@ -7,5 +7,7 @@ type = "passive" def handler(fit, ship, context): for sensorType in ("Gravimetric", "Ladar", "Magnetometric", "Radar"): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Electronic Warfare"), "scan{0}StrengthBonus".format(sensorType), - ship.getModifiedItemAttr("shipBonusCB"), stackingPenalties=True, skill="Caldari Battleship") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Electronic Warfare"), + "scan{0}StrengthBonus".format(sensorType), + ship.getModifiedItemAttr("shipBonusCB"), stackingPenalties=True, + skill="Caldari Battleship") diff --git a/eos/effects/chargebonuswarfarecharge.py b/eos/effects/chargebonuswarfarecharge.py index 032107bf1..fc4af6c2d 100644 --- a/eos/effects/chargebonuswarfarecharge.py +++ b/eos/effects/chargebonuswarfarecharge.py @@ -14,6 +14,8 @@ which warfareBuffID to run (shouldn't need this right now, but better safe than ''' type = "active", "gang" + + def handler(fit, module, context, **kwargs): print "In chargeBonusWarfareEffect, context: ", context @@ -25,8 +27,12 @@ def handler(fit, module, context, **kwargs): fit.ship.boostItemAttr("shield%sDamageResonance" % damageType, value, stackingPenalties=True) if id == 11: # Shield Burst: Active Shielding: Repair Duration/Capacitor - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill("Shield Emission Systems"), "capacitorNeed", value) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill("Shield Emission Systems"), "duration", value) + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill( + "Shield Emission Systems"), "capacitorNeed", value) + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Operation") or mod.item.requiresSkill( + "Shield Emission Systems"), "duration", value) if id == 12: # Shield Burst: Shield Extension: Shield HP fit.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True) @@ -36,8 +42,12 @@ def handler(fit, module, context, **kwargs): fit.ship.boostItemAttr("armor%sDamageResonance" % damageType, value, stackingPenalties=True) if id == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"), "capacitorNeed", value) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or mod.item.requiresSkill("Repair Systems"), "duration", value) + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or mod.item.requiresSkill( + "Repair Systems"), "capacitorNeed", value) + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems") or mod.item.requiresSkill( + "Repair Systems"), "duration", value) if id == 15: # Armor Burst: Armor Reinforcement: Armor HP fit.ship.boostItemAttr("armorHP", value, stackingPenalties=True) @@ -47,11 +57,14 @@ def handler(fit, module, context, **kwargs): if id == 17: # Information Burst: Electronic Superiority: EWAR Range and Strength groups = ("ECM", "Sensor Dampener", "Weapon Disruptor", "Target Painter") - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True) - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "falloffEffectiveness", value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, + stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "falloffEffectiveness", value, + stackingPenalties=True) for scanType in ("Magnetometric", "Radar", "Ladar", "Gravimetric"): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.nam == "ECM", "scan%sStrengthBonus" % scanType, value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.nam == "ECM", "scan%sStrengthBonus" % scanType, + value, stackingPenalties=True) for attr in ("missileVelocityBonus", "explosionDelayBonus", "aoeVelocityBonus", "falloffBonus", "maxRangeBonus", "aoeCloudSizeBonus", "trackingSpeedBonus"): @@ -60,7 +73,8 @@ def handler(fit, module, context, **kwargs): for attr in ("maxTargetRangeBonus", "scanResolutionBonus"): fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Sensor Dampener", attr, value) - fit.modules.filteredItemBoost(lambda mod: mod.item.gorup.name == "Target Painter", "signatureRadiusBonus", value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.gorup.name == "Target Painter", "signatureRadiusBonus", + value, stackingPenalties=True) if id == 18: # Information Burst: Electronic Hardening: Scan Strength for scanType in ("Gravimetric", "Radar", "Ladar", "Magnetometric"): @@ -78,27 +92,39 @@ def handler(fit, module, context, **kwargs): if id == 21: # Skirmish Burst: Interdiction Maneuvers: Tackle Range groups = ("Stasis Web", "Warp Scrambler") - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, + stackingPenalties=True) if id == 22: # Skirmish Burst: Rapid Deployment: AB/MWD Speed Increase - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Afterburner") or mod.item.requiresSkill("High Speed Maneuvering"), "speedFactor", value, stackingPenalties=True) + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Afterburner") or mod.item.requiresSkill("High Speed Maneuvering"), + "speedFactor", value, stackingPenalties=True) if id == 23: # Mining Burst: Mining Laser Field Enhancement: Mining/Survey Range - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill("Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "maxRange", value, stackingPenalties=True) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("CPU Management"), "surveyScanRange", value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or + mod.item.requiresSkill("Ice Harvesting") or + mod.item.requiresSkill("Gas Cloud Harvesting"), + "maxRange", + value, + stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("CPU Management"), "surveyScanRange", + value, stackingPenalties=True) if id == 24: # Mining Burst: Mining Laser Optimization: Mining Capacitor/Duration - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill("Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "capacitorNeed", value, stackingPenalties=True) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill("Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "duration", value, stackingPenalties = True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill( + "Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "capacitorNeed", value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill( + "Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "duration", value, stackingPenalties=True) if id == 25: # Mining Burst: Mining Equipment Preservation: Crystal Volatility - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"), "crystalVolatilityChance", value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"), "crystalVolatilityChance", + value, stackingPenalties=True) for x in xrange(1, 4): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) - print "Buff ID: ",id," value: ",value + print "Buff ID: ", id, " value: ", value if id: if 'commandRun' not in context: print "Add buffID", id, " to ", fit @@ -106,4 +132,3 @@ def handler(fit, module, context, **kwargs): elif kwargs['warfareBuffID'] is not None and kwargs['warfareBuffID'] == id: print "Running buffID ", kwargs['warfareBuffID'], " on ", fit runEffect(kwargs['warfareBuffID'], value) - diff --git a/eos/effects/commandburstaoebonus.py b/eos/effects/commandburstaoebonus.py index 8b7bbfce3..7965a6877 100644 --- a/eos/effects/commandburstaoebonus.py +++ b/eos/effects/commandburstaoebonus.py @@ -5,6 +5,8 @@ # Skill: Leadership # Skill: Wing Command type = "passive" + + def handler(fit, src, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), "maxRange", diff --git a/eos/effects/commandburstaoerolebonus.py b/eos/effects/commandburstaoerolebonus.py index eddf8713e..63de8ca87 100644 --- a/eos/effects/commandburstaoerolebonus.py +++ b/eos/effects/commandburstaoerolebonus.py @@ -11,5 +11,8 @@ # Ship: Orca # Ship: Rorqual type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), "maxRange", src.getModifiedItemAttr("roleBonusCommandBurstAoERange")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), "maxRange", + src.getModifiedItemAttr("roleBonusCommandBurstAoERange")) diff --git a/eos/effects/commandburstreloadtimebonus.py b/eos/effects/commandburstreloadtimebonus.py index 1af92ee57..c0207bef4 100644 --- a/eos/effects/commandburstreloadtimebonus.py +++ b/eos/effects/commandburstreloadtimebonus.py @@ -3,6 +3,8 @@ # Used by: # Skill: Command Burst Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Leadership"), diff --git a/eos/effects/commandprocessoreffect.py b/eos/effects/commandprocessoreffect.py index 03030ed85..887644ece 100644 --- a/eos/effects/commandprocessoreffect.py +++ b/eos/effects/commandprocessoreffect.py @@ -3,6 +3,10 @@ # Used by: # Modules named like: Command Processor I (4 of 4) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", src.getModifiedItemAttr("maxGangModules")) - fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", src.getModifiedItemAttr("maxGangModules")) + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", + src.getModifiedItemAttr("maxGangModules")) + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", + src.getModifiedItemAttr("maxGangModules")) diff --git a/eos/effects/commandshipmultirelayeffect.py b/eos/effects/commandshipmultirelayeffect.py index de57a5160..9a3b30089 100644 --- a/eos/effects/commandshipmultirelayeffect.py +++ b/eos/effects/commandshipmultirelayeffect.py @@ -6,6 +6,9 @@ # Ship: Rorqual type = "passive" + def handler(fit, src, context): - fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", src.getModifiedItemAttr("maxGangModules")) - fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", src.getModifiedItemAttr("maxGangModules")) \ No newline at end of file + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupActive", + src.getModifiedItemAttr("maxGangModules")) + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Leadership"), "maxGroupOnline", + src.getModifiedItemAttr("maxGangModules")) diff --git a/eos/effects/elitebonuscommanddestroyerarmored1.py b/eos/effects/elitebonuscommanddestroyerarmored1.py index 4b5442b0d..83df03351 100644 --- a/eos/effects/elitebonuscommanddestroyerarmored1.py +++ b/eos/effects/elitebonuscommanddestroyerarmored1.py @@ -4,9 +4,16 @@ # Ship: Magus # Ship: Pontifex type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommanddestroyerinfo1.py b/eos/effects/elitebonuscommanddestroyerinfo1.py index 9198bc1c7..2f17453b2 100644 --- a/eos/effects/elitebonuscommanddestroyerinfo1.py +++ b/eos/effects/elitebonuscommanddestroyerinfo1.py @@ -4,9 +4,16 @@ # Ship: Pontifex # Ship: Stork type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommanddestroyershield1.py b/eos/effects/elitebonuscommanddestroyershield1.py index ef5d75c29..03c2e2b8e 100644 --- a/eos/effects/elitebonuscommanddestroyershield1.py +++ b/eos/effects/elitebonuscommanddestroyershield1.py @@ -4,9 +4,16 @@ # Ship: Bifrost # Ship: Stork type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommanddestroyerskirmish1.py b/eos/effects/elitebonuscommanddestroyerskirmish1.py index aa600f865..b33bdfa02 100644 --- a/eos/effects/elitebonuscommanddestroyerskirmish1.py +++ b/eos/effects/elitebonuscommanddestroyerskirmish1.py @@ -4,9 +4,16 @@ # Ship: Bifrost # Ship: Magus type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommandshiparmoredcs3.py b/eos/effects/elitebonuscommandshiparmoredcs3.py index 4832b2904..d7bc3fc46 100644 --- a/eos/effects/elitebonuscommandshiparmoredcs3.py +++ b/eos/effects/elitebonuscommandshiparmoredcs3.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Command Ship (4 of 8) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscommandshipinformationcs3.py b/eos/effects/elitebonuscommandshipinformationcs3.py index ffa02ba05..283cc76d6 100644 --- a/eos/effects/elitebonuscommandshipinformationcs3.py +++ b/eos/effects/elitebonuscommandshipinformationcs3.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Command Ship (4 of 8) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscommandshipsiegecs3.py b/eos/effects/elitebonuscommandshipsiegecs3.py index 4e7395d21..a17eb44dc 100644 --- a/eos/effects/elitebonuscommandshipsiegecs3.py +++ b/eos/effects/elitebonuscommandshipsiegecs3.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Command Ship (4 of 8) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscommandshipskirmishcs3.py b/eos/effects/elitebonuscommandshipskirmishcs3.py index 2c6d03add..345191164 100644 --- a/eos/effects/elitebonuscommandshipskirmishcs3.py +++ b/eos/effects/elitebonuscommandshipskirmishcs3.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Command Ship (4 of 8) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/elitebonuscoveropsnosneutfalloff1.py b/eos/effects/elitebonuscoveropsnosneutfalloff1.py index df0711cac..aa5b5b558 100644 --- a/eos/effects/elitebonuscoveropsnosneutfalloff1.py +++ b/eos/effects/elitebonuscoveropsnosneutfalloff1.py @@ -3,7 +3,9 @@ # Used by: # Ship: Caedes type = "passive" + + def handler(fit, src, context): fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in ("Energy Nosferatu", "Energy Neutralizer"), - "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusCoverOps1"), stackingPenalties=True, skill="Covert Ops") - + "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusCoverOps1"), + stackingPenalties=True, skill="Covert Ops") diff --git a/eos/effects/elitebonuslogisticremotearmorrepairduration3.py b/eos/effects/elitebonuslogisticremotearmorrepairduration3.py index d26ef3525..5c9b24c1c 100644 --- a/eos/effects/elitebonuslogisticremotearmorrepairduration3.py +++ b/eos/effects/elitebonuslogisticremotearmorrepairduration3.py @@ -3,5 +3,8 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "duration", src.getModifiedItemAttr("eliteBonusLogistics3"), skill="Logistics Cruisers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "duration", + src.getModifiedItemAttr("eliteBonusLogistics3"), skill="Logistics Cruisers") diff --git a/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py b/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py index d9180ebd7..c14c4bbdf 100644 --- a/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py +++ b/eos/effects/elitebonuslogisticremotearmorrepairoptimalfalloff1.py @@ -3,6 +3,12 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusLogistics1"), stackingPenalties=True, skill="Logistics Cruisers") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("eliteBonusLogistics1"), stackingPenalties=True, skill="Logistics Cruisers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "falloffEffectiveness", src.getModifiedItemAttr("eliteBonusLogistics1"), + stackingPenalties=True, skill="Logistics Cruisers") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", + src.getModifiedItemAttr("eliteBonusLogistics1"), stackingPenalties=True, + skill="Logistics Cruisers") diff --git a/eos/effects/energyneutralizerfalloff.py b/eos/effects/energyneutralizerfalloff.py index 212f05ffa..e4c22c3fa 100644 --- a/eos/effects/energyneutralizerfalloff.py +++ b/eos/effects/energyneutralizerfalloff.py @@ -8,8 +8,8 @@ type = "active", "projected" def handler(fit, src, context): - if "projected" in context and ( - (hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): + if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or + hasattr(src, "amountActive")): amount = src.getModifiedItemAttr("energyNeutralizerAmount") time = src.getModifiedItemAttr("duration") diff --git a/eos/effects/entityenergyneutralizerfalloff.py b/eos/effects/entityenergyneutralizerfalloff.py index 0783295cf..a02abca12 100644 --- a/eos/effects/entityenergyneutralizerfalloff.py +++ b/eos/effects/entityenergyneutralizerfalloff.py @@ -8,8 +8,8 @@ type = "active", "projected" def handler(fit, src, context): - if "projected" in context and ( - (hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): + if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or + hasattr(src, "amountActive")): amount = src.getModifiedItemAttr("energyNeutralizerAmount") time = src.getModifiedItemAttr("energyNeutralizerDuration") diff --git a/eos/effects/iceharvestingdroneoperationdurationbonus.py b/eos/effects/iceharvestingdroneoperationdurationbonus.py index d8f61a6c1..18622ccf8 100644 --- a/eos/effects/iceharvestingdroneoperationdurationbonus.py +++ b/eos/effects/iceharvestingdroneoperationdurationbonus.py @@ -4,5 +4,8 @@ # Modules named like: Drone Mining Augmentor (8 of 8) # Skill: Ice Harvesting Drone Operation type = "passive" + + def handler(fit, src, context): - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Operation"), "duration", src.getModifiedItemAttr("rofBonus")) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Operation"), "duration", + src.getModifiedItemAttr("rofBonus")) diff --git a/eos/effects/iceharvestingdronespecbonus.py b/eos/effects/iceharvestingdronespecbonus.py index 6fee3f685..18af9f3b1 100644 --- a/eos/effects/iceharvestingdronespecbonus.py +++ b/eos/effects/iceharvestingdronespecbonus.py @@ -3,7 +3,11 @@ # Used by: # Skill: Ice Harvesting Drone Specialization type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Specialization"), "duration", src.getModifiedItemAttr("rofBonus") * lvl) - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Specialization"), "maxVelocity", src.getModifiedItemAttr("maxVelocityBonus") * lvl) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Specialization"), "duration", + src.getModifiedItemAttr("rofBonus") * lvl) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Specialization"), + "maxVelocity", src.getModifiedItemAttr("maxVelocityBonus") * lvl) diff --git a/eos/effects/industrialbonusdronedamage.py b/eos/effects/industrialbonusdronedamage.py index db3fba01d..f6c5137bd 100644 --- a/eos/effects/industrialbonusdronedamage.py +++ b/eos/effects/industrialbonusdronedamage.py @@ -11,8 +11,8 @@ # Ship: Rorqual type = "passive" + def handler(fit, src, context): fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), "damageMultiplier", src.getModifiedItemAttr("industrialBonusDroneDamage")) - diff --git a/eos/effects/informationcommanddurationbonus.py b/eos/effects/informationcommanddurationbonus.py index 2235cde74..f5a7dc351 100644 --- a/eos/effects/informationcommanddurationbonus.py +++ b/eos/effects/informationcommanddurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Information Command type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/informationcommandmindlink.py b/eos/effects/informationcommandmindlink.py index a4551304d..f545ab629 100644 --- a/eos/effects/informationcommandmindlink.py +++ b/eos/effects/informationcommandmindlink.py @@ -5,9 +5,16 @@ # Implant: Imperial Navy Command Mindlink # Implant: Information Command Mindlink type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/informationcommandstrengthbonus.py b/eos/effects/informationcommandstrengthbonus.py index 04f17d646..e4431bee0 100644 --- a/eos/effects/informationcommandstrengthbonus.py +++ b/eos/effects/informationcommandstrengthbonus.py @@ -3,9 +3,15 @@ # Used by: # Skill: Information Command Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/invulnerabilitycoredurationbonus.py b/eos/effects/invulnerabilitycoredurationbonus.py index 8d4165859..b318f11cb 100644 --- a/eos/effects/invulnerabilitycoredurationbonus.py +++ b/eos/effects/invulnerabilitycoredurationbonus.py @@ -3,7 +3,11 @@ # Used by: # Skill: Invulnerability Core Operation type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Invulnerability Core Operation"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Invulnerability Core Operation"), "duration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Invulnerability Core Operation"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Invulnerability Core Operation"), "duration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/miningdronespecbonus.py b/eos/effects/miningdronespecbonus.py index 17c36723c..fe8cdff5a 100644 --- a/eos/effects/miningdronespecbonus.py +++ b/eos/effects/miningdronespecbonus.py @@ -3,7 +3,11 @@ # Used by: # Skill: Mining Drone Specialization type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Drone Specialization"), "miningAmount", src.getModifiedItemAttr("miningAmountBonus") * lvl) - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Drone Specialization"), "maxVelocity", src.getModifiedItemAttr("maxVelocityBonus") * lvl) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Drone Specialization"), "miningAmount", + src.getModifiedItemAttr("miningAmountBonus") * lvl) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Drone Specialization"), "maxVelocity", + src.getModifiedItemAttr("maxVelocityBonus") * lvl) diff --git a/eos/effects/miningforemanburstbonusics2.py b/eos/effects/miningforemanburstbonusics2.py index a2664b025..485138a26 100644 --- a/eos/effects/miningforemanburstbonusics2.py +++ b/eos/effects/miningforemanburstbonusics2.py @@ -3,9 +3,16 @@ # Used by: # Ships from group: Industrial Command Ship (2 of 2) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("shipBonusICS2"), skill="Industrial Command Ships") diff --git a/eos/effects/miningforemanburstbonusorecapital2.py b/eos/effects/miningforemanburstbonusorecapital2.py index e66243732..b1e7f4b57 100644 --- a/eos/effects/miningforemanburstbonusorecapital2.py +++ b/eos/effects/miningforemanburstbonusorecapital2.py @@ -3,9 +3,16 @@ # Used by: # Ship: Rorqual type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", + src.getModifiedItemAttr("shipBonusORECapital2"), skill="Capital Industrial Ships") diff --git a/eos/effects/miningforemandurationbonus.py b/eos/effects/miningforemandurationbonus.py index 88f33e00a..14a290d2e 100644 --- a/eos/effects/miningforemandurationbonus.py +++ b/eos/effects/miningforemandurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Mining Foreman type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/miningforemanmindlink.py b/eos/effects/miningforemanmindlink.py index e8d89bdd7..ad4339f67 100644 --- a/eos/effects/miningforemanmindlink.py +++ b/eos/effects/miningforemanmindlink.py @@ -4,9 +4,16 @@ # Implant: Mining Foreman Mindlink # Implant: ORE Mining Director Mindlink type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/miningforemanstrengthbonus.py b/eos/effects/miningforemanstrengthbonus.py index 004c3500d..48d14e33b 100644 --- a/eos/effects/miningforemanstrengthbonus.py +++ b/eos/effects/miningforemanstrengthbonus.py @@ -3,9 +3,15 @@ # Used by: # Skill: Mining Director type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) \ No newline at end of file + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Mining Foreman"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/mininginfomultiplier.py b/eos/effects/mininginfomultiplier.py index d0ca12180..6cb722d76 100644 --- a/eos/effects/mininginfomultiplier.py +++ b/eos/effects/mininginfomultiplier.py @@ -7,5 +7,6 @@ type = "passive" def handler(fit, module, context): - module.multiplyItemAttr("specialtyMiningAmount", module.getModifiedChargeAttr("specialisationAsteroidYieldMultiplier")) - #module.multiplyItemAttr("miningAmount", module.getModifiedChargeAttr("specialisationAsteroidYieldMultiplier")) + module.multiplyItemAttr("specialtyMiningAmount", + module.getModifiedChargeAttr("specialisationAsteroidYieldMultiplier")) + # module.multiplyItemAttr("miningAmount", module.getModifiedChargeAttr("specialisationAsteroidYieldMultiplier")) diff --git a/eos/effects/modedamptdresistspostdiv.py b/eos/effects/modedamptdresistspostdiv.py index db8580229..891b84a82 100644 --- a/eos/effects/modedamptdresistspostdiv.py +++ b/eos/effects/modedamptdresistspostdiv.py @@ -4,6 +4,7 @@ # Modules named like: Sharpshooter Mode (4 of 4) type = "passive" + def handler(fit, module, context): fit.ship.multiplyItemAttr("weaponDisruptionResistance", 1 / module.getModifiedItemAttr("modeEwarResistancePostDiv")) fit.ship.multiplyItemAttr("sensorDampenerResistance", 1 / module.getModifiedItemAttr("modeEwarResistancePostDiv")) diff --git a/eos/effects/modulebonusafterburner.py b/eos/effects/modulebonusafterburner.py index e48bdb437..c26412bb0 100644 --- a/eos/effects/modulebonusafterburner.py +++ b/eos/effects/modulebonusafterburner.py @@ -11,4 +11,4 @@ def handler(fit, module, context): speedBoost = module.getModifiedItemAttr("speedFactor") mass = fit.ship.getModifiedItemAttr("mass") thrust = module.getModifiedItemAttr("speedBoostFactor") - fit.ship.boostItemAttr("maxVelocity", speedBoost * thrust / mass) \ No newline at end of file + fit.ship.boostItemAttr("maxVelocity", speedBoost * thrust / mass) diff --git a/eos/effects/modulebonusancillaryremotearmorrepairer.py b/eos/effects/modulebonusancillaryremotearmorrepairer.py index c3c157b81..78a6ef855 100644 --- a/eos/effects/modulebonusancillaryremotearmorrepairer.py +++ b/eos/effects/modulebonusancillaryremotearmorrepairer.py @@ -7,7 +7,8 @@ type = "projected", "active" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return if module.charge and module.charge.name == "Nanite Repair Paste": multiplier = 3 diff --git a/eos/effects/modulebonusancillaryremoteshieldbooster.py b/eos/effects/modulebonusancillaryremoteshieldbooster.py index 74443b269..99b1a444c 100644 --- a/eos/effects/modulebonusancillaryremoteshieldbooster.py +++ b/eos/effects/modulebonusancillaryremoteshieldbooster.py @@ -7,7 +7,8 @@ type = "projected", "active" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return amount = module.getModifiedItemAttr("shieldBonus") speed = module.getModifiedItemAttr("duration") / 1000.0 fit.extraAttributes.increase("shieldRepair", amount / speed) diff --git a/eos/effects/modulebonusmicrowarpdrive.py b/eos/effects/modulebonusmicrowarpdrive.py index 2c4f6cd91..75df915ce 100644 --- a/eos/effects/modulebonusmicrowarpdrive.py +++ b/eos/effects/modulebonusmicrowarpdrive.py @@ -12,4 +12,5 @@ def handler(fit, module, context): mass = fit.ship.getModifiedItemAttr("mass") thrust = module.getModifiedItemAttr("speedBoostFactor") fit.ship.boostItemAttr("maxVelocity", speedBoost * thrust / mass) - fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus"), stackingPenalties = True) + fit.ship.boostItemAttr("signatureRadius", module.getModifiedItemAttr("signatureRadiusBonus"), + stackingPenalties=True) diff --git a/eos/effects/modulebonuswarfarelinkarmor.py b/eos/effects/modulebonuswarfarelinkarmor.py index fb40b6dbc..daa55444e 100644 --- a/eos/effects/modulebonuswarfarelinkarmor.py +++ b/eos/effects/modulebonuswarfarelinkarmor.py @@ -5,6 +5,7 @@ type = "active" runTime = "late" + def handler(fit, module, context): for x in xrange(1, 4): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkinfo.py b/eos/effects/modulebonuswarfarelinkinfo.py index daaa041d9..32b493279 100644 --- a/eos/effects/modulebonuswarfarelinkinfo.py +++ b/eos/effects/modulebonuswarfarelinkinfo.py @@ -5,6 +5,7 @@ type = "active" runTime = "late" + def handler(fit, module, context): for x in xrange(1, 4): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkmining.py b/eos/effects/modulebonuswarfarelinkmining.py index 9b58290d2..973d523c6 100644 --- a/eos/effects/modulebonuswarfarelinkmining.py +++ b/eos/effects/modulebonuswarfarelinkmining.py @@ -5,6 +5,7 @@ type = "active" runTime = "late" + def handler(fit, module, context): for x in xrange(1, 4): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkshield.py b/eos/effects/modulebonuswarfarelinkshield.py index 709202d05..856137e74 100644 --- a/eos/effects/modulebonuswarfarelinkshield.py +++ b/eos/effects/modulebonuswarfarelinkshield.py @@ -5,6 +5,7 @@ type = "active" runTime = "late" + def handler(fit, module, context): for x in xrange(1, 4): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkskirmish.py b/eos/effects/modulebonuswarfarelinkskirmish.py index e9edf182b..c0bedf2b1 100644 --- a/eos/effects/modulebonuswarfarelinkskirmish.py +++ b/eos/effects/modulebonuswarfarelinkskirmish.py @@ -5,6 +5,7 @@ type = "active" runTime = "late" + def handler(fit, module, context): for x in xrange(1, 4): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) diff --git a/eos/effects/moduletitaneffectgenerator.py b/eos/effects/moduletitaneffectgenerator.py index 2a5de4473..df371813f 100644 --- a/eos/effects/moduletitaneffectgenerator.py +++ b/eos/effects/moduletitaneffectgenerator.py @@ -4,6 +4,8 @@ # Modules from group: Titan Phenomena Generator (4 of 4) type = "active", "gang" + + def handler(fit, module, context, **kwargs): def runEffect(id, value): if id == 39: # Avatar Effect Generator : Capacitor Recharge bonus @@ -53,14 +55,17 @@ def handler(fit, module, context, **kwargs): fit.ship.boostItemAttr("maxVelocity", value, stackingPenalties=True) if id == 52: # Erebus Effect Generator : Shield RR penalty - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", + value, stackingPenalties=True) if id == 53: # Leviathan Effect Generator : Armor RR penalty - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "armorDamageAmount", value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "armorDamageAmount", value, stackingPenalties=True) if id == 54: # Ragnarok Effect Generator : Laser and Hybrid Optimal penalty groups = ("Energy Weapon", "Hybrid Weapon") - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, "maxRange", value, + stackingPenalties=True) for x in xrange(1, 4): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): @@ -72,4 +77,3 @@ def handler(fit, module, context, **kwargs): fit.addCommandBonus(id, value, module, kwargs['effect']) elif kwargs['warfareBuffID'] is not None and kwargs['warfareBuffID'] == id: runEffect(kwargs['warfareBuffID'], value) - diff --git a/eos/effects/noscpuneedbonuseffect.py b/eos/effects/noscpuneedbonuseffect.py index 9f92b373c..d54b6e023 100644 --- a/eos/effects/noscpuneedbonuseffect.py +++ b/eos/effects/noscpuneedbonuseffect.py @@ -3,5 +3,8 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "cpu", src.getModifiedItemAttr("nosferatuCpuNeedBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "cpu", + src.getModifiedItemAttr("nosferatuCpuNeedBonus")) diff --git a/eos/effects/powerbooster.py b/eos/effects/powerbooster.py index 6fdafc9a4..42dcaca89 100644 --- a/eos/effects/powerbooster.py +++ b/eos/effects/powerbooster.py @@ -11,6 +11,7 @@ def handler(fit, module, context): # Make so that reloads are always taken into account during clculations module.forceReload = True - if module.charge is None: return + if module.charge is None: + return capAmount = module.getModifiedChargeAttr("capacitorBonus") or 0 module.itemModifiedAttributes["capacitorNeed"] = -capAmount diff --git a/eos/effects/remotehullrepairentity.py b/eos/effects/remotehullrepairentity.py index db8de5fe4..367d08bf1 100644 --- a/eos/effects/remotehullrepairentity.py +++ b/eos/effects/remotehullrepairentity.py @@ -7,7 +7,8 @@ runTime = "late" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return bonus = module.getModifiedItemAttr("structureDamageAmount") duration = module.getModifiedItemAttr("duration") / 1000.0 fit.extraAttributes.increase("hullRepair", bonus / duration) diff --git a/eos/effects/remotehullrepairfalloff.py b/eos/effects/remotehullrepairfalloff.py index d7344b646..eaecf6bf9 100644 --- a/eos/effects/remotehullrepairfalloff.py +++ b/eos/effects/remotehullrepairfalloff.py @@ -7,7 +7,8 @@ runTime = "late" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return bonus = module.getModifiedItemAttr("structureDamageAmount") duration = module.getModifiedItemAttr("duration") / 1000.0 fit.extraAttributes.increase("hullRepair", bonus / duration) diff --git a/eos/effects/remotewebifierentity.py b/eos/effects/remotewebifierentity.py index 38028881a..cf9905405 100644 --- a/eos/effects/remotewebifierentity.py +++ b/eos/effects/remotewebifierentity.py @@ -6,6 +6,7 @@ type = "active", "projected" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"), stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/remotewebifierfalloff.py b/eos/effects/remotewebifierfalloff.py index 67f6d4dc5..1db5eb858 100644 --- a/eos/effects/remotewebifierfalloff.py +++ b/eos/effects/remotewebifierfalloff.py @@ -7,6 +7,7 @@ type = "active", "projected" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"), stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py b/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py index c28744a06..f2c9ae4e9 100644 --- a/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py +++ b/eos/effects/rolebonusremotearmorrepairoptimalfalloff.py @@ -3,6 +3,11 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "falloffEffectiveness", src.getModifiedItemAttr("roleBonusRepairRange"), stackingPenalties=True) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", src.getModifiedItemAttr("roleBonusRepairRange"), stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), + "falloffEffectiveness", src.getModifiedItemAttr("roleBonusRepairRange"), + stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "maxRange", + src.getModifiedItemAttr("roleBonusRepairRange"), stackingPenalties=True) diff --git a/eos/effects/shieldcommandburstbonusics3.py b/eos/effects/shieldcommandburstbonusics3.py index 9c6ee1541..1a61286b7 100644 --- a/eos/effects/shieldcommandburstbonusics3.py +++ b/eos/effects/shieldcommandburstbonusics3.py @@ -3,9 +3,16 @@ # Used by: # Ship: Orca type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("shipBonusICS3"), skill="Industrial Command Ships") diff --git a/eos/effects/shieldcommandburstbonusorecapital3.py b/eos/effects/shieldcommandburstbonusorecapital3.py index 595d7afae..5972f2044 100644 --- a/eos/effects/shieldcommandburstbonusorecapital3.py +++ b/eos/effects/shieldcommandburstbonusorecapital3.py @@ -3,9 +3,16 @@ # Used by: # Ship: Rorqual type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("shipBonusORECapital3"), skill="Capital Industrial Ships") diff --git a/eos/effects/shieldcommanddurationbonus.py b/eos/effects/shieldcommanddurationbonus.py index 8e7c78ea5..a7425a974 100644 --- a/eos/effects/shieldcommanddurationbonus.py +++ b/eos/effects/shieldcommanddurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Shield Command type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/shieldcommandmindlink.py b/eos/effects/shieldcommandmindlink.py index a350e5c29..fac7a7720 100644 --- a/eos/effects/shieldcommandmindlink.py +++ b/eos/effects/shieldcommandmindlink.py @@ -3,9 +3,16 @@ # Used by: # Implants from group: Cyber Leadership (4 of 10) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/shieldcommandstrengthbonus.py b/eos/effects/shieldcommandstrengthbonus.py index 775e54d62..b95fe66df 100644 --- a/eos/effects/shieldcommandstrengthbonus.py +++ b/eos/effects/shieldcommandstrengthbonus.py @@ -3,9 +3,15 @@ # Used by: # Skill: Shield Command Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/shipbonuscarriera4warfarelinksbonus.py b/eos/effects/shipbonuscarriera4warfarelinksbonus.py index 43a053e04..31981bba6 100644 --- a/eos/effects/shipbonuscarriera4warfarelinksbonus.py +++ b/eos/effects/shipbonuscarriera4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Archon type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierA4"), skill="Amarr Carrier") diff --git a/eos/effects/shipbonuscarrierc4warfarelinksbonus.py b/eos/effects/shipbonuscarrierc4warfarelinksbonus.py index ecaf23399..4e19f6916 100644 --- a/eos/effects/shipbonuscarrierc4warfarelinksbonus.py +++ b/eos/effects/shipbonuscarrierc4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Chimera type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierC4"), skill="Caldari Carrier") diff --git a/eos/effects/shipbonuscarrierg4warfarelinksbonus.py b/eos/effects/shipbonuscarrierg4warfarelinksbonus.py index 673807782..05eb85c60 100644 --- a/eos/effects/shipbonuscarrierg4warfarelinksbonus.py +++ b/eos/effects/shipbonuscarrierg4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Thanatos type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierG4"), skill="Gallente Carrier") diff --git a/eos/effects/shipbonuscarrierm4warfarelinksbonus.py b/eos/effects/shipbonuscarrierm4warfarelinksbonus.py index 36fbbfa01..11592c9df 100644 --- a/eos/effects/shipbonuscarrierm4warfarelinksbonus.py +++ b/eos/effects/shipbonuscarrierm4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Nidhoggur type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusCarrierM4"), skill="Minmatar Carrier") diff --git a/eos/effects/shipbonuscloakcpumc2.py b/eos/effects/shipbonuscloakcpumc2.py index 0f8c90e35..ac83921f0 100644 --- a/eos/effects/shipbonuscloakcpumc2.py +++ b/eos/effects/shipbonuscloakcpumc2.py @@ -3,5 +3,8 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Cloaking"), "cpu", src.getModifiedItemAttr("shipBonusMC2"), skill="Minmatar Cruiser") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Cloaking"), "cpu", + src.getModifiedItemAttr("shipBonusMC2"), skill="Minmatar Cruiser") diff --git a/eos/effects/shipbonuscloakcpumf1.py b/eos/effects/shipbonuscloakcpumf1.py index 62a6d0aa7..fe201e1df 100644 --- a/eos/effects/shipbonuscloakcpumf1.py +++ b/eos/effects/shipbonuscloakcpumf1.py @@ -3,5 +3,8 @@ # Used by: # Ship: Caedes type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Cloaking"), "cpu", src.getModifiedItemAttr("shipBonusMF"), skill="Minmatar Frigate") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Cloaking"), "cpu", + src.getModifiedItemAttr("shipBonusMF"), skill="Minmatar Frigate") diff --git a/eos/effects/shipbonusdronehpdamageminingics4.py b/eos/effects/shipbonusdronehpdamageminingics4.py index c8d49571f..8b06c6dec 100644 --- a/eos/effects/shipbonusdronehpdamageminingics4.py +++ b/eos/effects/shipbonusdronehpdamageminingics4.py @@ -35,4 +35,3 @@ def handler(fit, src, context): src.getModifiedItemAttr("shipBonusICS4"), skill="Industrial Command Ships" ) - diff --git a/eos/effects/shipbonusdronehpdamageminingorecapital4.py b/eos/effects/shipbonusdronehpdamageminingorecapital4.py index 1b5ed13f5..8cd53cd81 100644 --- a/eos/effects/shipbonusdronehpdamageminingorecapital4.py +++ b/eos/effects/shipbonusdronehpdamageminingorecapital4.py @@ -35,4 +35,3 @@ def handler(fit, src, context): src.getModifiedItemAttr("shipBonusORECapital4"), skill="Capital Industrial Ships" ) - diff --git a/eos/effects/shipbonusdroneiceharvestingrole.py b/eos/effects/shipbonusdroneiceharvestingrole.py index 010fbea1b..b45241f9f 100644 --- a/eos/effects/shipbonusdroneiceharvestingrole.py +++ b/eos/effects/shipbonusdroneiceharvestingrole.py @@ -3,5 +3,8 @@ # Used by: # Ship: Orca type = "passive" + + def handler(fit, src, context): - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Operation"), "duration", src.getModifiedItemAttr("roleBonusDroneIceHarvestingSpeed")) + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Operation"), "duration", + src.getModifiedItemAttr("roleBonusDroneIceHarvestingSpeed")) diff --git a/eos/effects/shipbonusdronerepairmc1.py b/eos/effects/shipbonusdronerepairmc1.py index baaff71f5..77cc9a69d 100644 --- a/eos/effects/shipbonusdronerepairmc1.py +++ b/eos/effects/shipbonusdronerepairmc1.py @@ -3,7 +3,12 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "shieldBonus", src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "structureDamageAmount", src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "armorDamageAmount", src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "shieldBonus", + src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "structureDamageAmount", + src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Drones"), "armorDamageAmount", + src.getModifiedItemAttr("shipBonusMC"), skill="Minmatar Cruiser") diff --git a/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py b/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py index a6bded847..18ea7291a 100644 --- a/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py +++ b/eos/effects/shipbonusforceauxiliarya4warfarelinksbonus.py @@ -3,10 +3,22 @@ # Used by: # Ship: Apostle type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryA4") * lvl, skill="Amarr Carrier") diff --git a/eos/effects/shipbonusforceauxiliaryc4warfarelinksbonus.py b/eos/effects/shipbonusforceauxiliaryc4warfarelinksbonus.py index e2e6cd483..fda300de2 100644 --- a/eos/effects/shipbonusforceauxiliaryc4warfarelinksbonus.py +++ b/eos/effects/shipbonusforceauxiliaryc4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Minokawa type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryC4"), skill="Caldari Carrier") diff --git a/eos/effects/shipbonusforceauxiliaryg4warfarelinksbonus.py b/eos/effects/shipbonusforceauxiliaryg4warfarelinksbonus.py index 147ae8df0..63efca50e 100644 --- a/eos/effects/shipbonusforceauxiliaryg4warfarelinksbonus.py +++ b/eos/effects/shipbonusforceauxiliaryg4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Ninazu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryG4"), skill="Gallente Carrier") diff --git a/eos/effects/shipbonusforceauxiliarym4warfarelinksbonus.py b/eos/effects/shipbonusforceauxiliarym4warfarelinksbonus.py index 0c476942a..df5b5181b 100644 --- a/eos/effects/shipbonusforceauxiliarym4warfarelinksbonus.py +++ b/eos/effects/shipbonusforceauxiliarym4warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Lif type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusForceAuxiliaryM4"), skill="Minmatar Carrier") diff --git a/eos/effects/shipbonusminingdroneamountpercentrookie.py b/eos/effects/shipbonusminingdroneamountpercentrookie.py index ddffa24e9..bffbe6b81 100644 --- a/eos/effects/shipbonusminingdroneamountpercentrookie.py +++ b/eos/effects/shipbonusminingdroneamountpercentrookie.py @@ -8,6 +8,5 @@ type = "passive" def handler(fit, container, context): - level = container.level if "skill" in context else 1 fit.drones.filteredItemBoost(lambda drone: drone.item.group.name == "Mining Drone", "miningAmount", container.getModifiedItemAttr("rookieDroneBonus")) diff --git a/eos/effects/shipbonusnosoptimalfalloffac2.py b/eos/effects/shipbonusnosoptimalfalloffac2.py index f741681b5..355b6e22c 100644 --- a/eos/effects/shipbonusnosoptimalfalloffac2.py +++ b/eos/effects/shipbonusnosoptimalfalloffac2.py @@ -3,6 +3,10 @@ # Used by: # Ship: Rabisu type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "falloffEffectiveness", src.getModifiedItemAttr("shipBonusAC2"), skill="Amarr Cruiser") - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "maxRange", src.getModifiedItemAttr("shipBonusAC2"), skill="Amarr Cruiser") + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "falloffEffectiveness", + src.getModifiedItemAttr("shipBonusAC2"), skill="Amarr Cruiser") + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", "maxRange", + src.getModifiedItemAttr("shipBonusAC2"), skill="Amarr Cruiser") diff --git a/eos/effects/shipbonussetfalloffaf2.py b/eos/effects/shipbonussetfalloffaf2.py index cf22eedbd..9c6f67d13 100644 --- a/eos/effects/shipbonussetfalloffaf2.py +++ b/eos/effects/shipbonussetfalloffaf2.py @@ -3,5 +3,8 @@ # Used by: # Ship: Caedes type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Energy Turret"), "falloff", src.getModifiedItemAttr("shipBonus2AF"), skill="Amarr Frigate") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Small Energy Turret"), "falloff", + src.getModifiedItemAttr("shipBonus2AF"), skill="Amarr Frigate") diff --git a/eos/effects/shipbonussupercarriera5warfarelinksbonus.py b/eos/effects/shipbonussupercarriera5warfarelinksbonus.py index e1d000525..fdc2f24bc 100644 --- a/eos/effects/shipbonussupercarriera5warfarelinksbonus.py +++ b/eos/effects/shipbonussupercarriera5warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Aeon type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Armored Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierA5"), skill="Amarr Carrier") diff --git a/eos/effects/shipbonussupercarrierc5warfarelinksbonus.py b/eos/effects/shipbonussupercarrierc5warfarelinksbonus.py index 768818b2d..5ff00b1ca 100644 --- a/eos/effects/shipbonussupercarrierc5warfarelinksbonus.py +++ b/eos/effects/shipbonussupercarrierc5warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Wyvern type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Shield Command") or mod.item.requiresSkill("Information Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierC5"), skill="Caldari Carrier") diff --git a/eos/effects/shipbonussupercarrierg5warfarelinksbonus.py b/eos/effects/shipbonussupercarrierg5warfarelinksbonus.py index 28dd6ab20..aa6d4d1e2 100644 --- a/eos/effects/shipbonussupercarrierg5warfarelinksbonus.py +++ b/eos/effects/shipbonussupercarrierg5warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Nyx type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Armored Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierG5"), skill="Gallente Carrier") diff --git a/eos/effects/shipbonussupercarrierm5warfarelinksbonus.py b/eos/effects/shipbonussupercarrierm5warfarelinksbonus.py index 53a6fafec..403b6c6a7 100644 --- a/eos/effects/shipbonussupercarrierm5warfarelinksbonus.py +++ b/eos/effects/shipbonussupercarrierm5warfarelinksbonus.py @@ -3,9 +3,21 @@ # Used by: # Ship: Hel type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff4Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff1Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff3Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "buffDuration", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Skirmish Command") or mod.item.requiresSkill("Shield Command"), + "warfareBuff2Value", src.getModifiedItemAttr("shipBonusSupercarrierM5"), skill="Minmatar Carrier") diff --git a/eos/effects/shipmodesmallmissiledamagepostdiv.py b/eos/effects/shipmodesmallmissiledamagepostdiv.py index 116dd3571..7321f4848 100644 --- a/eos/effects/shipmodesmallmissiledamagepostdiv.py +++ b/eos/effects/shipmodesmallmissiledamagepostdiv.py @@ -4,8 +4,10 @@ # Module: Jackdaw Sharpshooter Mode type = "passive" + def handler(fit, module, context): types = ("thermal", "em", "explosive", "kinetic") for type in types: - fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Rockets") or mod.charge.requiresSkill("Light Missiles"), - "{}Damage".format(type), 1 / module.getModifiedItemAttr("modeDamageBonusPostDiv")) + fit.modules.filteredChargeBoost( + lambda mod: mod.charge.requiresSkill("Rockets") or mod.charge.requiresSkill("Light Missiles"), + "{}Damage".format(type), 1 / module.getModifiedItemAttr("modeDamageBonusPostDiv")) diff --git a/eos/effects/skirmishcommanddurationbonus.py b/eos/effects/skirmishcommanddurationbonus.py index 5ab212a25..7b42437e1 100644 --- a/eos/effects/skirmishcommanddurationbonus.py +++ b/eos/effects/skirmishcommanddurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Skirmish Command type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/skirmishcommandmindlink.py b/eos/effects/skirmishcommandmindlink.py index 4b5d09cad..9999d7f56 100644 --- a/eos/effects/skirmishcommandmindlink.py +++ b/eos/effects/skirmishcommandmindlink.py @@ -5,9 +5,16 @@ # Implant: Republic Fleet Command Mindlink # Implant: Skirmish Command Mindlink type = "passive" + + def handler(fit, src, context): - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("mindlinkBonus")) - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("mindlinkBonus")) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/skirmishcommandstrengthbonus.py b/eos/effects/skirmishcommandstrengthbonus.py index f0af86f77..7963755df 100644 --- a/eos/effects/skirmishcommandstrengthbonus.py +++ b/eos/effects/skirmishcommandstrengthbonus.py @@ -3,9 +3,15 @@ # Used by: # Skill: Skirmish Command Specialist type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) - fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Multiplier", src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) + fit.modules.filteredChargeBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Multiplier", + src.getModifiedItemAttr("commandStrengthBonus") * lvl) diff --git a/eos/effects/spatialphenomenagenerationdurationbonus.py b/eos/effects/spatialphenomenagenerationdurationbonus.py index 846fb6040..b127ae30d 100644 --- a/eos/effects/spatialphenomenagenerationdurationbonus.py +++ b/eos/effects/spatialphenomenagenerationdurationbonus.py @@ -3,6 +3,9 @@ # Used by: # Skill: Spatial Phenomena Generation type = "passive" + + def handler(fit, src, context): lvl = src.level - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Spatial Phenomena Generation"), "buffDuration", src.getModifiedItemAttr("durationBonus") * lvl) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Spatial Phenomena Generation"), "buffDuration", + src.getModifiedItemAttr("durationBonus") * lvl) diff --git a/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py b/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py index 9ff4f9d42..5805bbd60 100644 --- a/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py +++ b/eos/effects/subsystembonusamarrdefensive2remotearmorrepairamount.py @@ -4,6 +4,8 @@ # Subsystem: Legion Defensive - Adaptive Augmenter type = "passive" runTime = "early" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusAmarrDefensive2"), diff --git a/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py b/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py index 4a045cd49..75bcf296d 100644 --- a/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py +++ b/eos/effects/subsystembonusamarrdefensivearmoredwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Legion Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py b/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py index 2f3b96474..1d5b476b7 100644 --- a/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py +++ b/eos/effects/subsystembonusamarrdefensiveinformationwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Legion Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py b/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py index c767405fb..141f65cef 100644 --- a/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py +++ b/eos/effects/subsystembonusamarrdefensiveskirmishwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Legion Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusAmarrDefensive"), + skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py b/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py index 1f3b20c03..4478365ef 100644 --- a/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py +++ b/eos/effects/subsystembonuscaldaridefensive2remoteshieldtransporteramount.py @@ -4,6 +4,8 @@ # Subsystem: Tengu Defensive - Adaptive Shielding type = "passive" runTime = "early" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", module.getModifiedItemAttr("subsystemBonusCaldariDefensive2"), diff --git a/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py b/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py index c06646196..aefcd0b2d 100644 --- a/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py +++ b/eos/effects/subsystembonuscaldaridefensiveinformationwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Tengu Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py b/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py index 9a3de25c4..2869a8227 100644 --- a/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py +++ b/eos/effects/subsystembonuscaldaridefensivesiegewarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Tengu Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py b/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py index 21a1a2ad5..54778136c 100644 --- a/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py +++ b/eos/effects/subsystembonuscaldaridefensiveskirmishwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Tengu Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusCaldariDefensive"), + skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py b/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py index f18b60266..f9e7e5039 100644 --- a/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py +++ b/eos/effects/subsystembonusgallentedefensive2remotearmorrepairamount.py @@ -4,6 +4,8 @@ # Subsystem: Proteus Defensive - Adaptive Augmenter type = "passive" runTime = "early" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Remote Armor Repair Systems"), "armorDamageAmount", module.getModifiedItemAttr("subsystemBonusGallenteDefensive2"), diff --git a/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py b/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py index 5d9369857..d66f4fc32 100644 --- a/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py +++ b/eos/effects/subsystembonusgallentedefensivearmoredwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Proteus Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py b/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py index 83a9943ee..90e90eac6 100644 --- a/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py +++ b/eos/effects/subsystembonusgallentedefensiveinformationwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Proteus Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py b/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py index 6aae7922b..b4ed4e9e2 100644 --- a/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py +++ b/eos/effects/subsystembonusgallentedefensiveskirmishwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Proteus Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusGallenteDefensive"), + skill="Gallente Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py b/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py index 784c7e7e8..2586c28ec 100644 --- a/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py +++ b/eos/effects/subsystembonusminmatardefensive2remoteshieldtransporteramount.py @@ -4,6 +4,8 @@ # Subsystem: Loki Defensive - Adaptive Shielding type = "passive" runTime = "early" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Emission Systems"), "shieldBonus", module.getModifiedItemAttr("subsystemBonusMinmatarDefensive2"), diff --git a/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py b/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py index f38d8af9c..7921b5713 100644 --- a/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py +++ b/eos/effects/subsystembonusminmatardefensivearmoredwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Loki Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Armored Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensivesiegewarfare.py b/eos/effects/subsystembonusminmatardefensivesiegewarfare.py index 80547e83e..de019c309 100644 --- a/eos/effects/subsystembonusminmatardefensivesiegewarfare.py +++ b/eos/effects/subsystembonusminmatardefensivesiegewarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Loki Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Shield Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") diff --git a/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py b/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py index 75ecd4da2..645257326 100644 --- a/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py +++ b/eos/effects/subsystembonusminmatardefensiveskirmishwarfare.py @@ -3,9 +3,21 @@ # Used by: # Subsystem: Loki Defensive - Warfare Processor type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") - fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff1Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff3Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff4Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "warfareBuff2Value", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Skirmish Command"), "buffDuration", + src.getModifiedItemAttr("subsystemBonusMinmatarDefensive"), + skill="Minmatar Defensive Systems") diff --git a/eos/effects/systemarmorrepairamount.py b/eos/effects/systemarmorrepairamount.py index 1b28168f1..da585583d 100644 --- a/eos/effects/systemarmorrepairamount.py +++ b/eos/effects/systemarmorrepairamount.py @@ -7,7 +7,7 @@ type = ("projected", "passive") def handler(fit, module, context): - fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Repair Systems") - or mod.item.requiresSkill("Capital Repair Systems"), + fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Repair Systems") or + mod.item.requiresSkill("Capital Repair Systems"), "armorDamageAmount", module.getModifiedItemAttr("armorDamageAmountMultiplier"), stackingPenalties=True, penaltyGroup="postMul") diff --git a/eos/effects/systemshieldrepairamountshieldskills.py b/eos/effects/systemshieldrepairamountshieldskills.py index bde28cd04..707e2dbbb 100644 --- a/eos/effects/systemshieldrepairamountshieldskills.py +++ b/eos/effects/systemshieldrepairamountshieldskills.py @@ -7,7 +7,7 @@ type = ("projected", "passive") def handler(fit, module, context): - fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Shield Operation") - or mod.item.requiresSkill("Capital Shield Operation"), + fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Shield Operation") or + mod.item.requiresSkill("Capital Shield Operation"), "shieldBonus", module.getModifiedItemAttr("shieldBonusMultiplier"), stackingPenalties=True, penaltyGroup="postMul") diff --git a/eos/effects/zcolinorcasurveyscannerbonus.py b/eos/effects/zcolinorcasurveyscannerbonus.py index feed0cc0c..a1eb01fea 100644 --- a/eos/effects/zcolinorcasurveyscannerbonus.py +++ b/eos/effects/zcolinorcasurveyscannerbonus.py @@ -3,5 +3,8 @@ # Used by: # Ships from group: Industrial Command Ship (2 of 2) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Survey Scanner", "surveyScanRange", src.getModifiedItemAttr("roleBonusSurveyScannerRange")) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Survey Scanner", "surveyScanRange", + src.getModifiedItemAttr("roleBonusSurveyScannerRange")) diff --git a/eos/effects/zcolinorcatractorrangebonus.py b/eos/effects/zcolinorcatractorrangebonus.py index b2262a1e9..9c4a20c03 100644 --- a/eos/effects/zcolinorcatractorrangebonus.py +++ b/eos/effects/zcolinorcatractorrangebonus.py @@ -3,5 +3,8 @@ # Used by: # Ships from group: Industrial Command Ship (2 of 2) type = "passive" + + def handler(fit, src, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxRange", src.getModifiedItemAttr("roleBonusTractorBeamRange")) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxRange", + src.getModifiedItemAttr("roleBonusTractorBeamRange")) diff --git a/eos/effects/zcolinorcatractorvelocitybonus.py b/eos/effects/zcolinorcatractorvelocitybonus.py index 95219da80..736643324 100644 --- a/eos/effects/zcolinorcatractorvelocitybonus.py +++ b/eos/effects/zcolinorcatractorvelocitybonus.py @@ -6,4 +6,5 @@ type = "passive" def handler(fit, ship, context): - fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxTractorVelocity", ship.getModifiedItemAttr("roleBonusTractorBeamVelocity")) + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Tractor Beam", "maxTractorVelocity", + ship.getModifiedItemAttr("roleBonusTractorBeamVelocity")) diff --git a/eos/graph/fitDps.py b/eos/graph/fitDps.py index cfc9ec5f4..04ef910a5 100644 --- a/eos/graph/fitDps.py +++ b/eos/graph/fitDps.py @@ -1,183 +1,182 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -from math import log, sin, radians, exp - -from eos.graph import Graph -from eos.types import Hardpoint, State - - -class FitDpsGraph(Graph): - defaults = {"angle": 0, - "distance": 0, - "signatureRadius": None, - "velocity": 0} - - def __init__(self, fit, data=None): - Graph.__init__(self, fit, self.calcDps, data if data is not None else self.defaults) - self.fit = fit - - def calcDps(self, data): - ew = {'signatureRadius': [], 'velocity': []} - fit = self.fit - total = 0 - distance = data["distance"] * 1000 - abssort = lambda val: -abs(val - 1) - - for mod in fit.modules: - if not mod.isEmpty and mod.state >= State.ACTIVE: - if "remoteTargetPaintFalloff" in mod.item.effects: - ew['signatureRadius'].append( - 1 + (mod.getModifiedItemAttr("signatureRadiusBonus") / 100) * self.calculateModuleMultiplier( - mod, data)) - if "remoteWebifierFalloff" in mod.item.effects: - if distance <= mod.getModifiedItemAttr("maxRange"): - ew['velocity'].append(1 + (mod.getModifiedItemAttr("speedFactor") / 100)) - elif mod.getModifiedItemAttr("falloffEffectiveness") > 0: - # I am affected by falloff - ew['velocity'].append( - 1 + (mod.getModifiedItemAttr("speedFactor") / 100) * self.calculateModuleMultiplier(mod, - data)) - - ew['signatureRadius'].sort(key=abssort) - ew['velocity'].sort(key=abssort) - - for attr, values in ew.iteritems(): - val = data[attr] - try: - for i in xrange(len(values)): - bonus = values[i] - val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) - data[attr] = val - except: - pass - - for mod in fit.modules: - dps, _ = mod.damageStats(fit.targetResists) - if mod.hardpoint == Hardpoint.TURRET: - if mod.state >= State.ACTIVE: - total += dps * self.calculateTurretMultiplier(mod, data) - - elif mod.hardpoint == Hardpoint.MISSILE: - if mod.state >= State.ACTIVE and mod.maxRange >= distance: - total += dps * self.calculateMissileMultiplier(mod, data) - - if distance <= fit.extraAttributes["droneControlRange"]: - for drone in fit.drones: - multiplier = 1 if drone.getModifiedItemAttr("maxVelocity") > 1 else self.calculateTurretMultiplier( - drone, data) - dps, _ = drone.damageStats(fit.targetResists) - total += dps * multiplier - - # this is janky as fuck - for fighter in fit.fighters: - for ability in fighter.abilities: - if ability.dealsDamage and ability.active: - multiplier = self.calculateFighterMissileMultiplier(ability, data) - dps, _ = ability.damageStats(fit.targetResists) - total += dps * multiplier - - return total - - def calculateMissileMultiplier(self, mod, data): - targetSigRad = data["signatureRadius"] - targetVelocity = data["velocity"] - explosionRadius = mod.getModifiedChargeAttr("aoeCloudSize") - targetSigRad = explosionRadius if targetSigRad is None else targetSigRad - explosionVelocity = mod.getModifiedChargeAttr("aoeVelocity") - damageReductionFactor = mod.getModifiedChargeAttr("aoeDamageReductionFactor") - - sigRadiusFactor = targetSigRad / explosionRadius - if targetVelocity: - velocityFactor = ( - explosionVelocity / explosionRadius * targetSigRad / targetVelocity) ** damageReductionFactor - else: - velocityFactor = 1 - - return min(sigRadiusFactor, velocityFactor, 1) - - def calculateTurretMultiplier(self, mod, data): - # Source for most of turret calculation info: http://wiki.eveonline.com/en/wiki/Falloff - chanceToHit = self.calculateTurretChanceToHit(mod, data) - if chanceToHit > 0.01: - # AvgDPS = Base Damage * [ ( ChanceToHit^2 + ChanceToHit + 0.0499 ) / 2 ] - multiplier = (chanceToHit ** 2 + chanceToHit + 0.0499) / 2 - else: - # All hits are wreckings - multiplier = chanceToHit * 3 - dmgScaling = mod.getModifiedItemAttr("turretDamageScalingRadius") - if dmgScaling: - targetSigRad = data["signatureRadius"] - multiplier = min(1, (float(targetSigRad) / dmgScaling) ** 2) - return multiplier - - def calculateFighterMissileMultiplier(self, ability, data): - prefix = ability.attrPrefix - - targetSigRad = data["signatureRadius"] - targetVelocity = data["velocity"] - explosionRadius = ability.fighter.getModifiedItemAttr("{}ExplosionRadius".format(prefix)) - explosionVelocity = ability.fighter.getModifiedItemAttr("{}ExplosionVelocity".format(prefix)) - damageReductionFactor = ability.fighter.getModifiedItemAttr("{}ReductionFactor".format(prefix)) - - # the following conditionals are because CCP can't keep a decent naming convention, as if fighter implementation - # wasn't already fucked. - if damageReductionFactor is None: - damageReductionFactor = ability.fighter.getModifiedItemAttr("{}DamageReductionFactor".format(prefix)) - - damageReductionSensitivity = ability.fighter.getModifiedItemAttr("{}ReductionSensitivity".format(prefix)) - if damageReductionSensitivity is None: - damageReductionSensitivity = ability.fighter.getModifiedItemAttr( - "{}DamageReductionSensitivity".format(prefix)) - - targetSigRad = explosionRadius if targetSigRad is None else targetSigRad - sigRadiusFactor = targetSigRad / explosionRadius - - if targetVelocity: - velocityFactor = (explosionVelocity / explosionRadius * targetSigRad / targetVelocity) ** ( - log(damageReductionFactor) / log(damageReductionSensitivity)) - else: - velocityFactor = 1 - - return min(sigRadiusFactor, velocityFactor, 1) - - def calculateTurretChanceToHit(self, mod, data): - distance = data["distance"] * 1000 - tracking = mod.getModifiedItemAttr("trackingSpeed") - turretOptimal = mod.maxRange - turretFalloff = mod.falloff - turretSigRes = mod.getModifiedItemAttr("optimalSigRadius") - targetSigRad = data["signatureRadius"] - targetSigRad = turretSigRes if targetSigRad is None else targetSigRad - transversal = sin(radians(data["angle"])) * data["velocity"] - trackingEq = (((transversal / (distance * tracking)) * - (turretSigRes / targetSigRad)) ** 2) - rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2 - - return 0.5 ** (trackingEq + rangeEq) - - def calculateModuleMultiplier(self, mod, data): - # Simplified formula, we make some assumptions about the module - # This is basically the calculateTurretChanceToHit without tracking values - distance = data["distance"] * 1000 - turretOptimal = mod.maxRange - turretFalloff = mod.falloff - rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2 - - return 0.5 ** (rangeEq) +# =============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +# =============================================================================== + +from math import log, sin, radians, exp + +from eos.graph import Graph +from eos.types import Hardpoint, State + + +class FitDpsGraph(Graph): + defaults = {"angle": 0, + "distance": 0, + "signatureRadius": None, + "velocity": 0} + + def __init__(self, fit, data=None): + Graph.__init__(self, fit, self.calcDps, data if data is not None else self.defaults) + self.fit = fit + + def calcDps(self, data): + ew = {'signatureRadius': [], 'velocity': []} + fit = self.fit + total = 0 + distance = data["distance"] * 1000 + abssort = lambda val: -abs(val - 1) + + for mod in fit.modules: + if not mod.isEmpty and mod.state >= State.ACTIVE: + if "remoteTargetPaintFalloff" in mod.item.effects: + ew['signatureRadius'].append( + 1 + (mod.getModifiedItemAttr("signatureRadiusBonus") / 100) * self.calculateModuleMultiplier( + mod, data)) + if "remoteWebifierFalloff" in mod.item.effects: + if distance <= mod.getModifiedItemAttr("maxRange"): + ew['velocity'].append(1 + (mod.getModifiedItemAttr("speedFactor") / 100)) + elif mod.getModifiedItemAttr("falloffEffectiveness") > 0: + # I am affected by falloff + ew['velocity'].append( + 1 + (mod.getModifiedItemAttr("speedFactor") / 100) * self.calculateModuleMultiplier(mod, + data)) + + ew['signatureRadius'].sort(key=abssort) + ew['velocity'].sort(key=abssort) + + for attr, values in ew.iteritems(): + val = data[attr] + try: + for i in xrange(len(values)): + bonus = values[i] + val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) + data[attr] = val + except: + pass + + for mod in fit.modules: + dps, _ = mod.damageStats(fit.targetResists) + if mod.hardpoint == Hardpoint.TURRET: + if mod.state >= State.ACTIVE: + total += dps * self.calculateTurretMultiplier(mod, data) + + elif mod.hardpoint == Hardpoint.MISSILE: + if mod.state >= State.ACTIVE and mod.maxRange >= distance: + total += dps * self.calculateMissileMultiplier(mod, data) + + if distance <= fit.extraAttributes["droneControlRange"]: + for drone in fit.drones: + multiplier = 1 if drone.getModifiedItemAttr("maxVelocity") > 1 else self.calculateTurretMultiplier( + drone, data) + dps, _ = drone.damageStats(fit.targetResists) + total += dps * multiplier + + # this is janky as fuck + for fighter in fit.fighters: + for ability in fighter.abilities: + if ability.dealsDamage and ability.active: + multiplier = self.calculateFighterMissileMultiplier(ability, data) + dps, _ = ability.damageStats(fit.targetResists) + total += dps * multiplier + + return total + + def calculateMissileMultiplier(self, mod, data): + targetSigRad = data["signatureRadius"] + targetVelocity = data["velocity"] + explosionRadius = mod.getModifiedChargeAttr("aoeCloudSize") + targetSigRad = explosionRadius if targetSigRad is None else targetSigRad + explosionVelocity = mod.getModifiedChargeAttr("aoeVelocity") + damageReductionFactor = mod.getModifiedChargeAttr("aoeDamageReductionFactor") + + sigRadiusFactor = targetSigRad / explosionRadius + if targetVelocity: + velocityFactor = (explosionVelocity / explosionRadius * targetSigRad / targetVelocity) ** damageReductionFactor + else: + velocityFactor = 1 + + return min(sigRadiusFactor, velocityFactor, 1) + + def calculateTurretMultiplier(self, mod, data): + # Source for most of turret calculation info: http://wiki.eveonline.com/en/wiki/Falloff + chanceToHit = self.calculateTurretChanceToHit(mod, data) + if chanceToHit > 0.01: + # AvgDPS = Base Damage * [ ( ChanceToHit^2 + ChanceToHit + 0.0499 ) / 2 ] + multiplier = (chanceToHit ** 2 + chanceToHit + 0.0499) / 2 + else: + # All hits are wreckings + multiplier = chanceToHit * 3 + dmgScaling = mod.getModifiedItemAttr("turretDamageScalingRadius") + if dmgScaling: + targetSigRad = data["signatureRadius"] + multiplier = min(1, (float(targetSigRad) / dmgScaling) ** 2) + return multiplier + + def calculateFighterMissileMultiplier(self, ability, data): + prefix = ability.attrPrefix + + targetSigRad = data["signatureRadius"] + targetVelocity = data["velocity"] + explosionRadius = ability.fighter.getModifiedItemAttr("{}ExplosionRadius".format(prefix)) + explosionVelocity = ability.fighter.getModifiedItemAttr("{}ExplosionVelocity".format(prefix)) + damageReductionFactor = ability.fighter.getModifiedItemAttr("{}ReductionFactor".format(prefix)) + + # the following conditionals are because CCP can't keep a decent naming convention, as if fighter implementation + # wasn't already fucked. + if damageReductionFactor is None: + damageReductionFactor = ability.fighter.getModifiedItemAttr("{}DamageReductionFactor".format(prefix)) + + damageReductionSensitivity = ability.fighter.getModifiedItemAttr("{}ReductionSensitivity".format(prefix)) + if damageReductionSensitivity is None: + damageReductionSensitivity = ability.fighter.getModifiedItemAttr( + "{}DamageReductionSensitivity".format(prefix)) + + targetSigRad = explosionRadius if targetSigRad is None else targetSigRad + sigRadiusFactor = targetSigRad / explosionRadius + + if targetVelocity: + velocityFactor = (explosionVelocity / explosionRadius * targetSigRad / targetVelocity) ** ( + log(damageReductionFactor) / log(damageReductionSensitivity)) + else: + velocityFactor = 1 + + return min(sigRadiusFactor, velocityFactor, 1) + + def calculateTurretChanceToHit(self, mod, data): + distance = data["distance"] * 1000 + tracking = mod.getModifiedItemAttr("trackingSpeed") + turretOptimal = mod.maxRange + turretFalloff = mod.falloff + turretSigRes = mod.getModifiedItemAttr("optimalSigRadius") + targetSigRad = data["signatureRadius"] + targetSigRad = turretSigRes if targetSigRad is None else targetSigRad + transversal = sin(radians(data["angle"])) * data["velocity"] + trackingEq = (((transversal / (distance * tracking)) * + (turretSigRes / targetSigRad)) ** 2) + rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2 + + return 0.5 ** (trackingEq + rangeEq) + + def calculateModuleMultiplier(self, mod, data): + # Simplified formula, we make some assumptions about the module + # This is basically the calculateTurretChanceToHit without tracking values + distance = data["distance"] * 1000 + turretOptimal = mod.maxRange + turretFalloff = mod.falloff + rangeEq = ((max(0, distance - turretOptimal)) / turretFalloff) ** 2 + + return 0.5 ** (rangeEq) diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index c0f778263..d060eb425 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -132,8 +132,8 @@ class ModifiedAttributeDict(collections.MutableMapping): return (key for key in all) def __contains__(self, key): - return ( - self.__original is not None and key in self.__original) or key in self.__modified or key in self.__intermediary + return (self.__original is not None and key in self.__original) or \ + key in self.__modified or key in self.__intermediary def __placehold(self, key): """Create calculation placeholder in item's modified attribute dict""" diff --git a/eos/saveddata/booster.py b/eos/saveddata/booster.py index 3be3845d0..93f0a4bc3 100644 --- a/eos/saveddata/booster.py +++ b/eos/saveddata/booster.py @@ -159,6 +159,7 @@ class Booster(HandledItem, ItemAttrShortcut): return copy + # Legacy booster side effect code, disabling as not currently implemented ''' class SideEffect(object): @@ -194,4 +195,4 @@ class Booster(HandledItem, ItemAttrShortcut): raise TypeError("Need an effect with a handler") self.__effect = effect -''' \ No newline at end of file +''' diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py index 23218ef02..54249a094 100644 --- a/eos/saveddata/fighter.py +++ b/eos/saveddata/fighter.py @@ -124,10 +124,6 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def abilities(self): return self.__abilities or [] - @property - def charge(self): - return self.__charge - @property def itemModifiedAttributes(self): return self.__itemModifiedAttributes @@ -269,8 +265,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): for ability in self.abilities: if ability.active: effect = ability.effect - if effect.runTime == runTime and \ - effect.activeByDefault and \ + if effect.runTime == runTime and effect.activeByDefault and \ ((projected and effect.isType("projected")) or not projected): if ability.grouped: effect.handler(fit, self, context) diff --git a/eos/saveddata/fighterAbility.py b/eos/saveddata/fighterAbility.py index 38a21a27e..cb7c34912 100644 --- a/eos/saveddata/fighterAbility.py +++ b/eos/saveddata/fighterAbility.py @@ -95,14 +95,12 @@ class FighterAbility(object): @property def reloadTime(self): - return self.fighter.getModifiedItemAttr("fighterRefuelingTime") + \ - (self.REARM_TIME_MAPPING[self.fighter.getModifiedItemAttr( - "fighterSquadronRole")] or 0 if self.hasCharges else 0) * self.numShots + rearm_time = (self.REARM_TIME_MAPPING[self.fighter.getModifiedItemAttr("fighterSquadronRole")] or 0 if self.hasCharges else 0) + return self.fighter.getModifiedItemAttr("fighterRefuelingTime") + rearm_time * self.numShots @property def numShots(self): - return self.NUM_SHOTS_MAPPING[ - self.fighter.getModifiedItemAttr("fighterSquadronRole")] or 0 if self.hasCharges else 0 + return self.NUM_SHOTS_MAPPING[self.fighter.getModifiedItemAttr("fighterSquadronRole")] or 0 if self.hasCharges else 0 @property def cycleTime(self): diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 69341c386..8e5435acf 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -28,10 +28,12 @@ from sqlalchemy.orm import validates, reconstructor import eos.db from eos import capSim from eos.effectHandlerHelpers import * +from eos.effectHandlerHelpers import HandledModuleList, HandledDroneCargoList, HandledImplantBoosterList, HandledProjectedDroneList, HandledProjectedModList from eos.enum import Enum from eos.saveddata.module import State, Hardpoint from eos.types import Ship, Character, Slot, Module, Citadel from utils.timer import Timer +import logging logger = logging.getLogger(__name__) @@ -471,7 +473,7 @@ class Fit(object): # todo: ensure that these are run with the module is active only context += ("commandRun",) self.register(thing) - effect.handler(self, thing, context, warfareBuffID = warfareBuffID) + effect.handler(self, thing, context, warfareBuffID=warfareBuffID) # if effect.isType("offline") or (effect.isType("passive") and thing.state >= State.ONLINE) or \ # (effect.isType("active") and thing.state >= State.ACTIVE): @@ -619,7 +621,7 @@ class Fit(object): if not withBoosters and self.commandBonuses: self.__runCommandBoosts(runTime) - timer.checkpoint('Done with runtime: %s'%runTime) + timer.checkpoint('Done with runtime: %s' % runTime) # Mark fit as calculated self.__calculated = True diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 998ac595b..f72431a00 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -1,725 +1,725 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -import logging - -from sqlalchemy.orm import validates, reconstructor - -import eos.db -from eos.effectHandlerHelpers import HandledItem, HandledCharge -from eos.enum import Enum -from eos.mathUtils import floorFloat -from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut -from eos.types import Citadel - -logger = logging.getLogger(__name__) - - -class State(Enum): - OFFLINE = -1 - ONLINE = 0 - ACTIVE = 1 - OVERHEATED = 2 - - -class Slot(Enum): - - # These are self-explanatory - LOW = 1 - MED = 2 - HIGH = 3 - RIG = 4 - SUBSYSTEM = 5 - # not a real slot, need for pyfa display rack separation - MODE = 6 - # system effects. They are projected "modules" and pyfa assumes all modules - # have a slot. In this case, make one up. - SYSTEM = 7 - # used for citadel services - SERVICE = 8 - # fighter 'slots'. Just easier to put them here... - F_LIGHT = 10 - F_SUPPORT = 11 - F_HEAVY = 12 - - -class Hardpoint(Enum): - - NONE = 0 - MISSILE = 1 - TURRET = 2 - - -class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, list): - """An instance of this class represents a module together with its charge and modified attributes""" - DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") - MINING_ATTRIBUTES = ("miningAmount",) - - def __init__(self, item): - """Initialize a module from the program""" - self.__item = item - - if item is not None and self.isInvalid: - raise ValueError("Passed item is not a Module") - - self.__charge = None - self.itemID = item.ID if item is not None else None - self.projected = False - self.state = State.ONLINE - self.build() - - @reconstructor - def init(self): - """Initialize a module from the database and validate""" - self.__item = None - self.__charge = None - - # we need this early if module is invalid and returns early - self.__slot = self.dummySlot - - if self.itemID: - self.__item = eos.db.getItem(self.itemID) - if self.__item is None: - logger.error("Item (id: %d) does not exist", self.itemID) - return - - if self.isInvalid: - logger.error("Item (id: %d) is not a Module", self.itemID) - return - - if self.chargeID: - self.__charge = eos.db.getItem(self.chargeID) - - self.build() - - def build(self): - """ Builds internal module variables from both init's """ - - if self.__charge and self.__charge.category.name != "Charge": - self.__charge = None - - self.__dps = None - self.__miningyield = None - self.__volley = None - self.__reloadTime = None - self.__reloadForce = None - self.__chargeCycles = None - self.__hardpoint = Hardpoint.NONE - self.__itemModifiedAttributes = ModifiedAttributeDict(parent=self) - self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self) - self.__slot = self.dummySlot # defaults to None - - if self.__item: - self.__itemModifiedAttributes.original = self.__item.attributes - self.__itemModifiedAttributes.overrides = self.__item.overrides - self.__hardpoint = self.__calculateHardpoint(self.__item) - self.__slot = self.__calculateSlot(self.__item) - if self.__charge: - self.__chargeModifiedAttributes.original = self.__charge.attributes - self.__chargeModifiedAttributes.overrides = self.__charge.overrides - - def toDummy(self, index): - mod = self[index] - if not mod.isEmpty: - dummy = Module.buildEmpty(mod.slot) - dummy.position = index - self[index] = dummy - - @classmethod - def buildEmpty(cls, slot): - empty = Module(None) - empty.__slot = slot - empty.dummySlot = slot - return empty - - @classmethod - def buildRack(cls, slot): - empty = Rack(None) - empty.__slot = slot - empty.dummySlot = slot - return empty - - @property - def isEmpty(self): - return self.dummySlot is not None - - @property - def hardpoint(self): - return self.__hardpoint - - @property - def isInvalid(self): - if self.isEmpty: - return False - return self.__item is None or (self.__item.category.name not in ("Module", "Subsystem", "Structure Module") and self.__item.group.name != "Effect Beacon") - - @property - def numCharges(self): - if self.charge is None: - charges = 0 - else: - chargeVolume = self.charge.volume - containerCapacity = self.item.capacity - if chargeVolume is None or containerCapacity is None: - charges = 0 - else: - charges = floorFloat(float(containerCapacity) / chargeVolume) - return charges - - @property - def numShots(self): - if self.charge is None: - return None - if self.__chargeCycles is None and self.charge: - numCharges = self.numCharges - # Usual ammo like projectiles and missiles - if numCharges > 0 and "chargeRate" in self.itemModifiedAttributes: - self.__chargeCycles = self.__calculateAmmoShots() - # Frequency crystals (combat and mining lasers) - elif numCharges > 0 and "crystalsGetDamaged" in self.chargeModifiedAttributes: - self.__chargeCycles = self.__calculateCrystalShots() - # Scripts and stuff - else: - self.__chargeCycles = 0 - return self.__chargeCycles - else: - return self.__chargeCycles - - @property - def hpBeforeReload(self): - """ - If item is some kind of repairer with charges, calculate - HP it reps before going into reload. - """ - cycles = self.numShots - armorRep = self.getModifiedItemAttr("armorDamageAmount") or 0 - shieldRep = self.getModifiedItemAttr("shieldBonus") or 0 - if not cycles or (not armorRep and not shieldRep): - return None - hp = round((armorRep + shieldRep) * cycles) - return hp - - def __calculateAmmoShots(self): - if self.charge is not None: - # Set number of cycles before reload is needed - chargeRate = self.getModifiedItemAttr("chargeRate") - numCharges = self.numCharges - numShots = floorFloat(float(numCharges) / chargeRate) - else: - numShots = None - return numShots - - def __calculateCrystalShots(self): - if self.charge is not None: - if self.getModifiedChargeAttr("crystalsGetDamaged") == 1: - # For depletable crystals, calculate average amount of shots before it's destroyed - hp = self.getModifiedChargeAttr("hp") - chance = self.getModifiedChargeAttr("crystalVolatilityChance") - damage = self.getModifiedChargeAttr("crystalVolatilityDamage") - crystals = self.numCharges - numShots = floorFloat(float(crystals * hp) / (damage * chance)) - else: - # Set 0 (infinite) for permanent crystals like t1 laser crystals - numShots = 0 - else: - numShots = None - return numShots - - @property - def maxRange(self): - attrs = ("maxRange", "shieldTransferRange", "powerTransferRange", - "energyDestabilizationRange", "empFieldRange", - "ecmBurstRange", "warpScrambleRange", "cargoScanRange", - "shipScanRange", "surveyScanRange") - for attr in attrs: - maxRange = self.getModifiedItemAttr(attr) - if maxRange is not None: - return maxRange - if self.charge is not None: - try: - chargeName = self.charge.group.name - except AttributeError: - pass - else: - if chargeName in ("Scanner Probe", "Survey Probe"): - return None - # Source: http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1307419&page=1#15 - # D_m = V_m * (T_m + T_0*[exp(- T_m/T_0)-1]) - maxVelocity = self.getModifiedChargeAttr("maxVelocity") - flightTime = self.getModifiedChargeAttr("explosionDelay") / 1000.0 - mass = self.getModifiedChargeAttr("mass") - agility = self.getModifiedChargeAttr("agility") - if maxVelocity and flightTime and mass and agility: - accelTime = min(flightTime, mass * agility / 1000000) - # Average distance done during acceleration - duringAcceleration = maxVelocity / 2 * accelTime - # Distance done after being at full speed - fullSpeed = maxVelocity * (flightTime - accelTime) - return duringAcceleration + fullSpeed - - @property - def falloff(self): - attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff") - for attr in attrs: - falloff = self.getModifiedItemAttr(attr) - if falloff is not None: - return falloff - - @property - def slot(self): - return self.__slot - - @property - def itemModifiedAttributes(self): - return self.__itemModifiedAttributes - - @property - def chargeModifiedAttributes(self): - return self.__chargeModifiedAttributes - - @property - def item(self): - return self.__item if self.__item != 0 else None - - @property - def charge(self): - return self.__charge if self.__charge != 0 else None - - @charge.setter - def charge(self, charge): - self.__charge = charge - if charge is not None: - self.chargeID = charge.ID - self.__chargeModifiedAttributes.original = charge.attributes - self.__chargeModifiedAttributes.overrides = charge.overrides - else: - self.chargeID = None - self.__chargeModifiedAttributes.original = None - self.__chargeModifiedAttributes.overrides = {} - - self.__itemModifiedAttributes.clear() - - def damageStats(self, targetResists): - if self.__dps is None: - self.__dps = 0 - self.__volley = 0 - - if not self.isEmpty and self.state >= State.ACTIVE: - if self.charge: - func = self.getModifiedChargeAttr - else: - func = self.getModifiedItemAttr - - volley = sum(map( - lambda attr: (func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)), - self.DAMAGE_TYPES)) - volley *= self.getModifiedItemAttr("damageMultiplier") or 1 - if volley: - cycleTime = self.cycleTime - self.__volley = volley - self.__dps = volley / (cycleTime / 1000.0) - - return self.__dps, self.__volley - - @property - def miningStats(self): - if self.__miningyield is None: - if self.isEmpty: - self.__miningyield = 0 - else: - if self.state >= State.ACTIVE: - volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr( - "miningAmount") or 0 - if volley: - cycleTime = self.cycleTime - self.__miningyield = volley / (cycleTime / 1000.0) - else: - self.__miningyield = 0 - else: - self.__miningyield = 0 - - return self.__miningyield - - @property - def dps(self): - return self.damageStats(None)[0] - - @property - def volley(self): - return self.damageStats(None)[1] - - @property - def reloadTime(self): - # Get reload time from attrs first, then use - # custom value specified otherwise (e.g. in effects) - moduleReloadTime = self.getModifiedItemAttr("reloadTime") - if moduleReloadTime is None: - moduleReloadTime = self.__reloadTime - return moduleReloadTime - - @reloadTime.setter - def reloadTime(self, milliseconds): - self.__reloadTime = milliseconds - - @property - def forceReload(self): - return self.__reloadForce - - @forceReload.setter - def forceReload(self, type): - self.__reloadForce = type - - def fits(self, fit, hardpointLimit=True): - slot = self.slot - if fit.getSlotsFree(slot) <= (0 if self.owner != fit else -1): - return False - - # Check ship type restrictions - fitsOnType = set() - fitsOnGroup = set() - - shipType = self.getModifiedItemAttr("fitsToShipType") - if shipType is not None: - fitsOnType.add(shipType) - - for attr in self.itemModifiedAttributes.keys(): - if attr.startswith("canFitShipType"): - shipType = self.getModifiedItemAttr(attr) - if shipType is not None: - fitsOnType.add(shipType) - - for attr in self.itemModifiedAttributes.keys(): - if attr.startswith("canFitShipGroup"): - shipGroup = self.getModifiedItemAttr(attr) - if shipGroup is not None: - fitsOnGroup.add(shipGroup) - - if (len(fitsOnGroup) > 0 or len( - fitsOnType) > 0) and fit.ship.item.group.ID not in fitsOnGroup and fit.ship.item.ID not in fitsOnType: - return False - - # AFAIK Citadel modules will always be restricted based on canFitShipType/Group. If we are fitting to a Citadel - # and the module does not have these properties, return false to prevent regular ship modules from being used - if isinstance(fit.ship, Citadel) and len(fitsOnGroup) == 0 and len(fitsOnType) == 0: - return False - - # If the mod is a subsystem, don't let two subs in the same slot fit - if self.slot == Slot.SUBSYSTEM: - subSlot = self.getModifiedItemAttr("subSystemSlot") - for mod in fit.modules: - if mod.getModifiedItemAttr("subSystemSlot") == subSlot: - return False - - # Check rig sizes - if self.slot == Slot.RIG: - if self.getModifiedItemAttr("rigSize") != fit.ship.getModifiedItemAttr("rigSize"): - return False - - # Check max group fitted - max = self.getModifiedItemAttr("maxGroupFitted") - if max is not None: - current = 0 if self.owner != fit else -1 - for mod in fit.modules: - if mod.item and mod.item.groupID == self.item.groupID: - current += 1 - - if current >= max: - return False - - # Check this only if we're told to do so - if hardpointLimit: - if self.hardpoint == Hardpoint.TURRET: - if (fit.ship.getModifiedItemAttr('turretSlotsLeft') or 0) - fit.getHardpointsUsed(Hardpoint.TURRET) < 1: - return False - elif self.hardpoint == Hardpoint.MISSILE: - if (fit.ship.getModifiedItemAttr('launcherSlotsLeft') or 0) - fit.getHardpointsUsed( - Hardpoint.MISSILE) < 1: - return False - - return True - - def isValidState(self, state): - """ - Check if the state is valid for this module, without considering other modules at all - """ - # Check if we're within bounds - if state < -1 or state > 2: - return False - elif state >= State.ACTIVE and not self.item.isType("active"): - return False - elif state == State.OVERHEATED and not self.item.isType("overheat"): - return False - else: - return True - - def canHaveState(self, state=None, projectedOnto=None): - """ - Check with other modules if there are restrictions that might not allow this module to be activated - """ - # If we're going to set module to offline or online for local modules or offline for projected, - # it should be fine for all cases - item = self.item - if (state <= State.ONLINE and projectedOnto is None) or (state <= State.OFFLINE): - return True - - # Check if the local module is over it's max limit; if it's not, we're fine - maxGroupActive = self.getModifiedItemAttr("maxGroupActive") - if maxGroupActive is None and projectedOnto is None: - return True - - # Following is applicable only to local modules, we do not want to limit projected - if projectedOnto is None: - currActive = 0 - group = item.group.name - for mod in self.owner.modules: - currItem = getattr(mod, "item", None) - if mod.state >= State.ACTIVE and currItem is not None and currItem.group.name == group: - currActive += 1 - if currActive > maxGroupActive: - break - return currActive <= maxGroupActive - # For projected, we're checking if ship is vulnerable to given item - else: - # Do not allow to apply offensive modules on ship with offensive module immunite, with few exceptions - # (all effects which apply instant modification are exception, generally speaking) - if item.offensive and projectedOnto.ship.getModifiedItemAttr("disallowOffensiveModifiers") == 1: - offensiveNonModifiers = {"energyDestabilizationNew", - "leech", - "energyNosferatuFalloff", - "energyNeutralizerFalloff"} - if not offensiveNonModifiers.intersection(set(item.effects)): - return False - # If assistive modules are not allowed, do not let to apply these altogether - if item.assistive and projectedOnto.ship.getModifiedItemAttr("disallowAssistance") == 1: - return False - return True - - def isValidCharge(self, charge): - # Check sizes, if 'charge size > module volume' it won't fit - if charge is None: - return True - chargeVolume = charge.volume - moduleCapacity = self.item.capacity - if chargeVolume is not None and moduleCapacity is not None and chargeVolume > moduleCapacity: - return False - - itemChargeSize = self.getModifiedItemAttr("chargeSize") - if itemChargeSize > 0: - chargeSize = charge.getAttribute('chargeSize') - if itemChargeSize != chargeSize: - return False - - chargeGroup = charge.groupID - for i in range(5): - itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) - if itemChargeGroup is None: - continue - if itemChargeGroup == chargeGroup: - return True - - return False - - def getValidCharges(self): - validCharges = set() - for i in range(5): - itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) - if itemChargeGroup is not None: - g = eos.db.getGroup(int(itemChargeGroup), eager=("items.icon", "items.attributes")) - if g is None: - continue - for singleItem in g.items: - if singleItem.published and self.isValidCharge(singleItem): - validCharges.add(singleItem) - - return validCharges - - def __calculateHardpoint(self, item): - effectHardpointMap = {"turretFitted": Hardpoint.TURRET, - "launcherFitted": Hardpoint.MISSILE} - - if item is None: - return Hardpoint.NONE - - for effectName, slot in effectHardpointMap.iteritems(): - if effectName in item.effects: - return slot - - return Hardpoint.NONE - - def __calculateSlot(self, item): - effectSlotMap = {"rigSlot": Slot.RIG, - "loPower": Slot.LOW, - "medPower": Slot.MED, - "hiPower": Slot.HIGH, - "subSystem": Slot.SUBSYSTEM, - "serviceSlot": Slot.SERVICE} - if item is None: - return None - for effectName, slot in effectSlotMap.iteritems(): - if effectName in item.effects: - return slot - if item.group.name == "Effect Beacon": - return Slot.SYSTEM - - raise ValueError("Passed item does not fit in any known slot") - - @validates("ID", "itemID", "ammoID") - def validator(self, key, val): - map = {"ID": lambda val: isinstance(val, int), - "itemID": lambda val: val is None or isinstance(val, int), - "ammoID": lambda val: isinstance(val, int)} - - if not map[key](val): - raise ValueError(str(val) + " is not a valid value for " + key) - else: - return val - - def clear(self): - self.__dps = None - self.__miningyield = None - self.__volley = None - self.__reloadTime = None - self.__reloadForce = None - self.__chargeCycles = None - self.itemModifiedAttributes.clear() - self.chargeModifiedAttributes.clear() - - def calculateModifiedAttributes(self, fit, runTime, forceProjected = False, gang = False): - #We will run the effect when two conditions are met: - #1: It makes sense to run the effect - # The effect is either offline - # or the effect is passive and the module is in the online state (or higher) - - # or the effect is active and the module is in the active state (or higher) - # or the effect is overheat and the module is in the overheated state (or higher) - # 2: the runtimes match - - if self.projected or forceProjected: - context = "projected", "module" - projected = True - else: - context = ("module",) - projected = False - - # if gang: - # context += ("commandRun",) - - if self.charge is not None: - # fix for #82 and it's regression #106 - if not projected or (self.projected and not forceProjected) or gang: - for effect in self.charge.effects.itervalues(): - if effect.runTime == runTime and \ - effect.activeByDefault and \ - (effect.isType("offline") or - (effect.isType("passive") and self.state >= State.ONLINE) or - (effect.isType("active") and self.state >= State.ACTIVE)) and \ - (not gang or (gang and effect.isType("gang"))): - - chargeContext = ("moduleCharge",) - # For gang effects, we pass in the effect itself as an argument. However, to avoid going through - # all the effect files and defining this argument, do a simple try/catch here and be done with it. - # @todo: possibly fix this - try: - effect.handler(fit, self, chargeContext, effect=effect) - except: - effect.handler(fit, self, chargeContext) - - if self.item: - if self.state >= State.OVERHEATED: - for effect in self.item.effects.itervalues(): - if effect.runTime == runTime and \ - effect.isType("overheat") \ - and not forceProjected \ - and effect.activeByDefault \ - and ((gang and effect.isType("gang")) or not gang): - effect.handler(fit, self, context) - - for effect in self.item.effects.itervalues(): - if effect.runTime == runTime and \ - effect.activeByDefault and \ - (effect.isType("offline") or - (effect.isType("passive") and self.state >= State.ONLINE) or - (effect.isType("active") and self.state >= State.ACTIVE))\ - and ((projected and effect.isType("projected")) or not projected)\ - and ((gang and effect.isType("gang")) or not gang): - effect.handler(fit, self, context) - - @property - def cycleTime(self): - reactivation = (self.getModifiedItemAttr("moduleReactivationDelay") or 0) - # Reactivation time starts counting after end of module cycle - speed = self.rawCycleTime + reactivation - if self.charge: - reload = self.reloadTime - else: - reload = 0.0 - # Determine if we'll take into account reload time or not - factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload - # If reactivation is longer than 10 seconds then module can be reloaded - # during reactivation time, thus we may ignore reload - if factorReload and reactivation < reload: - numShots = self.numShots - # Time it takes to reload module after end of reactivation time, - # given that we started when module cycle has just over - additionalReloadTime = (reload - reactivation) - # Speed here already takes into consideration reactivation time - speed = (speed * numShots + additionalReloadTime) / numShots if numShots > 0 else speed - - return speed - - @property - def rawCycleTime(self): - speed = self.getModifiedItemAttr("speed") or self.getModifiedItemAttr("duration") - return speed - - @property - def capUse(self): - capNeed = self.getModifiedItemAttr("capacitorNeed") - if capNeed and self.state >= State.ACTIVE: - cycleTime = self.cycleTime - capUsed = capNeed / (cycleTime / 1000.0) - return capUsed - else: - return 0 - - def __deepcopy__(self, memo): - item = self.item - if item is None: - copy = Module.buildEmpty(self.slot) - else: - copy = Module(self.item) - copy.charge = self.charge - copy.state = self.state - return copy - - def __repr__(self): - if self.item: - return "Module(ID={}, name={}) at {}".format( - self.item.ID, self.item.name, hex(id(self)) - ) - else: - return "EmptyModule() at {}".format(hex(id(self))) - - -class Rack(Module): - """ - This is simply the Module class named something else to differentiate - it for app logic. This class does not do anything special - """ - pass +# =============================================================================== +# Copyright (C) 2010 Diego Duclos +# +# This file is part of eos. +# +# eos is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# eos is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with eos. If not, see . +# =============================================================================== + +import logging + +from sqlalchemy.orm import validates, reconstructor + +import eos.db +from eos.effectHandlerHelpers import HandledItem, HandledCharge +from eos.enum import Enum +from eos.mathUtils import floorFloat +from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut +from eos.types import Citadel + +logger = logging.getLogger(__name__) + + +class State(Enum): + OFFLINE = -1 + ONLINE = 0 + ACTIVE = 1 + OVERHEATED = 2 + + +class Slot(Enum): + # These are self-explanatory + LOW = 1 + MED = 2 + HIGH = 3 + RIG = 4 + SUBSYSTEM = 5 + # not a real slot, need for pyfa display rack separation + MODE = 6 + # system effects. They are projected "modules" and pyfa assumes all modules + # have a slot. In this case, make one up. + SYSTEM = 7 + # used for citadel services + SERVICE = 8 + # fighter 'slots'. Just easier to put them here... + F_LIGHT = 10 + F_SUPPORT = 11 + F_HEAVY = 12 + + +class Hardpoint(Enum): + NONE = 0 + MISSILE = 1 + TURRET = 2 + + +class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, list): + """An instance of this class represents a module together with its charge and modified attributes""" + DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") + MINING_ATTRIBUTES = ("miningAmount",) + + def __init__(self, item): + """Initialize a module from the program""" + self.__item = item + + if item is not None and self.isInvalid: + raise ValueError("Passed item is not a Module") + + self.__charge = None + self.itemID = item.ID if item is not None else None + self.projected = False + self.state = State.ONLINE + self.build() + + @reconstructor + def init(self): + """Initialize a module from the database and validate""" + self.__item = None + self.__charge = None + + # we need this early if module is invalid and returns early + self.__slot = self.dummySlot + + if self.itemID: + self.__item = eos.db.getItem(self.itemID) + if self.__item is None: + logger.error("Item (id: %d) does not exist", self.itemID) + return + + if self.isInvalid: + logger.error("Item (id: %d) is not a Module", self.itemID) + return + + if self.chargeID: + self.__charge = eos.db.getItem(self.chargeID) + + self.build() + + def build(self): + """ Builds internal module variables from both init's """ + + if self.__charge and self.__charge.category.name != "Charge": + self.__charge = None + + self.__dps = None + self.__miningyield = None + self.__volley = None + self.__reloadTime = None + self.__reloadForce = None + self.__chargeCycles = None + self.__hardpoint = Hardpoint.NONE + self.__itemModifiedAttributes = ModifiedAttributeDict(parent=self) + self.__chargeModifiedAttributes = ModifiedAttributeDict(parent=self) + self.__slot = self.dummySlot # defaults to None + + if self.__item: + self.__itemModifiedAttributes.original = self.__item.attributes + self.__itemModifiedAttributes.overrides = self.__item.overrides + self.__hardpoint = self.__calculateHardpoint(self.__item) + self.__slot = self.__calculateSlot(self.__item) + if self.__charge: + self.__chargeModifiedAttributes.original = self.__charge.attributes + self.__chargeModifiedAttributes.overrides = self.__charge.overrides + + def toDummy(self, index): + mod = self[index] + if not mod.isEmpty: + dummy = Module.buildEmpty(mod.slot) + dummy.position = index + self[index] = dummy + + @classmethod + def buildEmpty(cls, slot): + empty = Module(None) + empty.__slot = slot + empty.dummySlot = slot + return empty + + @classmethod + def buildRack(cls, slot): + empty = Rack(None) + empty.__slot = slot + empty.dummySlot = slot + return empty + + @property + def isEmpty(self): + return self.dummySlot is not None + + @property + def hardpoint(self): + return self.__hardpoint + + @property + def isInvalid(self): + if self.isEmpty: + return False + return self.__item is None or \ + (self.__item.category.name not in ("Module", "Subsystem", "Structure Module") and + self.__item.group.name != "Effect Beacon") + + @property + def numCharges(self): + if self.charge is None: + charges = 0 + else: + chargeVolume = self.charge.volume + containerCapacity = self.item.capacity + if chargeVolume is None or containerCapacity is None: + charges = 0 + else: + charges = floorFloat(float(containerCapacity) / chargeVolume) + return charges + + @property + def numShots(self): + if self.charge is None: + return None + if self.__chargeCycles is None and self.charge: + numCharges = self.numCharges + # Usual ammo like projectiles and missiles + if numCharges > 0 and "chargeRate" in self.itemModifiedAttributes: + self.__chargeCycles = self.__calculateAmmoShots() + # Frequency crystals (combat and mining lasers) + elif numCharges > 0 and "crystalsGetDamaged" in self.chargeModifiedAttributes: + self.__chargeCycles = self.__calculateCrystalShots() + # Scripts and stuff + else: + self.__chargeCycles = 0 + return self.__chargeCycles + else: + return self.__chargeCycles + + @property + def hpBeforeReload(self): + """ + If item is some kind of repairer with charges, calculate + HP it reps before going into reload. + """ + cycles = self.numShots + armorRep = self.getModifiedItemAttr("armorDamageAmount") or 0 + shieldRep = self.getModifiedItemAttr("shieldBonus") or 0 + if not cycles or (not armorRep and not shieldRep): + return None + hp = round((armorRep + shieldRep) * cycles) + return hp + + def __calculateAmmoShots(self): + if self.charge is not None: + # Set number of cycles before reload is needed + chargeRate = self.getModifiedItemAttr("chargeRate") + numCharges = self.numCharges + numShots = floorFloat(float(numCharges) / chargeRate) + else: + numShots = None + return numShots + + def __calculateCrystalShots(self): + if self.charge is not None: + if self.getModifiedChargeAttr("crystalsGetDamaged") == 1: + # For depletable crystals, calculate average amount of shots before it's destroyed + hp = self.getModifiedChargeAttr("hp") + chance = self.getModifiedChargeAttr("crystalVolatilityChance") + damage = self.getModifiedChargeAttr("crystalVolatilityDamage") + crystals = self.numCharges + numShots = floorFloat(float(crystals * hp) / (damage * chance)) + else: + # Set 0 (infinite) for permanent crystals like t1 laser crystals + numShots = 0 + else: + numShots = None + return numShots + + @property + def maxRange(self): + attrs = ("maxRange", "shieldTransferRange", "powerTransferRange", + "energyDestabilizationRange", "empFieldRange", + "ecmBurstRange", "warpScrambleRange", "cargoScanRange", + "shipScanRange", "surveyScanRange") + for attr in attrs: + maxRange = self.getModifiedItemAttr(attr) + if maxRange is not None: + return maxRange + if self.charge is not None: + try: + chargeName = self.charge.group.name + except AttributeError: + pass + else: + if chargeName in ("Scanner Probe", "Survey Probe"): + return None + # Source: http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1307419&page=1#15 + # D_m = V_m * (T_m + T_0*[exp(- T_m/T_0)-1]) + maxVelocity = self.getModifiedChargeAttr("maxVelocity") + flightTime = self.getModifiedChargeAttr("explosionDelay") / 1000.0 + mass = self.getModifiedChargeAttr("mass") + agility = self.getModifiedChargeAttr("agility") + if maxVelocity and flightTime and mass and agility: + accelTime = min(flightTime, mass * agility / 1000000) + # Average distance done during acceleration + duringAcceleration = maxVelocity / 2 * accelTime + # Distance done after being at full speed + fullSpeed = maxVelocity * (flightTime - accelTime) + return duringAcceleration + fullSpeed + + @property + def falloff(self): + attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff") + for attr in attrs: + falloff = self.getModifiedItemAttr(attr) + if falloff is not None: + return falloff + + @property + def slot(self): + return self.__slot + + @property + def itemModifiedAttributes(self): + return self.__itemModifiedAttributes + + @property + def chargeModifiedAttributes(self): + return self.__chargeModifiedAttributes + + @property + def item(self): + return self.__item if self.__item != 0 else None + + @property + def charge(self): + return self.__charge if self.__charge != 0 else None + + @charge.setter + def charge(self, charge): + self.__charge = charge + if charge is not None: + self.chargeID = charge.ID + self.__chargeModifiedAttributes.original = charge.attributes + self.__chargeModifiedAttributes.overrides = charge.overrides + else: + self.chargeID = None + self.__chargeModifiedAttributes.original = None + self.__chargeModifiedAttributes.overrides = {} + + self.__itemModifiedAttributes.clear() + + def damageStats(self, targetResists): + if self.__dps is None: + self.__dps = 0 + self.__volley = 0 + + if not self.isEmpty and self.state >= State.ACTIVE: + if self.charge: + func = self.getModifiedChargeAttr + else: + func = self.getModifiedItemAttr + + volley = sum(map( + lambda attr: (func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)), + self.DAMAGE_TYPES)) + volley *= self.getModifiedItemAttr("damageMultiplier") or 1 + if volley: + cycleTime = self.cycleTime + self.__volley = volley + self.__dps = volley / (cycleTime / 1000.0) + + return self.__dps, self.__volley + + @property + def miningStats(self): + if self.__miningyield is None: + if self.isEmpty: + self.__miningyield = 0 + else: + if self.state >= State.ACTIVE: + volley = self.getModifiedItemAttr("specialtyMiningAmount") or self.getModifiedItemAttr( + "miningAmount") or 0 + if volley: + cycleTime = self.cycleTime + self.__miningyield = volley / (cycleTime / 1000.0) + else: + self.__miningyield = 0 + else: + self.__miningyield = 0 + + return self.__miningyield + + @property + def dps(self): + return self.damageStats(None)[0] + + @property + def volley(self): + return self.damageStats(None)[1] + + @property + def reloadTime(self): + # Get reload time from attrs first, then use + # custom value specified otherwise (e.g. in effects) + moduleReloadTime = self.getModifiedItemAttr("reloadTime") + if moduleReloadTime is None: + moduleReloadTime = self.__reloadTime + return moduleReloadTime + + @reloadTime.setter + def reloadTime(self, milliseconds): + self.__reloadTime = milliseconds + + @property + def forceReload(self): + return self.__reloadForce + + @forceReload.setter + def forceReload(self, type): + self.__reloadForce = type + + def fits(self, fit, hardpointLimit=True): + slot = self.slot + if fit.getSlotsFree(slot) <= (0 if self.owner != fit else -1): + return False + + # Check ship type restrictions + fitsOnType = set() + fitsOnGroup = set() + + shipType = self.getModifiedItemAttr("fitsToShipType") + if shipType is not None: + fitsOnType.add(shipType) + + for attr in self.itemModifiedAttributes.keys(): + if attr.startswith("canFitShipType"): + shipType = self.getModifiedItemAttr(attr) + if shipType is not None: + fitsOnType.add(shipType) + + for attr in self.itemModifiedAttributes.keys(): + if attr.startswith("canFitShipGroup"): + shipGroup = self.getModifiedItemAttr(attr) + if shipGroup is not None: + fitsOnGroup.add(shipGroup) + + if (len(fitsOnGroup) > 0 or len( + fitsOnType) > 0) and fit.ship.item.group.ID not in fitsOnGroup and fit.ship.item.ID not in fitsOnType: + return False + + # AFAIK Citadel modules will always be restricted based on canFitShipType/Group. If we are fitting to a Citadel + # and the module does not have these properties, return false to prevent regular ship modules from being used + if isinstance(fit.ship, Citadel) and len(fitsOnGroup) == 0 and len(fitsOnType) == 0: + return False + + # If the mod is a subsystem, don't let two subs in the same slot fit + if self.slot == Slot.SUBSYSTEM: + subSlot = self.getModifiedItemAttr("subSystemSlot") + for mod in fit.modules: + if mod.getModifiedItemAttr("subSystemSlot") == subSlot: + return False + + # Check rig sizes + if self.slot == Slot.RIG: + if self.getModifiedItemAttr("rigSize") != fit.ship.getModifiedItemAttr("rigSize"): + return False + + # Check max group fitted + max = self.getModifiedItemAttr("maxGroupFitted") + if max is not None: + current = 0 if self.owner != fit else -1 + for mod in fit.modules: + if mod.item and mod.item.groupID == self.item.groupID: + current += 1 + + if current >= max: + return False + + # Check this only if we're told to do so + if hardpointLimit: + if self.hardpoint == Hardpoint.TURRET: + if (fit.ship.getModifiedItemAttr('turretSlotsLeft') or 0) - fit.getHardpointsUsed(Hardpoint.TURRET) < 1: + return False + elif self.hardpoint == Hardpoint.MISSILE: + if (fit.ship.getModifiedItemAttr('launcherSlotsLeft') or 0) - fit.getHardpointsUsed( + Hardpoint.MISSILE) < 1: + return False + + return True + + def isValidState(self, state): + """ + Check if the state is valid for this module, without considering other modules at all + """ + # Check if we're within bounds + if state < -1 or state > 2: + return False + elif state >= State.ACTIVE and not self.item.isType("active"): + return False + elif state == State.OVERHEATED and not self.item.isType("overheat"): + return False + else: + return True + + def canHaveState(self, state=None, projectedOnto=None): + """ + Check with other modules if there are restrictions that might not allow this module to be activated + """ + # If we're going to set module to offline or online for local modules or offline for projected, + # it should be fine for all cases + item = self.item + if (state <= State.ONLINE and projectedOnto is None) or (state <= State.OFFLINE): + return True + + # Check if the local module is over it's max limit; if it's not, we're fine + maxGroupActive = self.getModifiedItemAttr("maxGroupActive") + if maxGroupActive is None and projectedOnto is None: + return True + + # Following is applicable only to local modules, we do not want to limit projected + if projectedOnto is None: + currActive = 0 + group = item.group.name + for mod in self.owner.modules: + currItem = getattr(mod, "item", None) + if mod.state >= State.ACTIVE and currItem is not None and currItem.group.name == group: + currActive += 1 + if currActive > maxGroupActive: + break + return currActive <= maxGroupActive + # For projected, we're checking if ship is vulnerable to given item + else: + # Do not allow to apply offensive modules on ship with offensive module immunite, with few exceptions + # (all effects which apply instant modification are exception, generally speaking) + if item.offensive and projectedOnto.ship.getModifiedItemAttr("disallowOffensiveModifiers") == 1: + offensiveNonModifiers = {"energyDestabilizationNew", + "leech", + "energyNosferatuFalloff", + "energyNeutralizerFalloff"} + if not offensiveNonModifiers.intersection(set(item.effects)): + return False + # If assistive modules are not allowed, do not let to apply these altogether + if item.assistive and projectedOnto.ship.getModifiedItemAttr("disallowAssistance") == 1: + return False + return True + + def isValidCharge(self, charge): + # Check sizes, if 'charge size > module volume' it won't fit + if charge is None: + return True + chargeVolume = charge.volume + moduleCapacity = self.item.capacity + if chargeVolume is not None and moduleCapacity is not None and chargeVolume > moduleCapacity: + return False + + itemChargeSize = self.getModifiedItemAttr("chargeSize") + if itemChargeSize > 0: + chargeSize = charge.getAttribute('chargeSize') + if itemChargeSize != chargeSize: + return False + + chargeGroup = charge.groupID + for i in range(5): + itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) + if itemChargeGroup is None: + continue + if itemChargeGroup == chargeGroup: + return True + + return False + + def getValidCharges(self): + validCharges = set() + for i in range(5): + itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) + if itemChargeGroup is not None: + g = eos.db.getGroup(int(itemChargeGroup), eager=("items.icon", "items.attributes")) + if g is None: + continue + for singleItem in g.items: + if singleItem.published and self.isValidCharge(singleItem): + validCharges.add(singleItem) + + return validCharges + + def __calculateHardpoint(self, item): + effectHardpointMap = {"turretFitted": Hardpoint.TURRET, + "launcherFitted": Hardpoint.MISSILE} + + if item is None: + return Hardpoint.NONE + + for effectName, slot in effectHardpointMap.iteritems(): + if effectName in item.effects: + return slot + + return Hardpoint.NONE + + def __calculateSlot(self, item): + effectSlotMap = {"rigSlot": Slot.RIG, + "loPower": Slot.LOW, + "medPower": Slot.MED, + "hiPower": Slot.HIGH, + "subSystem": Slot.SUBSYSTEM, + "serviceSlot": Slot.SERVICE} + if item is None: + return None + for effectName, slot in effectSlotMap.iteritems(): + if effectName in item.effects: + return slot + if item.group.name == "Effect Beacon": + return Slot.SYSTEM + + raise ValueError("Passed item does not fit in any known slot") + + @validates("ID", "itemID", "ammoID") + def validator(self, key, val): + map = {"ID": lambda val: isinstance(val, int), + "itemID": lambda val: val is None or isinstance(val, int), + "ammoID": lambda val: isinstance(val, int)} + + if not map[key](val): + raise ValueError(str(val) + " is not a valid value for " + key) + else: + return val + + def clear(self): + self.__dps = None + self.__miningyield = None + self.__volley = None + self.__reloadTime = None + self.__reloadForce = None + self.__chargeCycles = None + self.itemModifiedAttributes.clear() + self.chargeModifiedAttributes.clear() + + def calculateModifiedAttributes(self, fit, runTime, forceProjected=False, gang=False): + # We will run the effect when two conditions are met: + # 1: It makes sense to run the effect + # The effect is either offline + # or the effect is passive and the module is in the online state (or higher) + + # or the effect is active and the module is in the active state (or higher) + # or the effect is overheat and the module is in the overheated state (or higher) + # 2: the runtimes match + + if self.projected or forceProjected: + context = "projected", "module" + projected = True + else: + context = ("module",) + projected = False + + # if gang: + # context += ("commandRun",) + + if self.charge is not None: + # fix for #82 and it's regression #106 + if not projected or (self.projected and not forceProjected) or gang: + for effect in self.charge.effects.itervalues(): + if effect.runTime == runTime and \ + effect.activeByDefault and \ + (effect.isType("offline") or + (effect.isType("passive") and self.state >= State.ONLINE) or + (effect.isType("active") and self.state >= State.ACTIVE)) and \ + (not gang or (gang and effect.isType("gang"))): + + chargeContext = ("moduleCharge",) + # For gang effects, we pass in the effect itself as an argument. However, to avoid going through + # all the effect files and defining this argument, do a simple try/catch here and be done with it. + # @todo: possibly fix this + try: + effect.handler(fit, self, chargeContext, effect=effect) + except: + effect.handler(fit, self, chargeContext) + + if self.item: + if self.state >= State.OVERHEATED: + for effect in self.item.effects.itervalues(): + if effect.runTime == runTime and \ + effect.isType("overheat") \ + and not forceProjected \ + and effect.activeByDefault \ + and ((gang and effect.isType("gang")) or not gang): + effect.handler(fit, self, context) + + for effect in self.item.effects.itervalues(): + if effect.runTime == runTime and \ + effect.activeByDefault and \ + (effect.isType("offline") or + (effect.isType("passive") and self.state >= State.ONLINE) or + (effect.isType("active") and self.state >= State.ACTIVE)) \ + and ((projected and effect.isType("projected")) or not projected) \ + and ((gang and effect.isType("gang")) or not gang): + effect.handler(fit, self, context) + + @property + def cycleTime(self): + reactivation = (self.getModifiedItemAttr("moduleReactivationDelay") or 0) + # Reactivation time starts counting after end of module cycle + speed = self.rawCycleTime + reactivation + if self.charge: + reload = self.reloadTime + else: + reload = 0.0 + # Determine if we'll take into account reload time or not + factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload + # If reactivation is longer than 10 seconds then module can be reloaded + # during reactivation time, thus we may ignore reload + if factorReload and reactivation < reload: + numShots = self.numShots + # Time it takes to reload module after end of reactivation time, + # given that we started when module cycle has just over + additionalReloadTime = (reload - reactivation) + # Speed here already takes into consideration reactivation time + speed = (speed * numShots + additionalReloadTime) / numShots if numShots > 0 else speed + + return speed + + @property + def rawCycleTime(self): + speed = self.getModifiedItemAttr("speed") or self.getModifiedItemAttr("duration") + return speed + + @property + def capUse(self): + capNeed = self.getModifiedItemAttr("capacitorNeed") + if capNeed and self.state >= State.ACTIVE: + cycleTime = self.cycleTime + capUsed = capNeed / (cycleTime / 1000.0) + return capUsed + else: + return 0 + + def __deepcopy__(self, memo): + item = self.item + if item is None: + copy = Module.buildEmpty(self.slot) + else: + copy = Module(self.item) + copy.charge = self.charge + copy.state = self.state + return copy + + def __repr__(self): + if self.item: + return "Module(ID={}, name={}) at {}".format( + self.item.ID, self.item.name, hex(id(self)) + ) + else: + return "EmptyModule() at {}".format(hex(id(self))) + + +class Rack(Module): + """ + This is simply the Module class named something else to differentiate + it for app logic. This class does not do anything special + """ + pass diff --git a/eos/saveddata/targetResists.py b/eos/saveddata/targetResists.py index 8b286b3f6..67cd323a8 100644 --- a/eos/saveddata/targetResists.py +++ b/eos/saveddata/targetResists.py @@ -77,7 +77,12 @@ class TargetResists(object): out += "# TargetResists = [name],[EM %],[Thermal %],[Kinetic %],[Explosive %]\n\n" for dp in patterns: out += cls.EXPORT_FORMAT % ( - dp.name, dp.emAmount * 100, dp.thermalAmount * 100, dp.kineticAmount * 100, dp.explosiveAmount * 100) + dp.name, + dp.emAmount * 100, + dp.thermalAmount * 100, + dp.kineticAmount * 100, + dp.explosiveAmount * 100 + ) return out.strip() diff --git a/gui/builtinContextMenus/__init__.py b/gui/builtinContextMenus/__init__.py index 55f26256a..c6a41c112 100644 --- a/gui/builtinContextMenus/__init__.py +++ b/gui/builtinContextMenus/__init__.py @@ -1,6 +1,6 @@ __all__ = [ "openFit", - #"moduleGlobalAmmoPicker", + # "moduleGlobalAmmoPicker", "moduleAmmoPicker", "itemStats", "damagePattern", diff --git a/gui/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py index a3186b723..aeac92e5c 100644 --- a/gui/builtinContextMenus/amount.py +++ b/gui/builtinContextMenus/amount.py @@ -1,5 +1,4 @@ from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog import eos.types import gui.mainFrame import gui.globalEvents as GE @@ -8,12 +7,13 @@ from service.fit import Fit from eos.saveddata.cargo import Cargo as es_Cargo from eos.saveddata.fighter import Fighter as es_Fighter + class ChangeAmount(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() def display(self, srcContext, selection): - return srcContext in ("cargoItem","projectedFit","fighterItem","projectedFighter") + return srcContext in ("cargoItem", "projectedFit", "fighterItem", "projectedFighter") def getText(self, itmContext, selection): return "Change {0} Quantity".format(itmContext) @@ -26,8 +26,8 @@ class ChangeAmount(ContextMenu): ChangeAmount.register() -class AmountChanger(wx.Dialog): +class AmountChanger(wx.Dialog): def __init__(self, parent, thing, context): wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60)) self.thing = thing @@ -65,15 +65,14 @@ class AmountChanger(wx.Dialog): event.Skip() self.Close() - ## checks to make sure it's valid number + # checks to make sure it's valid number def onChar(self, event): key = event.GetKeyCode() acceptable_characters = "1234567890" - acceptable_keycode = [3, 22, 13, 8, 127] # modifiers like delete, copy, paste + acceptable_keycode = [3, 22, 13, 8, 127] # modifiers like delete, copy, paste if key in acceptable_keycode or key >= 255 or (key < 255 and chr(key) in acceptable_characters): event.Skip() return else: return False - diff --git a/gui/builtinContextMenus/cargo.py b/gui/builtinContextMenus/cargo.py index 8d1705ee6..1f5ad4202 100644 --- a/gui/builtinContextMenus/cargo.py +++ b/gui/builtinContextMenus/cargo.py @@ -1,11 +1,10 @@ from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import eos.types import gui.mainFrame import gui.globalEvents as GE import wx from service.fit import Fit + class Cargo(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index d679a8791..79b7bd3bd 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -8,6 +8,7 @@ import gui.globalEvents as GE from service.fit import Fit from service.character import Character + class ChangeAffectingSkills(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -22,7 +23,7 @@ class ChangeAffectingSkills(ContextMenu): self.charID = fit.character.ID - #if self.sChar.getCharName(self.charID) in ("All 0", "All 5"): + # if self.sChar.getCharName(self.charID) in ("All 0", "All 5"): # return False if srcContext == "fittingShip": diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index 553c02e04..b2089e9c0 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -11,6 +11,7 @@ try: except ImportError: from gui.utils.compat import OrderedDict + class DamagePattern(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -35,9 +36,9 @@ class DamagePattern(ContextMenu): for pattern in self.patterns: start, end = pattern.name.find('['), pattern.name.find(']') if start is not -1 and end is not -1: - currBase = pattern.name[start+1:end] + currBase = pattern.name[start + 1:end] # set helper attr - setattr(pattern, "_name", pattern.name[end+1:].strip()) + setattr(pattern, "_name", pattern.name[end + 1:].strip()) if currBase not in self.subMenus: self.subMenus[currBase] = [] self.subMenus[currBase].append(pattern) @@ -102,7 +103,7 @@ class DamagePattern(ContextMenu): sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() sFit.setDamagePattern(fitID, pattern) - setattr(self.mainFrame,"_activeDmgPattern", pattern) + setattr(self.mainFrame, "_activeDmgPattern", pattern) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) diff --git a/gui/builtinContextMenus/droneRemoveStack.py b/gui/builtinContextMenus/droneRemoveStack.py index af2ecd9ea..8132a8fc9 100644 --- a/gui/builtinContextMenus/droneRemoveStack.py +++ b/gui/builtinContextMenus/droneRemoveStack.py @@ -2,6 +2,8 @@ from gui.contextMenu import ContextMenu import gui.mainFrame import wx import gui.globalEvents as GE +from service.fit import Fit + class ItemRemove(ContextMenu): def __init__(self): diff --git a/gui/builtinContextMenus/droneSplit.py b/gui/builtinContextMenus/droneSplit.py index 476e40eee..2f2ec923e 100644 --- a/gui/builtinContextMenus/droneSplit.py +++ b/gui/builtinContextMenus/droneSplit.py @@ -1,5 +1,4 @@ from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog import gui.mainFrame import gui.globalEvents as GE from service.fit import Fit @@ -27,7 +26,6 @@ DroneSplit.register() class DroneSpinner(wx.Dialog): - def __init__(self, parent, drone, context): wx.Dialog.__init__(self, parent, title="Select Amount", size=wx.Size(220, 60)) self.drone = drone diff --git a/gui/builtinContextMenus/factorReload.py b/gui/builtinContextMenus/factorReload.py index ec7c1acdb..d7b33658d 100644 --- a/gui/builtinContextMenus/factorReload.py +++ b/gui/builtinContextMenus/factorReload.py @@ -1,35 +1,36 @@ -from gui.contextMenu import ContextMenu -import gui.mainFrame -import gui.globalEvents as GE -import wx -from gui.bitmapLoader import BitmapLoader -from service.fit import Fit - -class FactorReload(ContextMenu): - def __init__(self): - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def display(self, srcContext, selection): - return srcContext == "firepowerViewFull" and self.mainFrame.getActiveFit() is not None - - def getText(self, itmContext, selection): - return "Factor in Reload Time" - - def activate(self, fullContext, selection, i): - sFit = Fit.getInstance() - sFit.serviceFittingOptions["useGlobalForceReload"] = not sFit.serviceFittingOptions["useGlobalForceReload"] - fitID = self.mainFrame.getActiveFit() - sFit.refreshFit(fitID) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - - def getBitmap(self, context, selection): - sFit = Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(fitID) - if fit.factorReload: - return BitmapLoader.getBitmap("state_active_small", "gui") - else: - return None - - -FactorReload.register() +from gui.contextMenu import ContextMenu +import gui.mainFrame +import gui.globalEvents as GE +import wx +from gui.bitmapLoader import BitmapLoader +from service.fit import Fit + + +class FactorReload(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + return srcContext == "firepowerViewFull" and self.mainFrame.getActiveFit() is not None + + def getText(self, itmContext, selection): + return "Factor in Reload Time" + + def activate(self, fullContext, selection, i): + sFit = Fit.getInstance() + sFit.serviceFittingOptions["useGlobalForceReload"] = not sFit.serviceFittingOptions["useGlobalForceReload"] + fitID = self.mainFrame.getActiveFit() + sFit.refreshFit(fitID) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + def getBitmap(self, context, selection): + sFit = Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + fit = sFit.getFit(fitID) + if fit.factorReload: + return BitmapLoader.getBitmap("state_active_small", "gui") + else: + return None + + +FactorReload.register() diff --git a/gui/builtinContextMenus/fighterAbilities.py b/gui/builtinContextMenus/fighterAbilities.py index e422070cc..d262cb052 100644 --- a/gui/builtinContextMenus/fighterAbilities.py +++ b/gui/builtinContextMenus/fighterAbilities.py @@ -2,6 +2,8 @@ import wx from gui.contextMenu import ContextMenu import gui.mainFrame import gui.globalEvents as GE +from service.fit import Fit + class FighterAbility(ContextMenu): def __init__(self): diff --git a/gui/builtinContextMenus/implantSets.py b/gui/builtinContextMenus/implantSets.py index 0cbfef508..490f52ef2 100644 --- a/gui/builtinContextMenus/implantSets.py +++ b/gui/builtinContextMenus/implantSets.py @@ -2,6 +2,10 @@ from gui.contextMenu import ContextMenu import gui.mainFrame import gui.globalEvents as GE import wx +from service.implantSet import ImplantSets as s_ImplantSets +from service.character import Character +from service.fit import Fit + class ImplantSets(ContextMenu): def __init__(self): @@ -31,7 +35,7 @@ class ImplantSets(ContextMenu): m = wx.Menu() bindmenu = rootMenu if "wxMSW" in wx.PlatformInfo else m - sIS = service.ImplantSets.getInstance() + sIS = s_ImplantSets.getInstance() implantSets = sIS.getImplantSetList() self.context = context diff --git a/gui/builtinContextMenus/itemRemove.py b/gui/builtinContextMenus/itemRemove.py index 5fbac825f..400890b1a 100644 --- a/gui/builtinContextMenus/itemRemove.py +++ b/gui/builtinContextMenus/itemRemove.py @@ -2,6 +2,8 @@ from gui.contextMenu import ContextMenu import gui.mainFrame import wx import gui.globalEvents as GE +from service.fit import Fit + class ItemRemove(ContextMenu): def __init__(self): @@ -27,8 +29,8 @@ class ItemRemove(ContextMenu): if srcContext == "fittingModule": for module in selection: if module is not None: - sFit.removeModule(fitID,fit.modules.index(module)) - elif srcContext in ("fittingCharge" , "projectedCharge"): + sFit.removeModule(fitID, fit.modules.index(module)) + elif srcContext in ("fittingCharge", "projectedCharge"): sFit.setAmmo(fitID, None, selection) elif srcContext == "droneItem": sFit.removeDrone(fitID, fit.drones.index(selection[0])) @@ -46,6 +48,4 @@ class ItemRemove(ContextMenu): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - - ItemRemove.register() diff --git a/gui/builtinContextMenus/marketJump.py b/gui/builtinContextMenus/marketJump.py index eea2a9a2b..e96292b41 100644 --- a/gui/builtinContextMenus/marketJump.py +++ b/gui/builtinContextMenus/marketJump.py @@ -1,8 +1,8 @@ from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog import gui.mainFrame from service.market import Market + class MarketJump(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py index 66e9ed8a2..3462410e5 100644 --- a/gui/builtinContextMenus/metaSwap.py +++ b/gui/builtinContextMenus/metaSwap.py @@ -8,6 +8,7 @@ import gui.mainFrame import gui.globalEvents as GE from gui.contextMenu import ContextMenu + class MetaSwap(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -94,7 +95,7 @@ class MetaSwap(ContextMenu): sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() fit = sFit.getFit(fitID) - + for mod in self.selection: pos = fit.modules.index(mod) sFit.changeModule(fitID, pos, item.ID) diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 649b7bc27..e69aac4cd 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -52,8 +52,10 @@ class ModuleAmmoPicker(ContextMenu): def turretSorter(self, charge): damage = 0 - range_ = (self.module.getModifiedItemAttr("maxRange") or 0) * (charge.getAttribute("weaponRangeMultiplier") or 1) - falloff = (self.module.getModifiedItemAttr("falloff") or 0) * (charge.getAttribute("fallofMultiplier") or 1) + range_ = (self.module.getModifiedItemAttr("maxRange") or 0) * \ + (charge.getAttribute("weaponRangeMultiplier") or 1) + falloff = (self.module.getModifiedItemAttr("falloff") or 0) * \ + (charge.getAttribute("fallofMultiplier") or 1) for type_ in self.DAMAGE_TYPES: d = charge.getAttribute("%sDamage" % type_) if d > 0: diff --git a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py index 29eedf521..035109320 100644 --- a/gui/builtinContextMenus/moduleGlobalAmmoPicker.py +++ b/gui/builtinContextMenus/moduleGlobalAmmoPicker.py @@ -1,12 +1,11 @@ # -*- coding: utf-8 -*- -from gui.contextMenu import ContextMenu import gui.mainFrame import wx -from gui.bitmapLoader import BitmapLoader -from eos.types import Hardpoint import gui.globalEvents as GE from gui.builtinContextMenus.moduleAmmoPicker import ModuleAmmoPicker import eos.db +from service.fit import Fit + class ModuleGlobalAmmoPicker(ModuleAmmoPicker): def __init__(self): diff --git a/gui/builtinContextMenus/openFit.py b/gui/builtinContextMenus/openFit.py index 621885f67..fad0c2640 100644 --- a/gui/builtinContextMenus/openFit.py +++ b/gui/builtinContextMenus/openFit.py @@ -3,6 +3,7 @@ import gui.mainFrame import wx from gui.shipBrowser import FitSelected + class OpenFit(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() diff --git a/gui/builtinContextMenus/priceClear.py b/gui/builtinContextMenus/priceClear.py index be7986536..e61b7df7f 100644 --- a/gui/builtinContextMenus/priceClear.py +++ b/gui/builtinContextMenus/priceClear.py @@ -4,6 +4,7 @@ import wx import gui.globalEvents as GE from service.market import Market + class PriceClear(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() diff --git a/gui/builtinContextMenus/shipJump.py b/gui/builtinContextMenus/shipJump.py index 478619cf7..b743201a3 100644 --- a/gui/builtinContextMenus/shipJump.py +++ b/gui/builtinContextMenus/shipJump.py @@ -4,6 +4,7 @@ import gui.mainFrame from gui.shipBrowser import Stage3Selected from service.fit import Fit + class ShipJump(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -21,7 +22,7 @@ class ShipJump(ContextMenu): groupID = stuff.item.group.ID self.mainFrame.notebookBrowsers.SetSelection(1) - wx.PostEvent(self.mainFrame.shipBrowser,Stage3Selected(shipID=stuff.item.ID, back=groupID)) + wx.PostEvent(self.mainFrame.shipBrowser, Stage3Selected(shipID=stuff.item.ID, back=groupID)) ShipJump.register() diff --git a/gui/builtinContextMenus/tacticalMode.py b/gui/builtinContextMenus/tacticalMode.py index 3909ef693..62abf309d 100644 --- a/gui/builtinContextMenus/tacticalMode.py +++ b/gui/builtinContextMenus/tacticalMode.py @@ -5,6 +5,7 @@ import gui.mainFrame import gui.globalEvents as GE from service.fit import Fit + class TacticalMode(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index 7f3ba2131..823aa0d82 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -11,6 +11,7 @@ try: except ImportError: from gui.utils.compat import OrderedDict + class TargetResists(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -65,15 +66,15 @@ class TargetResists(ContextMenu): msw = True if "wxMSW" in wx.PlatformInfo else False self.patternIds = {} self.subMenus = OrderedDict() - self.singles = [] + self.singles = [] sub = wx.Menu() for pattern in self.patterns: start, end = pattern.name.find('['), pattern.name.find(']') if start is not -1 and end is not -1: - currBase = pattern.name[start+1:end] + currBase = pattern.name[start + 1:end] # set helper attr - setattr(pattern, "_name", pattern.name[end+1:].strip()) + setattr(pattern, "_name", pattern.name[end + 1:].strip()) if currBase not in self.subMenus: self.subMenus[currBase] = [] self.subMenus[currBase].append(pattern) @@ -94,7 +95,7 @@ class TargetResists(ContextMenu): # Create menu for child items grandSub = wx.Menu() - #sub.Bind(wx.EVT_MENU, self.handleResistSwitch) + # sub.Bind(wx.EVT_MENU, self.handleResistSwitch) # Apply child menu to parent item item.SetSubMenu(grandSub) @@ -102,7 +103,7 @@ class TargetResists(ContextMenu): # Append child items to child menu for pattern in patterns: grandSub.AppendItem(self.addPattern(rootMenu if msw else grandSub, pattern)) - sub.AppendItem(item) #finally, append parent item to root menu + sub.AppendItem(item) # finally, append parent item to root menu return sub diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py index 96fe2c767..0e3bfed93 100644 --- a/gui/builtinContextMenus/whProjector.py +++ b/gui/builtinContextMenus/whProjector.py @@ -5,6 +5,7 @@ import wx from service.market import Market from service.fit import Fit + class WhProjector(ContextMenu): def __init__(self): self.mainFrame = gui.mainFrame.MainFrame.getInstance() @@ -42,7 +43,7 @@ class WhProjector(ContextMenu): return sub def handleSelection(self, event): - #Skip events ids that aren't mapped + # Skip events ids that aren't mapped swObj, swName = self.idmap.get(event.Id, (False, False)) if not swObj and not swName: diff --git a/gui/builtinPreferenceViews/__init__.py b/gui/builtinPreferenceViews/__init__.py index 48e631eb1..eb7f3c970 100644 --- a/gui/builtinPreferenceViews/__init__.py +++ b/gui/builtinPreferenceViews/__init__.py @@ -1,6 +1,7 @@ import wx -__all__ = ["pyfaGeneralPreferences", "pyfaHTMLExportPreferences", "pyfaUpdatePreferences", "pyfaNetworkPreferences"] # noqa +__all__ = ["pyfaGeneralPreferences", "pyfaHTMLExportPreferences", "pyfaUpdatePreferences", + "pyfaNetworkPreferences"] # noqa if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): __all__.append("pyfaCrestPreferences") diff --git a/gui/builtinPreferenceViews/dummyView.py b/gui/builtinPreferenceViews/dummyView.py index c6c93f582..fb46db87e 100644 --- a/gui/builtinPreferenceViews/dummyView.py +++ b/gui/builtinPreferenceViews/dummyView.py @@ -1,95 +1,94 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -import wx -from gui.preferenceView import PreferenceView - - -class DummyView(PreferenceView): - title = "Dummy" - - def populatePanel(self, panel): - - mainSizer = wx.BoxSizer(wx.VERTICAL) - - headerSizer = self.initHeader(panel) - mainSizer.Add(headerSizer, 0, wx.EXPAND, 5) - - self.stline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - mainSizer.Add(self.stline1, 0, wx.EXPAND, 5) - - contentSizer = self.initContent(panel) - mainSizer.Add(contentSizer, 1, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.LEFT, 10) - - self.stline2 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - mainSizer.Add(self.stline2, 0, wx.EXPAND, 5) - - footerSizer = self.initFooter(panel) - mainSizer.Add(footerSizer, 0, wx.EXPAND, 5) - panel.SetSizer(mainSizer) - panel.Layout() - - def refreshPanel(self, fit): - pass - - def initHeader(self, panel): - headerSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText(panel, wx.ID_ANY, u"Dummy", wx.DefaultPosition, wx.DefaultSize, 0) - self.stTitle.Wrap(-1) - self.stTitle.SetFont(wx.Font(14, 70, 90, 90, False, wx.EmptyString)) - headerSizer.Add(self.stTitle, 0, wx.ALL, 5) - - return headerSizer - - def initContent(self, panel): - contentSizer = wx.BoxSizer(wx.VERTICAL) - - self.m_checkBox2 = wx.CheckBox(panel, wx.ID_ANY, u"Check Me!", wx.DefaultPosition, wx.DefaultSize, 0) - contentSizer.Add(self.m_checkBox2, 0, wx.ALL, 5) - - self.m_radioBtn2 = wx.RadioButton(panel, wx.ID_ANY, u"RadioBtn", wx.DefaultPosition, wx.DefaultSize, 0) - contentSizer.Add(self.m_radioBtn2, 0, wx.ALL, 5) - - self.m_slider2 = wx.Slider(panel, wx.ID_ANY, 50, 0, 100, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL) - contentSizer.Add(self.m_slider2, 0, wx.ALL, 5) - - self.m_gauge1 = wx.Gauge(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) - contentSizer.Add(self.m_gauge1, 0, wx.ALL, 5) - - self.m_textCtrl2 = wx.TextCtrl(panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) - contentSizer.Add(self.m_textCtrl2, 0, wx.ALL, 5) - - return contentSizer - - def initFooter(self, panel): - footerSizer = wx.BoxSizer(wx.HORIZONTAL) - - footerSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - - self.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore", wx.DefaultPosition, wx.DefaultSize, 0) - self.btnRestore.Enable(False) - - footerSizer.Add(self.btnRestore, 0, wx.ALL, 5) - - self.btnApply = wx.Button(panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0) - footerSizer.Add(self.btnApply, 0, wx.ALL, 5) - return footerSizer - - -DummyView.register() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +import wx +from gui.preferenceView import PreferenceView + + +class DummyView(PreferenceView): + title = "Dummy" + + def populatePanel(self, panel): + mainSizer = wx.BoxSizer(wx.VERTICAL) + + headerSizer = self.initHeader(panel) + mainSizer.Add(headerSizer, 0, wx.EXPAND, 5) + + self.stline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.stline1, 0, wx.EXPAND, 5) + + contentSizer = self.initContent(panel) + mainSizer.Add(contentSizer, 1, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.LEFT, 10) + + self.stline2 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.stline2, 0, wx.EXPAND, 5) + + footerSizer = self.initFooter(panel) + mainSizer.Add(footerSizer, 0, wx.EXPAND, 5) + panel.SetSizer(mainSizer) + panel.Layout() + + def refreshPanel(self, fit): + pass + + def initHeader(self, panel): + headerSizer = wx.BoxSizer(wx.VERTICAL) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, u"Dummy", wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(14, 70, 90, 90, False, wx.EmptyString)) + headerSizer.Add(self.stTitle, 0, wx.ALL, 5) + + return headerSizer + + def initContent(self, panel): + contentSizer = wx.BoxSizer(wx.VERTICAL) + + self.m_checkBox2 = wx.CheckBox(panel, wx.ID_ANY, u"Check Me!", wx.DefaultPosition, wx.DefaultSize, 0) + contentSizer.Add(self.m_checkBox2, 0, wx.ALL, 5) + + self.m_radioBtn2 = wx.RadioButton(panel, wx.ID_ANY, u"RadioBtn", wx.DefaultPosition, wx.DefaultSize, 0) + contentSizer.Add(self.m_radioBtn2, 0, wx.ALL, 5) + + self.m_slider2 = wx.Slider(panel, wx.ID_ANY, 50, 0, 100, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL) + contentSizer.Add(self.m_slider2, 0, wx.ALL, 5) + + self.m_gauge1 = wx.Gauge(panel, wx.ID_ANY, 100, wx.DefaultPosition, wx.DefaultSize, wx.GA_HORIZONTAL) + contentSizer.Add(self.m_gauge1, 0, wx.ALL, 5) + + self.m_textCtrl2 = wx.TextCtrl(panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) + contentSizer.Add(self.m_textCtrl2, 0, wx.ALL, 5) + + return contentSizer + + def initFooter(self, panel): + footerSizer = wx.BoxSizer(wx.HORIZONTAL) + + footerSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + + self.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnRestore.Enable(False) + + footerSizer.Add(self.btnRestore, 0, wx.ALL, 5) + + self.btnApply = wx.Button(panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0) + footerSizer.Add(self.btnApply, 0, wx.ALL, 5) + return footerSizer + + +DummyView.register() diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py index 964596eb6..1891f2216 100644 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ b/gui/builtinPreferenceViews/pyfaCrestPreferences.py @@ -30,13 +30,17 @@ class PFCrestPref(PreferenceView): self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.stInfo = wx.StaticText(panel, wx.ID_ANY, u"Please see the pyfa wiki on GitHub for information regarding these options.", wx.DefaultPosition, wx.DefaultSize, 0) + self.stInfo = wx.StaticText(panel, wx.ID_ANY, + u"Please see the pyfa wiki on GitHub for information regarding these options.", + wx.DefaultPosition, wx.DefaultSize, 0) self.stInfo.Wrap(dlgWidth - 50) mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) rbSizer = wx.BoxSizer(wx.HORIZONTAL) - self.rbMode = wx.RadioBox(panel, -1, "Mode", wx.DefaultPosition, wx.DefaultSize, ['Implicit', 'User-supplied details'], 1, wx.RA_SPECIFY_COLS) - self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize, ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS) + self.rbMode = wx.RadioBox(panel, -1, "Mode", wx.DefaultPosition, wx.DefaultSize, + ['Implicit', 'User-supplied details'], 1, wx.RA_SPECIFY_COLS) + self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize, + ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS) self.rbMode.SetSelection(self.settings.get('mode')) self.rbServer.SetSelection(self.settings.get('server')) @@ -67,7 +71,8 @@ class PFCrestPref(PreferenceView): detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) mainSizer.Add(detailsTitle, 0, wx.ALL, 5) - mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 5) + mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND, 5) fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) fgAddrSizer.AddGrowableCol(1) @@ -78,7 +83,8 @@ class PFCrestPref(PreferenceView): self.stSetID.Wrap(-1) fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, wx.DefaultSize, 0) + self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, + wx.DefaultSize, 0) fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) @@ -87,7 +93,8 @@ class PFCrestPref(PreferenceView): fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, wx.DefaultSize, 0) + self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, + wx.DefaultSize, 0) fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index c20f0d878..88c1e1313 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -10,6 +10,7 @@ from gui.bitmapLoader import BitmapLoader from gui.utils import colorUtils import gui.utils.drawUtils as drawUtils + ########################################################################### # Class PFGaugePref ########################################################################### @@ -162,10 +163,12 @@ class PFGaugePref(PreferenceView): self.st0100.Wrap(-1) gSizer1.Add(self.st0100, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp0100S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + self.cp0100S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) gSizer1.Add(self.cp0100S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp0100E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + self.cp0100E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) gSizer1.Add(self.cp0100E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.gauge0100S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) @@ -181,14 +184,17 @@ class PFGaugePref(PreferenceView): gSizer2 = wx.BoxSizer(wx.HORIZONTAL) - self.st100101 = wx.StaticText(panel, wx.ID_ANY, u"100 - 101", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) + self.st100101 = wx.StaticText(panel, wx.ID_ANY, u"100 - 101", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) self.st100101.Wrap(-1) gSizer2.Add(self.st100101, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp100101S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + self.cp100101S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) gSizer2.Add(self.cp100101S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp100101E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + self.cp100101E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) gSizer2.Add(self.cp100101E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.gauge100101S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) @@ -204,14 +210,17 @@ class PFGaugePref(PreferenceView): gSizer3 = wx.BoxSizer(wx.HORIZONTAL) - self.st101103 = wx.StaticText(panel, wx.ID_ANY, u"101 - 103", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) + self.st101103 = wx.StaticText(panel, wx.ID_ANY, u"101 - 103", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) self.st101103.Wrap(-1) gSizer3.Add(self.st101103, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp101103S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + self.cp101103S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) gSizer3.Add(self.cp101103S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp101103E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + self.cp101103E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) gSizer3.Add(self.cp101103E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.gauge101103S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) @@ -227,14 +236,17 @@ class PFGaugePref(PreferenceView): gSizer4 = wx.BoxSizer(wx.HORIZONTAL) - self.st103105 = wx.StaticText(panel, wx.ID_ANY, u"103 - 105", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) + self.st103105 = wx.StaticText(panel, wx.ID_ANY, u"103 - 105", wx.DefaultPosition, wx.DefaultSize, + wx.ALIGN_RIGHT) self.st103105.Wrap(-1) gSizer4.Add(self.st103105, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp103105S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + self.cp103105S = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) gSizer4.Add(self.cp103105S, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.cp103105E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) + self.cp103105E = wx.ColourPickerCtrl(panel, wx.ID_ANY, wx.BLACK, wx.DefaultPosition, wx.DefaultSize, + wx.CLRP_DEFAULT_STYLE | wx.CLRP_SHOW_LABEL) gSizer4.Add(self.cp103105E, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.gauge103105S = PFGaugePreview(panel, wx.ID_ANY, 33, wx.DefaultPosition, wx.DefaultSize, wx.SIMPLE_BORDER) @@ -274,7 +286,8 @@ class PFGaugePref(PreferenceView): self.cbLink = wx.CheckBox(panel, wx.ID_ANY, u"Link Colors", wx.DefaultPosition, wx.DefaultSize, 0) buttonsSizer.Add(self.cbLink, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) - self.sliderGradientStart = wx.Slider(panel, wx.ID_ANY, self.gradientStart, -100, 100, wx.DefaultPosition, (127, -1), wx.SL_HORIZONTAL | wx.SL_LABELS) + self.sliderGradientStart = wx.Slider(panel, wx.ID_ANY, self.gradientStart, -100, 100, wx.DefaultPosition, + (127, -1), wx.SL_HORIZONTAL | wx.SL_LABELS) buttonsSizer.Add(self.sliderGradientStart, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore Defaults", wx.DefaultPosition, wx.DefaultSize, 0) diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index 35ea9cb1d..0e5a18f93 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -15,7 +15,8 @@ class PFGeneralPref(PreferenceView): def populatePanel(self, panel): self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.dirtySettings = False - self.openFitsSettings = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) + self.openFitsSettings = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", + {"enabled": False, "pyfaOpenFits": []}) mainSizer = wx.BoxSizer(wx.VERTICAL) @@ -28,22 +29,28 @@ class PFGeneralPref(PreferenceView): self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, u"Use global character", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, u"Use global character", wx.DefaultPosition, wx.DefaultSize, + 0) mainSizer.Add(self.cbGlobalChar, 0, wx.ALL | wx.EXPAND, 5) - self.cbGlobalDmgPattern = wx.CheckBox(panel, wx.ID_ANY, u"Use global damage pattern", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbGlobalDmgPattern = wx.CheckBox(panel, wx.ID_ANY, u"Use global damage pattern", wx.DefaultPosition, + wx.DefaultSize, 0) mainSizer.Add(self.cbGlobalDmgPattern, 0, wx.ALL | wx.EXPAND, 5) - self.cbGlobalForceReload = wx.CheckBox(panel, wx.ID_ANY, u"Factor in reload time", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbGlobalForceReload = wx.CheckBox(panel, wx.ID_ANY, u"Factor in reload time", wx.DefaultPosition, + wx.DefaultSize, 0) mainSizer.Add(self.cbGlobalForceReload, 0, wx.ALL | wx.EXPAND, 5) - self.cbCompactSkills = wx.CheckBox(panel, wx.ID_ANY, u"Compact skills needed tooltip", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbCompactSkills = wx.CheckBox(panel, wx.ID_ANY, u"Compact skills needed tooltip", wx.DefaultPosition, + wx.DefaultSize, 0) mainSizer.Add(self.cbCompactSkills, 0, wx.ALL | wx.EXPAND, 5) - self.cbFitColorSlots = wx.CheckBox(panel, wx.ID_ANY, u"Color fitting view by slot", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbFitColorSlots = wx.CheckBox(panel, wx.ID_ANY, u"Color fitting view by slot", wx.DefaultPosition, + wx.DefaultSize, 0) mainSizer.Add(self.cbFitColorSlots, 0, wx.ALL | wx.EXPAND, 5) - self.cbReopenFits = wx.CheckBox(panel, wx.ID_ANY, u"Reopen previous fits on startup", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbReopenFits = wx.CheckBox(panel, wx.ID_ANY, u"Reopen previous fits on startup", wx.DefaultPosition, + wx.DefaultSize, 0) mainSizer.Add(self.cbReopenFits, 0, wx.ALL | wx.EXPAND, 5) self.cbRackSlots = wx.CheckBox(panel, wx.ID_ANY, u"Separate Racks", wx.DefaultPosition, wx.DefaultSize, 0) @@ -57,16 +64,19 @@ class PFGeneralPref(PreferenceView): self.cbShowTooltip = wx.CheckBox(panel, wx.ID_ANY, u"Show tab tooltips", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbShowTooltip, 0, wx.ALL | wx.EXPAND, 5) - self.cbMarketShortcuts = wx.CheckBox(panel, wx.ID_ANY, u"Show market shortcuts", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbMarketShortcuts = wx.CheckBox(panel, wx.ID_ANY, u"Show market shortcuts", wx.DefaultPosition, + wx.DefaultSize, 0) mainSizer.Add(self.cbMarketShortcuts, 0, wx.ALL | wx.EXPAND, 5) self.cbGaugeAnimation = wx.CheckBox(panel, wx.ID_ANY, u"Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbGaugeAnimation, 0, wx.ALL | wx.EXPAND, 5) - self.cbExportCharges = wx.CheckBox(panel, wx.ID_ANY, u"Export loaded charges", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbExportCharges = wx.CheckBox(panel, wx.ID_ANY, u"Export loaded charges", wx.DefaultPosition, + wx.DefaultSize, 0) mainSizer.Add(self.cbExportCharges, 0, wx.ALL | wx.EXPAND, 5) - self.cbOpenFitInNew = wx.CheckBox(panel, wx.ID_ANY, u"Open fittings in a new page by default", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbOpenFitInNew = wx.CheckBox(panel, wx.ID_ANY, u"Open fittings in a new page by default", + wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbOpenFitInNew, 0, wx.ALL | wx.EXPAND, 5) wx.BoxSizer(wx.HORIZONTAL) diff --git a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py index 588c9eead..c6428d8b7 100644 --- a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py +++ b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py @@ -39,10 +39,14 @@ class PFHTMLExportPref(PreferenceView): self.stDesc.Wrap(dlgWidth - 50) mainSizer.Add(self.stDesc, 0, wx.ALL, 5) - self.PathLinkCtrl = wx.HyperlinkCtrl(panel, wx.ID_ANY, self.HTMLExportSettings.getPath(), u'file:///{}'.format(self.HTMLExportSettings.getPath()), wx.DefaultPosition, wx.DefaultSize, wx.HL_ALIGN_LEFT | wx.NO_BORDER | wx.HL_CONTEXTMENU) + self.PathLinkCtrl = wx.HyperlinkCtrl(panel, wx.ID_ANY, self.HTMLExportSettings.getPath(), + u'file:///{}'.format(self.HTMLExportSettings.getPath()), + wx.DefaultPosition, wx.DefaultSize, + wx.HL_ALIGN_LEFT | wx.NO_BORDER | wx.HL_CONTEXTMENU) mainSizer.Add(self.PathLinkCtrl, 0, wx.ALL | wx.EXPAND, 5) - self.fileSelectDialog = wx.FileDialog(None, "Save Fitting As...", wildcard="EVE IGB HTML fitting file (*.html)|*.html", style=wx.FD_SAVE) + self.fileSelectDialog = wx.FileDialog(None, "Save Fitting As...", + wildcard="EVE IGB HTML fitting file (*.html)|*.html", style=wx.FD_SAVE) self.fileSelectDialog.SetPath(self.HTMLExportSettings.getPath()) self.fileSelectDialog.SetFilename(os.path.basename(self.HTMLExportSettings.getPath())) @@ -54,7 +58,8 @@ class PFHTMLExportPref(PreferenceView): self.stDesc2.Wrap(dlgWidth - 50) mainSizer.Add(self.stDesc2, 0, wx.ALL, 5) - self.exportEnabled = wx.CheckBox(panel, wx.ID_ANY, u"Enable automatic HTML export", wx.DefaultPosition, wx.DefaultSize, 0) + self.exportEnabled = wx.CheckBox(panel, wx.ID_ANY, u"Enable automatic HTML export", wx.DefaultPosition, + wx.DefaultSize, 0) self.exportEnabled.SetValue(self.HTMLExportSettings.getEnabled()) self.exportEnabled.Bind(wx.EVT_CHECKBOX, self.OnExportEnabledChange) mainSizer.Add(self.exportEnabled, 0, wx.ALL | wx.EXPAND, 5) @@ -63,7 +68,8 @@ class PFHTMLExportPref(PreferenceView): self.stDesc4.Wrap(dlgWidth - 50) mainSizer.Add(self.stDesc4, 0, wx.ALL, 5) - self.exportMinimal = wx.CheckBox(panel, wx.ID_ANY, u"Enable minimal export Format", wx.DefaultPosition, wx.DefaultSize, 0) + self.exportMinimal = wx.CheckBox(panel, wx.ID_ANY, u"Enable minimal export Format", wx.DefaultPosition, + wx.DefaultSize, 0) self.exportMinimal.SetValue(self.HTMLExportSettings.getMinimalEnabled()) self.exportMinimal.Bind(wx.EVT_CHECKBOX, self.OnMinimalEnabledChange) mainSizer.Add(self.exportMinimal, 0, wx.ALL | wx.EXPAND, 5) diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index c8da61bc3..8c93b1a5f 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -33,7 +33,8 @@ class PFNetworkPref(PreferenceView): mainSizer.Add(self.cbEnableNetwork, 0, wx.ALL | wx.EXPAND, 5) subSizer = wx.BoxSizer(wx.VERTICAL) - self.cbEve = wx.CheckBox(panel, wx.ID_ANY, u"EVE Servers (API && CREST import)", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbEve = wx.CheckBox(panel, wx.ID_ANY, u"EVE Servers (API && CREST import)", wx.DefaultPosition, + wx.DefaultSize, 0) subSizer.Add(self.cbEve, 0, wx.ALL | wx.EXPAND, 5) self.cbPricing = wx.CheckBox(panel, wx.ID_ANY, u"Pricing updates", wx.DefaultPosition, wx.DefaultSize, 0) @@ -49,7 +50,8 @@ class PFNetworkPref(PreferenceView): proxyTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) mainSizer.Add(proxyTitle, 0, wx.ALL, 5) - mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 5) + mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND, 5) self.cbEnableNetwork.SetValue(self.settings.isEnabled(self.network.ENABLED)) self.cbEve.SetValue(self.settings.isEnabled(self.network.EVE)) @@ -117,7 +119,8 @@ class PFNetworkPref(PreferenceView): # proxy auth information: login and pass self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, u"Username:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetLogin.Wrap(-1) - self.editProxySettingsLogin = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[0], wx.DefaultPosition, wx.DefaultSize, 0) + self.editProxySettingsLogin = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[0], wx.DefaultPosition, wx.DefaultSize, + 0) self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, u"Password:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetPassword.Wrap(-1) self.editProxySettingsPassword = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[1], wx.DefaultPosition, @@ -129,7 +132,8 @@ class PFNetworkPref(PreferenceView): pAuthSizer.Add(self.editProxySettingsPassword, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) mainSizer.Add(pAuthSizer, 0, wx.EXPAND, 5) - self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, u"Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, u"Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, + 0) self.stPSAutoDetected.Wrap(-1) mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) diff --git a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py index 88022be61..48e2a79cc 100644 --- a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py +++ b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py @@ -32,7 +32,8 @@ class PFUpdatePref(PreferenceView): self.stDesc.Wrap(dlgWidth - 50) mainSizer.Add(self.stDesc, 0, wx.ALL, 5) - self.suppressPrerelease = wx.CheckBox(panel, wx.ID_ANY, u"Allow pre-release notifications", wx.DefaultPosition, wx.DefaultSize, 0) + self.suppressPrerelease = wx.CheckBox(panel, wx.ID_ANY, u"Allow pre-release notifications", wx.DefaultPosition, + wx.DefaultSize, 0) self.suppressPrerelease.Bind(wx.EVT_CHECKBOX, self.OnPrereleaseStateChange) self.suppressPrerelease.SetValue(not self.UpdateSettings.get('prerelease')) @@ -41,7 +42,8 @@ class PFUpdatePref(PreferenceView): if (self.UpdateSettings.get('version')): self.versionSizer = wx.BoxSizer(wx.VERTICAL) - self.versionTitle = wx.StaticText(panel, wx.ID_ANY, "Suppressing {0} Notifications".format(self.UpdateSettings.get('version')), wx.DefaultPosition, wx.DefaultSize, 0) + self.versionTitle = wx.StaticText(panel, wx.ID_ANY, "Suppressing {0} Notifications".format( + self.UpdateSettings.get('version')), wx.DefaultPosition, wx.DefaultSize, 0) self.versionTitle.Wrap(-1) self.versionTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) @@ -51,7 +53,8 @@ class PFUpdatePref(PreferenceView): self.versionSizer.AddSpacer((5, 5), 0, wx.EXPAND, 5) - self.versionSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 5) + self.versionSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), + 0, wx.EXPAND, 5) self.versionSizer.AddSpacer((5, 5), 0, wx.EXPAND, 5) self.versionSizer.Add(self.versionTitle, 0, wx.EXPAND, 5) diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 39f7bdbd1..9c31fefe2 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -22,9 +22,12 @@ import gui.mainFrame from gui.statsView import StatsView from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount +from service.fit import Fit + class FirepowerViewFull(StatsView): name = "firepowerViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent @@ -34,7 +37,7 @@ class FirepowerViewFull(StatsView): return "Firepower" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -83,7 +86,7 @@ class FirepowerViewFull(StatsView): baseBox.Add(BitmapLoader.getStaticBitmap("volley_big", parent, "gui"), 0, wx.ALIGN_CENTER) - gridS = wx.GridSizer(2,2,0,0) + gridS = wx.GridSizer(2, 2, 0, 0) baseBox.Add(gridS, 0) @@ -138,20 +141,20 @@ class FirepowerViewFull(StatsView): view.refreshPanel(fit) def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here if fit is not None and fit.targetResists is not None: self.stEff.Show() else: self.stEff.Hide() - stats = (("labelFullDpsWeapon", lambda: fit.weaponDPS, 3, 0, 0, "%s DPS",None), + stats = (("labelFullDpsWeapon", lambda: fit.weaponDPS, 3, 0, 0, "%s DPS", None), ("labelFullDpsDrone", lambda: fit.droneDPS, 3, 0, 0, "%s DPS", None), ("labelFullVolleyTotal", lambda: fit.totalVolley, 3, 0, 0, "%s", "Volley: %.1f"), ("labelFullDpsTotal", lambda: fit.totalDPS, 3, 0, 0, "%s", None)) # See GH issue # - #if fit is not None and fit.totalYield > 0: + # if fit is not None and fit.totalYield > 0: # self.miningyield.Show() - #else: + # else: # self.miningyield.Hide() counter = 0 @@ -165,7 +168,7 @@ class FirepowerViewFull(StatsView): tipStr = valueFormat % valueStr if altFormat is None else altFormat % value label.SetToolTip(wx.ToolTip(tipStr)) self._cachedValues[counter] = value - counter +=1 + counter += 1 self.panel.Layout() self.headerPanel.Layout() diff --git a/gui/builtinStatsViews/miningyieldViewFull.py b/gui/builtinStatsViews/miningyieldViewFull.py index 5a3a10212..f17e30ecf 100644 --- a/gui/builtinStatsViews/miningyieldViewFull.py +++ b/gui/builtinStatsViews/miningyieldViewFull.py @@ -22,18 +22,22 @@ import gui.mainFrame from gui.statsView import StatsView from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount +from service.fit import Fit + class MiningYieldViewFull(StatsView): name = "miningyieldViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent self._cachedValues = [] + def getHeaderText(self, fit): return "Mining Yield" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -46,11 +50,11 @@ class MiningYieldViewFull(StatsView): sizerMiningYield = wx.FlexGridSizer(1, 4) sizerMiningYield.AddGrowableCol(1) - contentSizer.Add( sizerMiningYield, 0, wx.EXPAND, 0) + contentSizer.Add(sizerMiningYield, 0, wx.EXPAND, 0) counter = 0 - for miningType, image in (("miner", "mining") , ("drone", "drones")): + for miningType, image in (("miner", "mining"), ("drone", "drones")): baseBox = wx.BoxSizer(wx.HORIZONTAL) sizerMiningYield.Add(baseBox, 1, wx.ALIGN_LEFT if counter == 0 else wx.ALIGN_CENTER_HORIZONTAL) @@ -65,7 +69,7 @@ class MiningYieldViewFull(StatsView): box.Add(hbox, 1, wx.ALIGN_CENTER) lbl = wx.StaticText(parent, wx.ID_ANY, u"0.0 m\u00B3/s") - setattr(self, "label%sminingyield%s" % (panel.capitalize() ,miningType.capitalize()), lbl) + setattr(self, "label%sminingyield%s" % (panel.capitalize(), miningType.capitalize()), lbl) hbox.Add(lbl, 0, wx.ALIGN_CENTER) self._cachedValues.append(0) @@ -121,9 +125,9 @@ class MiningYieldViewFull(StatsView): view.refreshPanel(fit) def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats = (("labelFullminingyieldMiner", lambda: fit.minerYield, 3, 0, 0, u"%s m\u00B3/s",None), + stats = (("labelFullminingyieldMiner", lambda: fit.minerYield, 3, 0, 0, u"%s m\u00B3/s", None), ("labelFullminingyieldDrone", lambda: fit.droneYield, 3, 0, 0, u"%s m\u00B3/s", None), ("labelFullminingyieldTotal", lambda: fit.totalYield, 3, 0, 0, u"%s m\u00B3/s", None)) @@ -138,7 +142,7 @@ class MiningYieldViewFull(StatsView): tipStr = valueFormat % valueStr if altFormat is None else altFormat % value label.SetToolTip(wx.ToolTip(tipStr)) self._cachedValues[counter] = value - counter +=1 + counter += 1 self.panel.Layout() self.headerPanel.Layout() diff --git a/gui/builtinStatsViews/rechargeViewFull.py b/gui/builtinStatsViews/rechargeViewFull.py index b8055de97..a285c04cb 100644 --- a/gui/builtinStatsViews/rechargeViewFull.py +++ b/gui/builtinStatsViews/rechargeViewFull.py @@ -62,7 +62,8 @@ class RechargeViewFull(StatsView): # Add an empty label first for correct alignment. sizerTankStats.Add(wx.StaticText(contentPanel, wx.ID_ANY, ""), 0) - toolTipText = {"shieldPassive": "Passive shield recharge", "shieldActive": "Active shield boost", "armorActive": "Armor repair amount", "hullActive": "Hull repair amount"} + toolTipText = {"shieldPassive": "Passive shield recharge", "shieldActive": "Active shield boost", + "armorActive": "Armor repair amount", "hullActive": "Hull repair amount"} for tankType in ("shieldPassive", "shieldActive", "armorActive", "hullActive"): bitmap = BitmapLoader.getStaticBitmap("%s_big" % tankType, contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[tankType]) diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index 3f56c3727..16f8cb5a5 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -19,18 +19,18 @@ import wx from gui.statsView import StatsView -from gui import builtinStatsViews from gui.bitmapLoader import BitmapLoader from gui import pygauge as PG from gui.utils.numberFormatter import formatAmount import gui.mainFrame -import gui.builtinViews.fittingView as fv import gui.globalEvents as GE EffectiveHpToggled, EFFECTIVE_HP_TOGGLED = wx.lib.newevent.NewEvent() + class ResistancesViewFull(StatsView): name = "resistancesViewFull" + def __init__(self, parent): StatsView.__init__(self) self.parent = parent @@ -44,7 +44,7 @@ class ResistancesViewFull(StatsView): return "Resistances" def getTextExtentW(self, text): - width, height = self.parent.GetTextExtent( text ) + width, height = self.parent.GetTextExtent(text) return width def populatePanel(self, contentPanel, headerPanel): @@ -55,7 +55,7 @@ class ResistancesViewFull(StatsView): # Custom header EHP headerContentSizer = wx.BoxSizer(wx.HORIZONTAL) hsizer = headerPanel.GetSizer() - hsizer.Add(headerContentSizer,0,0,0) + hsizer.Add(headerContentSizer, 0, 0, 0) self.stEff = wx.StaticText(headerPanel, wx.ID_ANY, "( Effective HP: ") headerContentSizer.Add(self.stEff) headerPanel.GetParent().AddToggleItem(self.stEff) @@ -66,81 +66,83 @@ class ResistancesViewFull(StatsView): stCls = wx.StaticText(headerPanel, wx.ID_ANY, " )") - headerPanel.GetParent().AddToggleItem( stCls ) - headerContentSizer.Add( stCls ) -# headerContentSizer.Add(wx.StaticLine(headerPanel, wx.ID_ANY), 1, wx.ALIGN_CENTER) + headerPanel.GetParent().AddToggleItem(stCls) + headerContentSizer.Add(stCls) + # headerContentSizer.Add(wx.StaticLine(headerPanel, wx.ID_ANY), 1, wx.ALIGN_CENTER) # Display table col = 0 row = 0 sizerResistances = wx.GridBagSizer() - contentSizer.Add( sizerResistances, 0, wx.EXPAND , 0) + contentSizer.Add(sizerResistances, 0, wx.EXPAND, 0) # Add an empty label, then the rest. - sizerResistances.Add(wx.StaticText(contentPanel, wx.ID_ANY), wx.GBPosition( row, col ), wx.GBSpan( 1, 1 )) - col+=1 - toolTipText = {"em" : "Electromagnetic resistance", "thermal" : "Thermal resistance", "kinetic" : "Kinetic resistance", "explosive" : "Explosive resistance"} + sizerResistances.Add(wx.StaticText(contentPanel, wx.ID_ANY), wx.GBPosition(row, col), wx.GBSpan(1, 1)) + col += 1 + toolTipText = {"em": "Electromagnetic resistance", "thermal": "Thermal resistance", + "kinetic": "Kinetic resistance", "explosive": "Explosive resistance"} for damageType in ("em", "thermal", "kinetic", "explosive"): bitmap = BitmapLoader.getStaticBitmap("%s_big" % damageType, contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[damageType]) bitmap.SetToolTip(tooltip) - sizerResistances.Add(bitmap, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) - col+=1 - self.stEHPs = wx.Button(contentPanel, style = wx.BU_EXACTFIT, label = "EHP") + sizerResistances.Add(bitmap, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + col += 1 + self.stEHPs = wx.Button(contentPanel, style=wx.BU_EXACTFIT, label="EHP") self.stEHPs.SetToolTip(wx.ToolTip("Click to toggle between effective HP and raw HP")) self.stEHPs.Bind(wx.EVT_BUTTON, self.toggleEHP) for i in xrange(4): - sizerResistances.AddGrowableCol(i+1) + sizerResistances.AddGrowableCol(i + 1) - sizerResistances.Add(self.stEHPs, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) - col=0 - row+=1 + sizerResistances.Add(self.stEHPs, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + col = 0 + row += 1 - gaugeColours=( ((38,133,198),(52,86,98)), ((198,38,38),(83,65,67)), ((163,163,163),(74,90,93)), ((198,133,38),(81,83,67)) ) + gaugeColours = (((38, 133, 198), (52, 86, 98)), ((198, 38, 38), (83, 65, 67)), ((163, 163, 163), (74, 90, 93)), + ((198, 133, 38), (81, 83, 67))) - toolTipText = {"shield" : "Shield resistance", "armor" : "Armor resistance", "hull" : "Hull resistance", "damagePattern" : "Incoming damage pattern"} + toolTipText = {"shield": "Shield resistance", "armor": "Armor resistance", "hull": "Hull resistance", + "damagePattern": "Incoming damage pattern"} for tankType in ("shield", "armor", "hull", "separator", "damagePattern"): if tankType != "separator": bitmap = BitmapLoader.getStaticBitmap("%s_big" % tankType, contentPanel, "gui") tooltip = wx.ToolTip(toolTipText[tankType]) bitmap.SetToolTip(tooltip) - sizerResistances.Add(bitmap, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) - col+=1 + sizerResistances.Add(bitmap, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + col += 1 else: - sizerResistances.Add(wx.StaticLine(contentPanel, wx.ID_ANY), wx.GBPosition( row, col ), wx.GBSpan( 1, 6 ), wx.EXPAND|wx.ALIGN_CENTER) - row+=1 - col=0 + sizerResistances.Add(wx.StaticLine(contentPanel, wx.ID_ANY), wx.GBPosition(row, col), wx.GBSpan(1, 6), + wx.EXPAND | wx.ALIGN_CENTER) + row += 1 + col = 0 continue - currGColour=0 + currGColour = 0 for damageType in ("em", "thermal", "kinetic", "explosive"): - box = wx.BoxSizer(wx.HORIZONTAL) - sizerResistances.Add(box, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) + sizerResistances.Add(box, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + # Fancy gauges addon - #Fancy gauges addon - - pgColour= gaugeColours[currGColour] + pgColour = gaugeColours[currGColour] fc = pgColour[0] bc = pgColour[1] - currGColour+=1 + currGColour += 1 lbl = PG.PyGauge(contentPanel, wx.ID_ANY, 100) lbl.SetMinSize((48, 16)) - lbl.SetBackgroundColour(wx.Colour(bc[0],bc[1],bc[2])) - lbl.SetBarColour(wx.Colour(fc[0],fc[1],fc[2])) + lbl.SetBackgroundColour(wx.Colour(bc[0], bc[1], bc[2])) + lbl.SetBarColour(wx.Colour(fc[0], fc[1], fc[2])) lbl.SetBarGradient() lbl.SetFractionDigits(1) setattr(self, "gaugeResistance%s%s" % (tankType.capitalize(), damageType.capitalize()), lbl) box.Add(lbl, 0, wx.ALIGN_CENTER) - col+=1 + col += 1 box = wx.BoxSizer(wx.VERTICAL) box.SetMinSize(wx.Size(self.getTextExtentW("WWWWk"), -1)) @@ -148,9 +150,9 @@ class ResistancesViewFull(StatsView): box.Add(lbl, 0, wx.ALIGN_CENTER) setattr(self, "labelResistance%sEhp" % tankType.capitalize(), lbl) - sizerResistances.Add(box, wx.GBPosition( row, col ), wx.GBSpan( 1, 1 ), wx.ALIGN_CENTER) - row+=1 - col=0 + sizerResistances.Add(box, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) + row += 1 + col = 0 self.stEHPs.SetToolTip(wx.ToolTip("Click to toggle between effective HP and raw HP")) @@ -162,7 +164,7 @@ class ResistancesViewFull(StatsView): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) def refreshPanel(self, fit): - #If we did anything intresting, we'd update our labels to reflect the new fit's stats here + # If we did anything intresting, we'd update our labels to reflect the new fit's stats here if fit is None and not self.showEffective: self.showEffective = True wx.PostEvent(self.mainFrame, EffectiveHpToggled(effective=True)) @@ -193,11 +195,11 @@ class ResistancesViewFull(StatsView): total += ehp[tankType] rrFactor = fit.ehp[tankType] / fit.hp[tankType] lbl.SetLabel(formatAmount(ehp[tankType], 3, 0, 9)) - lbl.SetToolTip(wx.ToolTip("%s: %d\nResist Multiplier: x%.2f" % (tankType.capitalize(), ehp[tankType], rrFactor))) + lbl.SetToolTip( + wx.ToolTip("%s: %d\nResist Multiplier: x%.2f" % (tankType.capitalize(), ehp[tankType], rrFactor))) else: lbl.SetLabel("0") - self.labelEhp.SetLabel("%s" % formatAmount(total, 3, 0, 9)) if self.showEffective: self.stEff.SetLabel("( Effective HP: ") @@ -206,10 +208,9 @@ class ResistancesViewFull(StatsView): self.stEff.SetLabel("( Raw HP: ") self.labelEhp.SetToolTip(wx.ToolTip("Raw: %d HP" % total)) - - damagePattern = fit.damagePattern if fit is not None and self.showEffective else None + damagePattern = fit.damagePattern if fit is not None and self.showEffective else None total = sum((damagePattern.emAmount, damagePattern.thermalAmount, - damagePattern.kineticAmount, damagePattern.explosiveAmount)) if damagePattern is not None else 0 + damagePattern.kineticAmount, damagePattern.explosiveAmount)) if damagePattern is not None else 0 for damageType in ("em", "thermal", "kinetic", "explosive"): lbl = getattr(self, "gaugeResistanceDamagepattern%s" % damageType.capitalize()) @@ -224,4 +225,3 @@ class ResistancesViewFull(StatsView): ResistancesViewFull.register() - diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index d9c71ff65..39bb0b62a 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -102,7 +102,8 @@ class ResourcesViewFull(StatsView): base = sizerResources sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) # Turrets & launcher hardslots display - tooltipText = {"turret": "Turret hardpoints", "launcher": "Launcher hardpoints", "drones": "Drones active", "fighter": "Fighter squadrons active", "calibration": "Calibration"} + tooltipText = {"turret": "Turret hardpoints", "launcher": "Launcher hardpoints", "drones": "Drones active", + "fighter": "Fighter squadrons active", "calibration": "Calibration"} for type_ in ("turret", "launcher", "drones", "fighter", "calibration"): box = wx.BoxSizer(wx.HORIZONTAL) @@ -114,7 +115,8 @@ class ResourcesViewFull(StatsView): sizer.Add(box, 0, wx.ALIGN_CENTER) - suffix = {'turret': 'Hardpoints', 'launcher': 'Hardpoints', 'drones': 'Active', 'fighter': 'Tubes', 'calibration': 'Points'} + suffix = {'turret': 'Hardpoints', 'launcher': 'Hardpoints', 'drones': 'Active', 'fighter': 'Tubes', + 'calibration': 'Points'} lbl = wx.StaticText(parent, wx.ID_ANY, "0") setattr(self, "label%sUsed%s%s" % (panel.capitalize(), type_.capitalize(), suffix[type_].capitalize()), lbl) box.Add(lbl, 0, wx.ALIGN_CENTER | wx.LEFT, 5) @@ -122,7 +124,8 @@ class ResourcesViewFull(StatsView): box.Add(wx.StaticText(parent, wx.ID_ANY, "/"), 0, wx.ALIGN_CENTER) lbl = wx.StaticText(parent, wx.ID_ANY, "0") - setattr(self, "label%sTotal%s%s" % (panel.capitalize(), type_.capitalize(), suffix[type_].capitalize()), lbl) + setattr(self, "label%sTotal%s%s" % (panel.capitalize(), type_.capitalize(), suffix[type_].capitalize()), + lbl) box.Add(lbl, 0, wx.ALIGN_CENTER) setattr(self, "boxSizer{}".format(type_.capitalize()), box) @@ -132,7 +135,8 @@ class ResourcesViewFull(StatsView): sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) # PG, Cpu & drone stuff - tooltipText = {"cpu": "CPU", "pg": "PowerGrid", "droneBay": "Drone bay", "fighterBay": "Fighter bay", "droneBandwidth": "Drone bandwidth", "cargoBay": "Cargo bay"} + tooltipText = {"cpu": "CPU", "pg": "PowerGrid", "droneBay": "Drone bay", "fighterBay": "Fighter bay", + "droneBandwidth": "Drone bandwidth", "cargoBay": "Cargo bay"} for i, group in enumerate((("cpu", "pg"), ("cargoBay", "droneBay", "fighterBay", "droneBandwidth"))): main = wx.BoxSizer(wx.VERTICAL) base.Add(main, 1, wx.ALIGN_CENTER) @@ -164,7 +168,8 @@ class ResourcesViewFull(StatsView): setattr(self, "label%sTotal%s" % (panel.capitalize(), capitalizedType), lbl) absolute.Add(lbl, 0, wx.ALIGN_LEFT) - units = {"cpu": " tf", "pg": " MW", "droneBandwidth": " mbit/s", "droneBay": u" m\u00B3", "fighterBay": u" m\u00B3", "cargoBay": u" m\u00B3"} + units = {"cpu": " tf", "pg": " MW", "droneBandwidth": " mbit/s", "droneBay": u" m\u00B3", + "fighterBay": u" m\u00B3", "cargoBay": u" m\u00B3"} lbl = wx.StaticText(parent, wx.ID_ANY, "%s" % units[type_]) absolute.Add(lbl, 0, wx.ALIGN_LEFT) diff --git a/gui/builtinStatsViews/targetingMiscViewFull.py b/gui/builtinStatsViews/targetingMiscViewFull.py index e5be6110a..aab2bf25e 100644 --- a/gui/builtinStatsViews/targetingMiscViewFull.py +++ b/gui/builtinStatsViews/targetingMiscViewFull.py @@ -133,14 +133,17 @@ class TargetingMiscViewFull(StatsView): "specialSmallShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSmallShipHoldCapacity"), "specialMediumShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMediumShipHoldCapacity"), "specialLargeShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialLargeShipHoldCapacity"), - "specialIndustrialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialIndustrialShipHoldCapacity"), + "specialIndustrialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr( + "specialIndustrialShipHoldCapacity"), "specialOreHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialOreHoldCapacity"), "specialMineralHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMineralHoldCapacity"), "specialMaterialBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialMaterialBayCapacity"), "specialGasHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialGasHoldCapacity"), "specialSalvageHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSalvageHoldCapacity"), - "specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialCommandCenterHoldCapacity"), - "specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialPlanetaryCommoditiesHoldCapacity"), + "specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr( + "specialCommandCenterHoldCapacity"), + "specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr( + "specialPlanetaryCommoditiesHoldCapacity"), "specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity") } @@ -196,7 +199,8 @@ class TargetingMiscViewFull(StatsView): label.SetToolTip(wx.ToolTip("Max Warp Distance: %.1f AU" % fit.maxWarpDistance)) elif labelName == "labelSensorStr": if fit.jamChance > 0: - label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance))) + label.SetToolTip( + wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance))) else: label.SetToolTip(wx.ToolTip("Type: %s" % (fit.scanType))) elif labelName == "labelFullAlignTime": @@ -206,7 +210,8 @@ class TargetingMiscViewFull(StatsView): label.SetToolTip(wx.ToolTip("%s\n%s\n%s" % (alignTime, mass, agility))) elif labelName == "labelFullCargo": tipLines = [] - tipLines.append(u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])) + tipLines.append( + u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])) for attrName, tipAlias in cargoNamesOrder.items(): if newValues[attrName] > 0: tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) @@ -235,7 +240,8 @@ class TargetingMiscViewFull(StatsView): # if you add stuff to cargo, the capacity doesn't change and thus it is still cached # This assures us that we force refresh of cargo tooltip tipLines = [] - tipLines.append(u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])) + tipLines.append( + u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])) for attrName, tipAlias in cargoNamesOrder.items(): if cachedCargo[attrName] > 0: tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) diff --git a/gui/builtinViewColumns/baseIcon.py b/gui/builtinViewColumns/baseIcon.py index d767a97a6..7f9b16296 100644 --- a/gui/builtinViewColumns/baseIcon.py +++ b/gui/builtinViewColumns/baseIcon.py @@ -1,45 +1,46 @@ -import wx -from eos.types import Drone, Fit, Module, Slot, Rack, Implant -from gui.viewColumn import ViewColumn - - -class BaseIcon(ViewColumn): - name = "Base Icon" - - def __init__(self, fittingView, params): - ViewColumn.__init__(self, fittingView) - self.size = 24 - self.maxsize = self.size - self.mask = wx.LIST_MASK_IMAGE - self.columnText = "" - self.shipImage = fittingView.imageList.GetImageIndex("ship_small", "gui") - - def getImageId(self, stuff): - if isinstance(stuff, Drone): - return -1 - elif isinstance(stuff, Fit): - return self.shipImage - elif isinstance(stuff, Rack): - return -1 - elif isinstance(stuff, Implant): - if stuff.character: # if it has a character as it's parent - return self.fittingView.imageList.GetImageIndex("character_small", "gui") - else: - return self.shipImage - elif isinstance(stuff, Module): - if stuff.isEmpty: - return self.fittingView.imageList.GetImageIndex("slot_%s_small" % Slot.getName(stuff.slot).lower(), "gui") - else: - return self.loadIconFile(stuff.item.icon.iconFile if stuff.item.icon else "") - - item = getattr(stuff, "item", stuff) - return self.loadIconFile(item.icon.iconFile if item.icon else "") - - def loadIconFile(self, iconFile): - if iconFile: - return self.fittingView.imageList.GetImageIndex(iconFile, "icons") - else: - return -1 - - -BaseIcon.register() +import wx +from eos.types import Drone, Fit, Module, Slot, Rack, Implant +from gui.viewColumn import ViewColumn + + +class BaseIcon(ViewColumn): + name = "Base Icon" + + def __init__(self, fittingView, params): + ViewColumn.__init__(self, fittingView) + self.size = 24 + self.maxsize = self.size + self.mask = wx.LIST_MASK_IMAGE + self.columnText = "" + self.shipImage = fittingView.imageList.GetImageIndex("ship_small", "gui") + + def getImageId(self, stuff): + if isinstance(stuff, Drone): + return -1 + elif isinstance(stuff, Fit): + return self.shipImage + elif isinstance(stuff, Rack): + return -1 + elif isinstance(stuff, Implant): + if stuff.character: # if it has a character as it's parent + return self.fittingView.imageList.GetImageIndex("character_small", "gui") + else: + return self.shipImage + elif isinstance(stuff, Module): + if stuff.isEmpty: + return self.fittingView.imageList.GetImageIndex("slot_%s_small" % Slot.getName(stuff.slot).lower(), + "gui") + else: + return self.loadIconFile(stuff.item.icon.iconFile if stuff.item.icon else "") + + item = getattr(stuff, "item", stuff) + return self.loadIconFile(item.icon.iconFile if item.icon else "") + + def loadIconFile(self, iconFile): + if iconFile: + return self.fittingView.imageList.GetImageIndex(iconFile, "icons") + else: + return -1 + + +BaseIcon.register() diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index d739ee903..973219b2c 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -41,7 +41,8 @@ class BaseName(ViewColumn): if isinstance(stuff, Drone): return "%dx %s" % (stuff.amount, stuff.item.name) elif isinstance(stuff, Fighter): - return "%d/%d %s" % (stuff.amountActive, stuff.getModifiedItemAttr("fighterSquadronMaxSize"), stuff.item.name) + return "%d/%d %s" % \ + (stuff.amountActive, stuff.getModifiedItemAttr("fighterSquadronMaxSize"), stuff.item.name) elif isinstance(stuff, Cargo): return "%dx %s" % (stuff.amount, stuff.item.name) elif isinstance(stuff, Fit): diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index 95a8b07bd..6e9c5cb45 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -1,543 +1,559 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -import wx - -from service.fit import Fit -from service.market import Market -import gui.mainFrame -from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader -from gui.utils.numberFormatter import formatAmount -from gui.utils.listFormatter import formatList - - -class Miscellanea(ViewColumn): - name = "Miscellanea" - - def __init__(self, fittingView, params=None): - if params is None: - params = {"showIcon": True, "displayName": False} - - ViewColumn.__init__(self, fittingView) - if params["showIcon"]: - self.imageId = fittingView.imageList.GetImageIndex("column_misc", "gui") - self.bitmap = BitmapLoader.getBitmap("column_misc", "gui") - self.mask = wx.LIST_MASK_IMAGE - else: - self.imageId = -1 - - if params["displayName"] or self.imageId == -1: - self.columnText = "Misc data" - self.mask |= wx.LIST_MASK_TEXT - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - def getText(self, stuff): - return self.__getData(stuff)[0] - - def getToolTip(self, mod): - return self.__getData(mod)[1] - - def getImageId(self, mod): - return -1 - - def getParameters(self): - return (("displayName", bool, False), ("showIcon", bool, True)) - - def __getData(self, stuff): - item = stuff.item - if item is None: - return "", None - itemGroup = item.group.name - itemCategory = item.category.name - - if itemGroup == "Ship Modifiers": - return "", None - elif itemGroup in ("Energy Weapon", "Hybrid Weapon", "Projectile Weapon", "Combat Drone", "Fighter Drone"): - trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") - if not trackingSpeed: - return "", None - text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3)) - tooltip = "Tracking speed" - return text, tooltip - elif itemCategory == "Subsystem": - slots = ("hi", "med", "low") - info = [] - for slot in slots: - n = int(stuff.getModifiedItemAttr("%sSlotModifier" % slot)) - if n > 0: - info.append("{0}{1}".format(n, slot[0].upper())) - return "+ " + ", ".join(info), "Slot Modifiers" - elif itemGroup == "Energy Neutralizer": - neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") - cycleTime = stuff.cycleTime - if not neutAmount or not cycleTime: - return "", None - capPerSec = float(-neutAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) - tooltip = "Energy neutralization per second" - return text, tooltip - elif itemGroup == "Energy Nosferatu": - neutAmount = stuff.getModifiedItemAttr("powerTransferAmount") - cycleTime = stuff.cycleTime - if not neutAmount or not cycleTime: - return "", None - capPerSec = float(-neutAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) - tooltip = "Energy neutralization per second" - return text, tooltip - elif itemGroup == "Salvager": - chance = stuff.getModifiedItemAttr("accessDifficultyBonus") - if not chance: - return "", None - text = "{0}%".format(formatAmount(chance, 3, 0, 3)) - tooltip = "Item retrieval chance" - return text, tooltip - elif itemGroup == "Data Miners": - strength = stuff.getModifiedItemAttr("virusStrength") - coherence = stuff.getModifiedItemAttr("virusCoherence") - if not strength or not coherence: - return "", None - text = "{0} | {1}".format(formatAmount(strength, 3, 0, 3), formatAmount(coherence, 3, 0, 3)) - tooltip = "Virus strength and coherence" - return text, tooltip - elif itemGroup in ("Warp Scrambler", "Warp Core Stabilizer"): - scramStr = stuff.getModifiedItemAttr("warpScrambleStrength") - if not scramStr: - return "", None - text = "{0}".format(formatAmount(-scramStr, 3, 0, 3, forceSign=True)) - tooltip = "Warp core strength modification" - return text, tooltip - elif itemGroup in ("Stasis Web", "Stasis Webifying Drone"): - speedFactor = stuff.getModifiedItemAttr("speedFactor") - if not speedFactor: - return "", None - text = "{0}%".format(formatAmount(speedFactor, 3, 0, 3)) - tooltip = "Speed reduction" - return text, tooltip - elif itemGroup == "Target Painter": - sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") - if not sigRadBonus: - return "", None - text = "{0}%".format(formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) - tooltip = "Signature radius increase" - return text, tooltip - elif itemGroup == "Sensor Dampener": - lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") - scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") - if lockRangeBonus is None or scanResBonus is None: - return "", None - display = 0 - for bonus in (lockRangeBonus, scanResBonus): - if abs(bonus) > abs(display): - display = bonus - if not display: - return "", None - text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) - ttEntries = [] - if display == lockRangeBonus: - ttEntries.append("lock range") - if display == scanResBonus: - ttEntries.append("scan resolution") - tooltip = "{0} dampening".format(formatList(ttEntries)).capitalize() - return text, tooltip - elif itemGroup == "Weapon Disruptor": - # Weapon disruption now covers both tracking and guidance (missile) disruptors - # First get the attributes for tracking disruptors - optimalRangeBonus = stuff.getModifiedItemAttr("maxRangeBonus") - falloffRangeBonus = stuff.getModifiedItemAttr("falloffBonus") - trackingSpeedBonus = stuff.getModifiedItemAttr("trackingSpeedBonus") - - trackingDisruptorAttributes = { - "optimal range": optimalRangeBonus, - "falloff range": falloffRangeBonus, - "tracking speed": trackingSpeedBonus} - - isTrackingDisruptor = any(map(lambda x: x is not None and x != 0, trackingDisruptorAttributes.values())) - - # Then get the attributes for guidance disruptors - explosionVelocityBonus = stuff.getModifiedItemAttr("aoeVelocityBonus") - explosionRadiusBonus = stuff.getModifiedItemAttr("aoeCloudSizeBonus") - - flightTimeBonus = stuff.getModifiedItemAttr("explosionDelayBonus") - missileVelocityBonus = stuff.getModifiedItemAttr("missileVelocityBonus") - - guidanceDisruptorAttributes = { - "explosion velocity": explosionVelocityBonus, - "explosion radius": explosionRadiusBonus, - "flight time": flightTimeBonus, - "missile velocity": missileVelocityBonus} - - isGuidanceDisruptor = any(map(lambda x: x is not None and x != 0, guidanceDisruptorAttributes.values())) - - if isTrackingDisruptor: - attributes = trackingDisruptorAttributes - elif isGuidanceDisruptor: - attributes = guidanceDisruptorAttributes - else: - return "", None - - display = max(attributes.values(), key=lambda x: abs(x)) - - text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) - - ttEntries = [] - for attributeName, attributeValue in attributes.items(): - if attributeValue == display: - ttEntries.append(attributeName) - - tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize() - return text, tooltip - elif itemGroup in ("ECM", "Burst Jammer", "Burst Projectors"): - grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") - ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") - radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") - magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthBonus") - displayMax = max(grav, ladar, radar, magnet) - displayMin = min(grav, ladar, radar, magnet) - if grav is None or ladar is None or radar is None or magnet is None or displayMax is None: - return "", None - - if displayMax == displayMin or displayMin is None: - text = "{0}".format( - formatAmount(displayMax, 3, 0, 3), - ) - else: - text = "{0} | {1}".format( - formatAmount(displayMax, 3, 0, 3), - formatAmount(displayMin, 3, 0, 3), - ) - tooltip = "ECM Jammer Strength:\n{0} Gravimetric | {1} Ladar | {2} Magnetometric | {3} Radar".format( - formatAmount(grav, 3, 0, 3), - formatAmount(ladar, 3, 0, 3), - formatAmount(radar, 3, 0, 3), - formatAmount(magnet, 3, 0, 3), - ) - return text, tooltip - elif itemGroup in ("Remote Sensor Booster", "Sensor Booster", "Signal Amplifier"): - scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") - lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") - gravBonus = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") - if scanResBonus is None or lockRangeBonus is None or gravBonus is None: - return "", None - - text = "{0}% | {1}% | {2}%".format( - formatAmount(scanResBonus, 3, 0, 3), - formatAmount(lockRangeBonus, 3, 0, 3), - formatAmount(gravBonus, 3, 0, 3), - ) - tooltip = "Applied bonuses:\n{0}% scan resolution | {1}% lock range | {2}% sensor strength".format( - formatAmount(scanResBonus, 3, 0, 3), - formatAmount(lockRangeBonus, 3, 0, 3), - formatAmount(gravBonus, 3, 0, 3), - ) - return text, tooltip - elif itemGroup in ("Projected ECCM", "ECCM", "Sensor Backup Array"): - grav = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") - ladar = stuff.getModifiedItemAttr("scanLadarStrengthPercent") - radar = stuff.getModifiedItemAttr("scanRadarStrengthPercent") - magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthPercent") - if grav is None or ladar is None or radar is None or magnet is None: - return "", None - display = max(grav, ladar, radar, magnet) - if not display: - return "", None - text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) - ttEntries = [] - if display == grav: - ttEntries.append("gravimetric") - if display == ladar: - ttEntries.append("ladar") - if display == magnet: - ttEntries.append("magnetometric") - if display == radar: - ttEntries.append("radar") - plu = "" if len(ttEntries) == 1 else "s" - tooltip = "{0} strength{1} bonus".format(formatList(ttEntries), plu).capitalize() - return text, tooltip - elif itemGroup == "Cloaking Device": - recalibration = stuff.getModifiedItemAttr("cloakingTargetingDelay") - if recalibration is None: - return "", None - text = "{0}s".format(formatAmount(float(recalibration) / 1000, 3, 0, 3)) - tooltip = "Sensor recalibration time" - return text, tooltip - elif itemGroup == "Remote Armor Repairer": - repAmount = stuff.getModifiedItemAttr("armorDamageAmount") - cycleTime = stuff.getModifiedItemAttr("duration") - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) - tooltip = "Armor repaired per second" - return text, tooltip - elif itemGroup == "Remote Shield Booster": - repAmount = stuff.getModifiedItemAttr("shieldBonus") - cycleTime = stuff.cycleTime - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) - tooltip = "Shield transferred per second" - return text, tooltip - elif itemGroup == "Remote Capacitor Transmitter": - repAmount = stuff.getModifiedItemAttr("powerTransferAmount") - cycleTime = stuff.cycleTime - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) - tooltip = "Energy transferred per second" - return text, tooltip - elif itemGroup == "Remote Hull Repairer": - repAmount = stuff.getModifiedItemAttr("structureDamageAmount") - cycleTime = stuff.cycleTime - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) - tooltip = "Structure repaired per second" - return text, tooltip - elif itemGroup == "Gang Coordinator": - command = stuff.getModifiedItemAttr("commandBonus") or stuff.getModifiedItemAttr("commandBonusHidden") - if not command: - return "", None - text = "{0}%".format(formatAmount(command, 3, 0, 3, forceSign=True)) - tooltip = "Gang bonus strength" - return text, tooltip - elif itemGroup == "Electronic Warfare Drone": - sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") - lockRangeMult = stuff.getModifiedItemAttr("maxTargetRangeMultiplier") - scanResMult = stuff.getModifiedItemAttr("scanResolutionMultiplier") - falloffRangeMult = stuff.getModifiedItemAttr("fallofMultiplier") - optimalRangeMult = stuff.getModifiedItemAttr("maxRangeMultiplier") - trackingSpeedMult = stuff.getModifiedItemAttr("trackingSpeedMultiplier") - grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") - ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") - radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") - magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthBonus") - if sigRadBonus: - text = "{0}%".format(formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) - tooltip = "Signature radius increase" - return text, tooltip - if lockRangeMult is not None and scanResMult is not None: - lockRangeBonus = (lockRangeMult - 1) * 100 - scanResBonus = (scanResMult - 1) * 100 - display = 0 - for bonus in (lockRangeBonus, scanResBonus): - if abs(bonus) > abs(display): - display = bonus - if not display: - return "", None - text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) - ttEntries = [] - if display == lockRangeBonus: - ttEntries.append("lock range") - if display == scanResBonus: - ttEntries.append("scan resolution") - tooltip = "{0} dampening".format(formatList(ttEntries)).capitalize() - return text, tooltip - if falloffRangeMult is not None and optimalRangeMult is not None and trackingSpeedMult is not None: - falloffRangeBonus = (falloffRangeMult - 1) * 100 - optimalRangeBonus = (optimalRangeMult - 1) * 100 - trackingSpeedBonus = (trackingSpeedMult - 1) * 100 - display = 0 - for bonus in (falloffRangeBonus, optimalRangeBonus, trackingSpeedBonus): - if abs(bonus) > abs(display): - display = bonus - if not display: - return "", None - text = "{0}%".format(formatAmount(display, 3, 0, 3), forceSign=True) - ttEntries = [] - if display == optimalRangeBonus: - ttEntries.append("optimal range") - if display == falloffRangeBonus: - ttEntries.append("falloff range") - if display == trackingSpeedBonus: - ttEntries.append("tracking speed") - tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize() - return text, tooltip - if grav is not None and ladar is not None and radar is not None and magnet is not None: - display = max(grav, ladar, radar, magnet) - if not display: - return "", None - text = "{0}".format(formatAmount(display, 3, 0, 3)) - ttEntries = [] - if display == grav: - ttEntries.append("gravimetric") - if display == ladar: - ttEntries.append("ladar") - if display == magnet: - ttEntries.append("magnetometric") - if display == radar: - ttEntries.append("radar") - plu = "" if len(ttEntries) == 1 else "s" - tooltip = "{0} strength{1}".format(formatList(ttEntries), plu).capitalize() - return text, tooltip - else: - return "", None - elif itemGroup == "Fighter Bomber": - optimalSig = stuff.getModifiedItemAttr("optimalSigRadius") - if not optimalSig: - return "", None - text = "{0}m".format(formatAmount(optimalSig, 3, 0, 3)) - tooltip = "Optimal signature radius" - return text, tooltip - elif itemGroup in ("Frequency Mining Laser", "Strip Miner", "Mining Laser", "Gas Cloud Harvester"): - miningAmount = stuff.getModifiedItemAttr("specialtyMiningAmount") or stuff.getModifiedItemAttr("miningAmount") - cycleTime = stuff.cycleTime - if not miningAmount or not cycleTime: - return "", None - minePerSec = float(miningAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(minePerSec, 3, 0, 3)) - tooltip = "Yield per second" - return text, tooltip - elif itemGroup == "Logistic Drone": - armorAmount = stuff.getModifiedItemAttr("armorDamageAmount") - shieldAmount = stuff.getModifiedItemAttr("shieldBonus") - hullAmount = stuff.getModifiedItemAttr("structureDamageAmount") - repAmount = armorAmount or shieldAmount or hullAmount - cycleTime = stuff.getModifiedItemAttr("duration") - if not repAmount or not cycleTime: - return "", None - repPerSec = float(repAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3)) - ttEntries = [] - if hullAmount is not None and repAmount == hullAmount: - ttEntries.append("structure") - if armorAmount is not None and repAmount == armorAmount: - ttEntries.append("armor") - if shieldAmount is not None and repAmount == shieldAmount: - ttEntries.append("shield") - tooltip = "{0} repaired per second".format(formatList(ttEntries)).capitalize() - return text, tooltip - elif itemGroup == "Energy Neutralizer Drone": - neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") - cycleTime = stuff.getModifiedItemAttr("energyNeutralizerDuration") - if not neutAmount or not cycleTime: - return "", None - capPerSec = float(-neutAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) - tooltip = "Energy neutralization per second" - return text, tooltip - elif itemGroup == "Mining Drone": - miningAmount = stuff.getModifiedItemAttr("miningAmount") - cycleTime = stuff.getModifiedItemAttr("duration") - if not miningAmount or not cycleTime: - return "", None - minePerSec = float(miningAmount) * 1000 / cycleTime - text = "{0}/s".format(formatAmount(minePerSec, 3, 0, 3)) - tooltip = "Yield per second" - return text, tooltip - elif itemGroup == "Micro Jump Drive": - cycleTime = stuff.getModifiedItemAttr("duration") / 1000 - text = "{0}s".format(cycleTime) - tooltip = "Spoolup time" - return text, tooltip - elif itemGroup in ("Siege Module", "Cynosural Field"): - amt = stuff.getModifiedItemAttr("consumptionQuantity") - if amt: - typeID = stuff.getModifiedItemAttr("consumptionType") - item = Market.getInstance().getItem(typeID) - text = "{0} units".format(formatAmount(amt, 3, 0, 3)) - return text, item.name - else: - return "", None - elif itemGroup in ("Ancillary Armor Repairer", "Ancillary Shield Booster"): - hp = stuff.hpBeforeReload - cycles = stuff.numShots - cycleTime = stuff.rawCycleTime - if not hp or not cycleTime or not cycles: - return "", None - fit = Fit.getInstance().getFit(self.mainFrame.getActiveFit()) - ehpTotal = fit.ehp - hpTotal = fit.hp - useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective - tooltip = "HP restored over duration using charges" - if useEhp: - if itemGroup == "Ancillary Armor Repairer": - hpRatio = ehpTotal["armor"] / hpTotal["armor"] - else: - hpRatio = ehpTotal["shield"] / hpTotal["shield"] - tooltip = "E{0}".format(tooltip) - else: - hpRatio = 1 - if itemGroup == "Ancillary Armor Repairer": - hpRatio *= 3 - ehp = hp * hpRatio - duration = cycles * cycleTime / 1000 - text = "{0} / {1}s".format(formatAmount(ehp, 3, 0, 9), formatAmount(duration, 3, 0, 3)) - - return text, tooltip - elif itemGroup == "Armor Resistance Shift Hardener": - itemArmorResistanceShiftHardenerEM = (1 - stuff.getModifiedItemAttr("armorEmDamageResonance")) * 100 - itemArmorResistanceShiftHardenerTherm = (1 - stuff.getModifiedItemAttr("armorThermalDamageResonance")) * 100 - itemArmorResistanceShiftHardenerKin = (1 - stuff.getModifiedItemAttr("armorKineticDamageResonance")) * 100 - itemArmorResistanceShiftHardenerExp = (1 - stuff.getModifiedItemAttr("armorExplosiveDamageResonance")) * 100 - - text = "{0}% | {1}% | {2}% | {3}%".format( - formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), - ) - tooltip = "Resistances Shifted to Damage Profile:\n{0}% EM | {1}% Therm | {2}% Kin | {3}% Exp".format( - formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), - formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), - ) - return text, tooltip - elif stuff.charge is not None: - chargeGroup = stuff.charge.group.name - if chargeGroup in ("Rocket", "Advanced Rocket", "Light Missile", "Advanced Light Missile", "FoF Light Missile", - "Heavy Assault Missile", "Advanced Heavy Assault Missile", "Heavy Missile", "Advanced Heavy Missile", "FoF Heavy Missile", - "Torpedo", "Advanced Torpedo", "Cruise Missile", "Advanced Cruise Missile", "FoF Cruise Missile", - "XL Torpedo", "XL Cruise Missile"): - cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") - aoeVelocity = stuff.getModifiedChargeAttr("aoeVelocity") - if not cloudSize or not aoeVelocity: - return "", None - text = "{0}{1} | {2}{3}".format(formatAmount(cloudSize, 3, 0, 3), "m", - formatAmount(aoeVelocity, 3, 0, 3), "m/s") - tooltip = "Explosion radius and explosion velocity" - return text, tooltip - elif chargeGroup == "Bomb": - cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") - if not cloudSize: - return "", None - text = "{0}{1}".format(formatAmount(cloudSize, 3, 0, 3), "m") - tooltip = "Explosion radius" - return text, tooltip - elif chargeGroup in ("Scanner Probe",): - scanStr = stuff.getModifiedChargeAttr("baseSensorStrength") - baseRange = stuff.getModifiedChargeAttr("baseScanRange") - if not scanStr or not baseRange: - return "", None - strTwoAu = scanStr / (2.0 / baseRange) - text = "{0}".format(formatAmount(strTwoAu, 3, 0, 3)) - tooltip = "Scan strength with 2 AU scan range" - return text, tooltip - else: - return "", None - else: - return "", None - - -Miscellanea.register() +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +import wx + +from service.fit import Fit +from service.market import Market +import gui.mainFrame +from gui.viewColumn import ViewColumn +from gui.bitmapLoader import BitmapLoader +from gui.utils.numberFormatter import formatAmount +from gui.utils.listFormatter import formatList + + +class Miscellanea(ViewColumn): + name = "Miscellanea" + + def __init__(self, fittingView, params=None): + if params is None: + params = {"showIcon": True, "displayName": False} + + ViewColumn.__init__(self, fittingView) + if params["showIcon"]: + self.imageId = fittingView.imageList.GetImageIndex("column_misc", "gui") + self.bitmap = BitmapLoader.getBitmap("column_misc", "gui") + self.mask = wx.LIST_MASK_IMAGE + else: + self.imageId = -1 + + if params["displayName"] or self.imageId == -1: + self.columnText = "Misc data" + self.mask |= wx.LIST_MASK_TEXT + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def getText(self, stuff): + return self.__getData(stuff)[0] + + def getToolTip(self, mod): + return self.__getData(mod)[1] + + def getImageId(self, mod): + return -1 + + def getParameters(self): + return (("displayName", bool, False), ("showIcon", bool, True)) + + def __getData(self, stuff): + item = stuff.item + if item is None: + return "", None + itemGroup = item.group.name + itemCategory = item.category.name + + if itemGroup == "Ship Modifiers": + return "", None + elif itemGroup in ("Energy Weapon", "Hybrid Weapon", "Projectile Weapon", "Combat Drone", "Fighter Drone"): + trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed") + if not trackingSpeed: + return "", None + text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3)) + tooltip = "Tracking speed" + return text, tooltip + elif itemCategory == "Subsystem": + slots = ("hi", "med", "low") + info = [] + for slot in slots: + n = int(stuff.getModifiedItemAttr("%sSlotModifier" % slot)) + if n > 0: + info.append("{0}{1}".format(n, slot[0].upper())) + return "+ " + ", ".join(info), "Slot Modifiers" + elif itemGroup == "Energy Neutralizer": + neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") + cycleTime = stuff.cycleTime + if not neutAmount or not cycleTime: + return "", None + capPerSec = float(-neutAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) + tooltip = "Energy neutralization per second" + return text, tooltip + elif itemGroup == "Energy Nosferatu": + neutAmount = stuff.getModifiedItemAttr("powerTransferAmount") + cycleTime = stuff.cycleTime + if not neutAmount or not cycleTime: + return "", None + capPerSec = float(-neutAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) + tooltip = "Energy neutralization per second" + return text, tooltip + elif itemGroup == "Salvager": + chance = stuff.getModifiedItemAttr("accessDifficultyBonus") + if not chance: + return "", None + text = "{0}%".format(formatAmount(chance, 3, 0, 3)) + tooltip = "Item retrieval chance" + return text, tooltip + elif itemGroup == "Data Miners": + strength = stuff.getModifiedItemAttr("virusStrength") + coherence = stuff.getModifiedItemAttr("virusCoherence") + if not strength or not coherence: + return "", None + text = "{0} | {1}".format(formatAmount(strength, 3, 0, 3), formatAmount(coherence, 3, 0, 3)) + tooltip = "Virus strength and coherence" + return text, tooltip + elif itemGroup in ("Warp Scrambler", "Warp Core Stabilizer"): + scramStr = stuff.getModifiedItemAttr("warpScrambleStrength") + if not scramStr: + return "", None + text = "{0}".format(formatAmount(-scramStr, 3, 0, 3, forceSign=True)) + tooltip = "Warp core strength modification" + return text, tooltip + elif itemGroup in ("Stasis Web", "Stasis Webifying Drone"): + speedFactor = stuff.getModifiedItemAttr("speedFactor") + if not speedFactor: + return "", None + text = "{0}%".format(formatAmount(speedFactor, 3, 0, 3)) + tooltip = "Speed reduction" + return text, tooltip + elif itemGroup == "Target Painter": + sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") + if not sigRadBonus: + return "", None + text = "{0}%".format(formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) + tooltip = "Signature radius increase" + return text, tooltip + elif itemGroup == "Sensor Dampener": + lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") + scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") + if lockRangeBonus is None or scanResBonus is None: + return "", None + display = 0 + for bonus in (lockRangeBonus, scanResBonus): + if abs(bonus) > abs(display): + display = bonus + if not display: + return "", None + text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) + ttEntries = [] + if display == lockRangeBonus: + ttEntries.append("lock range") + if display == scanResBonus: + ttEntries.append("scan resolution") + tooltip = "{0} dampening".format(formatList(ttEntries)).capitalize() + return text, tooltip + elif itemGroup == "Weapon Disruptor": + # Weapon disruption now covers both tracking and guidance (missile) disruptors + # First get the attributes for tracking disruptors + optimalRangeBonus = stuff.getModifiedItemAttr("maxRangeBonus") + falloffRangeBonus = stuff.getModifiedItemAttr("falloffBonus") + trackingSpeedBonus = stuff.getModifiedItemAttr("trackingSpeedBonus") + + trackingDisruptorAttributes = { + "optimal range": optimalRangeBonus, + "falloff range": falloffRangeBonus, + "tracking speed": trackingSpeedBonus} + + isTrackingDisruptor = any(map(lambda x: x is not None and x != 0, trackingDisruptorAttributes.values())) + + # Then get the attributes for guidance disruptors + explosionVelocityBonus = stuff.getModifiedItemAttr("aoeVelocityBonus") + explosionRadiusBonus = stuff.getModifiedItemAttr("aoeCloudSizeBonus") + + flightTimeBonus = stuff.getModifiedItemAttr("explosionDelayBonus") + missileVelocityBonus = stuff.getModifiedItemAttr("missileVelocityBonus") + + guidanceDisruptorAttributes = { + "explosion velocity": explosionVelocityBonus, + "explosion radius": explosionRadiusBonus, + "flight time": flightTimeBonus, + "missile velocity": missileVelocityBonus} + + isGuidanceDisruptor = any(map(lambda x: x is not None and x != 0, guidanceDisruptorAttributes.values())) + + if isTrackingDisruptor: + attributes = trackingDisruptorAttributes + elif isGuidanceDisruptor: + attributes = guidanceDisruptorAttributes + else: + return "", None + + display = max(attributes.values(), key=lambda x: abs(x)) + + text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) + + ttEntries = [] + for attributeName, attributeValue in attributes.items(): + if attributeValue == display: + ttEntries.append(attributeName) + + tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize() + return text, tooltip + elif itemGroup in ("ECM", "Burst Jammer", "Burst Projectors"): + grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") + ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") + radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") + magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthBonus") + displayMax = max(grav, ladar, radar, magnet) + displayMin = min(grav, ladar, radar, magnet) + if grav is None or ladar is None or radar is None or magnet is None or displayMax is None: + return "", None + + if displayMax == displayMin or displayMin is None: + text = "{0}".format( + formatAmount(displayMax, 3, 0, 3), + ) + else: + text = "{0} | {1}".format( + formatAmount(displayMax, 3, 0, 3), + formatAmount(displayMin, 3, 0, 3), + ) + tooltip = "ECM Jammer Strength:\n{0} Gravimetric | {1} Ladar | {2} Magnetometric | {3} Radar".format( + formatAmount(grav, 3, 0, 3), + formatAmount(ladar, 3, 0, 3), + formatAmount(radar, 3, 0, 3), + formatAmount(magnet, 3, 0, 3), + ) + return text, tooltip + elif itemGroup in ("Remote Sensor Booster", "Sensor Booster", "Signal Amplifier"): + scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus") + lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus") + gravBonus = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") + if scanResBonus is None or lockRangeBonus is None or gravBonus is None: + return "", None + + text = "{0}% | {1}% | {2}%".format( + formatAmount(scanResBonus, 3, 0, 3), + formatAmount(lockRangeBonus, 3, 0, 3), + formatAmount(gravBonus, 3, 0, 3), + ) + tooltip = "Applied bonuses:\n{0}% scan resolution | {1}% lock range | {2}% sensor strength".format( + formatAmount(scanResBonus, 3, 0, 3), + formatAmount(lockRangeBonus, 3, 0, 3), + formatAmount(gravBonus, 3, 0, 3), + ) + return text, tooltip + elif itemGroup in ("Projected ECCM", "ECCM", "Sensor Backup Array"): + grav = stuff.getModifiedItemAttr("scanGravimetricStrengthPercent") + ladar = stuff.getModifiedItemAttr("scanLadarStrengthPercent") + radar = stuff.getModifiedItemAttr("scanRadarStrengthPercent") + magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthPercent") + if grav is None or ladar is None or radar is None or magnet is None: + return "", None + display = max(grav, ladar, radar, magnet) + if not display: + return "", None + text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) + ttEntries = [] + if display == grav: + ttEntries.append("gravimetric") + if display == ladar: + ttEntries.append("ladar") + if display == magnet: + ttEntries.append("magnetometric") + if display == radar: + ttEntries.append("radar") + plu = "" if len(ttEntries) == 1 else "s" + tooltip = "{0} strength{1} bonus".format(formatList(ttEntries), plu).capitalize() + return text, tooltip + elif itemGroup == "Cloaking Device": + recalibration = stuff.getModifiedItemAttr("cloakingTargetingDelay") + if recalibration is None: + return "", None + text = "{0}s".format(formatAmount(float(recalibration) / 1000, 3, 0, 3)) + tooltip = "Sensor recalibration time" + return text, tooltip + elif itemGroup == "Remote Armor Repairer": + repAmount = stuff.getModifiedItemAttr("armorDamageAmount") + cycleTime = stuff.getModifiedItemAttr("duration") + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + tooltip = "Armor repaired per second" + return text, tooltip + elif itemGroup == "Remote Shield Booster": + repAmount = stuff.getModifiedItemAttr("shieldBonus") + cycleTime = stuff.cycleTime + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + tooltip = "Shield transferred per second" + return text, tooltip + elif itemGroup == "Remote Capacitor Transmitter": + repAmount = stuff.getModifiedItemAttr("powerTransferAmount") + cycleTime = stuff.cycleTime + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + tooltip = "Energy transferred per second" + return text, tooltip + elif itemGroup == "Remote Hull Repairer": + repAmount = stuff.getModifiedItemAttr("structureDamageAmount") + cycleTime = stuff.cycleTime + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3, forceSign=True)) + tooltip = "Structure repaired per second" + return text, tooltip + elif itemGroup == "Gang Coordinator": + command = stuff.getModifiedItemAttr("commandBonus") or stuff.getModifiedItemAttr("commandBonusHidden") + if not command: + return "", None + text = "{0}%".format(formatAmount(command, 3, 0, 3, forceSign=True)) + tooltip = "Gang bonus strength" + return text, tooltip + elif itemGroup == "Electronic Warfare Drone": + sigRadBonus = stuff.getModifiedItemAttr("signatureRadiusBonus") + lockRangeMult = stuff.getModifiedItemAttr("maxTargetRangeMultiplier") + scanResMult = stuff.getModifiedItemAttr("scanResolutionMultiplier") + falloffRangeMult = stuff.getModifiedItemAttr("fallofMultiplier") + optimalRangeMult = stuff.getModifiedItemAttr("maxRangeMultiplier") + trackingSpeedMult = stuff.getModifiedItemAttr("trackingSpeedMultiplier") + grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus") + ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus") + radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus") + magnet = stuff.getModifiedItemAttr("scanMagnetometricStrengthBonus") + if sigRadBonus: + text = "{0}%".format(formatAmount(sigRadBonus, 3, 0, 3, forceSign=True)) + tooltip = "Signature radius increase" + return text, tooltip + if lockRangeMult is not None and scanResMult is not None: + lockRangeBonus = (lockRangeMult - 1) * 100 + scanResBonus = (scanResMult - 1) * 100 + display = 0 + for bonus in (lockRangeBonus, scanResBonus): + if abs(bonus) > abs(display): + display = bonus + if not display: + return "", None + text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) + ttEntries = [] + if display == lockRangeBonus: + ttEntries.append("lock range") + if display == scanResBonus: + ttEntries.append("scan resolution") + tooltip = "{0} dampening".format(formatList(ttEntries)).capitalize() + return text, tooltip + if falloffRangeMult is not None and optimalRangeMult is not None and trackingSpeedMult is not None: + falloffRangeBonus = (falloffRangeMult - 1) * 100 + optimalRangeBonus = (optimalRangeMult - 1) * 100 + trackingSpeedBonus = (trackingSpeedMult - 1) * 100 + display = 0 + for bonus in (falloffRangeBonus, optimalRangeBonus, trackingSpeedBonus): + if abs(bonus) > abs(display): + display = bonus + if not display: + return "", None + text = "{0}%".format(formatAmount(display, 3, 0, 3), forceSign=True) + ttEntries = [] + if display == optimalRangeBonus: + ttEntries.append("optimal range") + if display == falloffRangeBonus: + ttEntries.append("falloff range") + if display == trackingSpeedBonus: + ttEntries.append("tracking speed") + tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize() + return text, tooltip + if grav is not None and ladar is not None and radar is not None and magnet is not None: + display = max(grav, ladar, radar, magnet) + if not display: + return "", None + text = "{0}".format(formatAmount(display, 3, 0, 3)) + ttEntries = [] + if display == grav: + ttEntries.append("gravimetric") + if display == ladar: + ttEntries.append("ladar") + if display == magnet: + ttEntries.append("magnetometric") + if display == radar: + ttEntries.append("radar") + plu = "" if len(ttEntries) == 1 else "s" + tooltip = "{0} strength{1}".format(formatList(ttEntries), plu).capitalize() + return text, tooltip + else: + return "", None + elif itemGroup == "Fighter Bomber": + optimalSig = stuff.getModifiedItemAttr("optimalSigRadius") + if not optimalSig: + return "", None + text = "{0}m".format(formatAmount(optimalSig, 3, 0, 3)) + tooltip = "Optimal signature radius" + return text, tooltip + elif itemGroup in ("Frequency Mining Laser", "Strip Miner", "Mining Laser", "Gas Cloud Harvester"): + miningAmount = stuff.getModifiedItemAttr("specialtyMiningAmount") or stuff.getModifiedItemAttr( + "miningAmount") + cycleTime = stuff.cycleTime + if not miningAmount or not cycleTime: + return "", None + minePerSec = float(miningAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(minePerSec, 3, 0, 3)) + tooltip = "Yield per second" + return text, tooltip + elif itemGroup == "Logistic Drone": + armorAmount = stuff.getModifiedItemAttr("armorDamageAmount") + shieldAmount = stuff.getModifiedItemAttr("shieldBonus") + hullAmount = stuff.getModifiedItemAttr("structureDamageAmount") + repAmount = armorAmount or shieldAmount or hullAmount + cycleTime = stuff.getModifiedItemAttr("duration") + if not repAmount or not cycleTime: + return "", None + repPerSec = float(repAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(repPerSec, 3, 0, 3)) + ttEntries = [] + if hullAmount is not None and repAmount == hullAmount: + ttEntries.append("structure") + if armorAmount is not None and repAmount == armorAmount: + ttEntries.append("armor") + if shieldAmount is not None and repAmount == shieldAmount: + ttEntries.append("shield") + tooltip = "{0} repaired per second".format(formatList(ttEntries)).capitalize() + return text, tooltip + elif itemGroup == "Energy Neutralizer Drone": + neutAmount = stuff.getModifiedItemAttr("energyNeutralizerAmount") + cycleTime = stuff.getModifiedItemAttr("energyNeutralizerDuration") + if not neutAmount or not cycleTime: + return "", None + capPerSec = float(-neutAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) + tooltip = "Energy neutralization per second" + return text, tooltip + elif itemGroup == "Mining Drone": + miningAmount = stuff.getModifiedItemAttr("miningAmount") + cycleTime = stuff.getModifiedItemAttr("duration") + if not miningAmount or not cycleTime: + return "", None + minePerSec = float(miningAmount) * 1000 / cycleTime + text = "{0}/s".format(formatAmount(minePerSec, 3, 0, 3)) + tooltip = "Yield per second" + return text, tooltip + elif itemGroup == "Micro Jump Drive": + cycleTime = stuff.getModifiedItemAttr("duration") / 1000 + text = "{0}s".format(cycleTime) + tooltip = "Spoolup time" + return text, tooltip + elif itemGroup in ("Siege Module", "Cynosural Field"): + amt = stuff.getModifiedItemAttr("consumptionQuantity") + if amt: + typeID = stuff.getModifiedItemAttr("consumptionType") + item = Market.getInstance().getItem(typeID) + text = "{0} units".format(formatAmount(amt, 3, 0, 3)) + return text, item.name + else: + return "", None + elif itemGroup in ("Ancillary Armor Repairer", "Ancillary Shield Booster"): + hp = stuff.hpBeforeReload + cycles = stuff.numShots + cycleTime = stuff.rawCycleTime + if not hp or not cycleTime or not cycles: + return "", None + fit = Fit.getInstance().getFit(self.mainFrame.getActiveFit()) + ehpTotal = fit.ehp + hpTotal = fit.hp + useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective + tooltip = "HP restored over duration using charges" + if useEhp: + if itemGroup == "Ancillary Armor Repairer": + hpRatio = ehpTotal["armor"] / hpTotal["armor"] + else: + hpRatio = ehpTotal["shield"] / hpTotal["shield"] + tooltip = "E{0}".format(tooltip) + else: + hpRatio = 1 + if itemGroup == "Ancillary Armor Repairer": + hpRatio *= 3 + ehp = hp * hpRatio + duration = cycles * cycleTime / 1000 + text = "{0} / {1}s".format(formatAmount(ehp, 3, 0, 9), formatAmount(duration, 3, 0, 3)) + + return text, tooltip + elif itemGroup == "Armor Resistance Shift Hardener": + itemArmorResistanceShiftHardenerEM = (1 - stuff.getModifiedItemAttr("armorEmDamageResonance")) * 100 + itemArmorResistanceShiftHardenerTherm = (1 - stuff.getModifiedItemAttr("armorThermalDamageResonance")) * 100 + itemArmorResistanceShiftHardenerKin = (1 - stuff.getModifiedItemAttr("armorKineticDamageResonance")) * 100 + itemArmorResistanceShiftHardenerExp = (1 - stuff.getModifiedItemAttr("armorExplosiveDamageResonance")) * 100 + + text = "{0}% | {1}% | {2}% | {3}%".format( + formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), + ) + tooltip = "Resistances Shifted to Damage Profile:\n{0}% EM | {1}% Therm | {2}% Kin | {3}% Exp".format( + formatAmount(itemArmorResistanceShiftHardenerEM, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerTherm, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerKin, 3, 0, 3), + formatAmount(itemArmorResistanceShiftHardenerExp, 3, 0, 3), + ) + return text, tooltip + elif stuff.charge is not None: + chargeGroup = stuff.charge.group.name + if chargeGroup in ( + "Rocket", + "Advanced Rocket", + "Light Missile", + "Advanced Light Missile", + "FoF Light Missile", + "Heavy Assault Missile", + "Advanced Heavy Assault Missile", + "Heavy Missile", + "Advanced Heavy Missile", + "FoF Heavy Missile", + "Torpedo", + "Advanced Torpedo", + "Cruise Missile", + "Advanced Cruise Missile", + "FoF Cruise Missile", + "XL Torpedo", + "XL Cruise Missile" + ): + cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") + aoeVelocity = stuff.getModifiedChargeAttr("aoeVelocity") + if not cloudSize or not aoeVelocity: + return "", None + text = "{0}{1} | {2}{3}".format(formatAmount(cloudSize, 3, 0, 3), "m", + formatAmount(aoeVelocity, 3, 0, 3), "m/s") + tooltip = "Explosion radius and explosion velocity" + return text, tooltip + elif chargeGroup == "Bomb": + cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize") + if not cloudSize: + return "", None + text = "{0}{1}".format(formatAmount(cloudSize, 3, 0, 3), "m") + tooltip = "Explosion radius" + return text, tooltip + elif chargeGroup in ("Scanner Probe",): + scanStr = stuff.getModifiedChargeAttr("baseSensorStrength") + baseRange = stuff.getModifiedChargeAttr("baseScanRange") + if not scanStr or not baseRange: + return "", None + strTwoAu = scanStr / (2.0 / baseRange) + text = "{0}".format(formatAmount(strTwoAu, 3, 0, 3)) + tooltip = "Scan strength with 2 AU scan range" + return text, tooltip + else: + return "", None + else: + return "", None + + +Miscellanea.register() diff --git a/gui/builtinViewColumns/propertyDisplay.py b/gui/builtinViewColumns/propertyDisplay.py index 71f22ca5d..1972cfae1 100644 --- a/gui/builtinViewColumns/propertyDisplay.py +++ b/gui/builtinViewColumns/propertyDisplay.py @@ -18,10 +18,8 @@ # ============================================================================= from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.attribute import Attribute -import wx class PropertyDisplay(ViewColumn): diff --git a/gui/builtinViewColumns/state.py b/gui/builtinViewColumns/state.py index b6c8698ec..920562087 100644 --- a/gui/builtinViewColumns/state.py +++ b/gui/builtinViewColumns/state.py @@ -47,7 +47,8 @@ class State(ViewColumn): def getImageId(self, stuff): generic_active = self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(1).lower(), "gui") - generic_inactive = self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(-1).lower(), "gui") + generic_inactive = self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(-1).lower(), + "gui") if isinstance(stuff, Drone): if stuff.amountActive > 0: @@ -60,7 +61,8 @@ class State(ViewColumn): if stuff.isEmpty: return -1 else: - return self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(stuff.state).lower(), "gui") + return self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(stuff.state).lower(), + "gui") elif isinstance(stuff, Fit): fitID = self.mainFrame.getActiveFit() projectionInfo = stuff.getProjectionInfo(fitID) diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index c9bffe951..b77efe8a5 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -1,6 +1,7 @@ import wx from gui.bitmapLoader import BitmapLoader + class BaseValidator(wx.PyValidator): def __init__(self): wx.PyValidator.__init__(self) @@ -14,6 +15,7 @@ class BaseValidator(wx.PyValidator): def TransferFromWindow(self): return True + class TextEntryValidatedDialog(wx.TextEntryDialog): def __init__(self, parent, validator=None, *args, **kargs): wx.TextEntryDialog.__init__(self, parent, *args, **kargs) @@ -23,7 +25,8 @@ class TextEntryValidatedDialog(wx.TextEntryDialog): if validator: self.txtctrl.SetValidator(validator()) -class EntityEditor (wx.Panel): + +class EntityEditor(wx.Panel): """ Entity Editor is a panel that takes some sort of list as a source and populates a drop down with options to add/ rename/clone/delete an entity. Comes with dialogs that take user input. Classes that derive this class must override @@ -51,7 +54,7 @@ class EntityEditor (wx.Panel): bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) if size is None: - size = btn.GetSize() + size = btn.GetSize() btn.SetMinSize(size) btn.SetMaxSize(size) @@ -134,8 +137,9 @@ class EntityEditor (wx.Panel): def OnDelete(self, event): dlg = wx.MessageDialog(self, - "Do you really want to delete the {} {}?".format(self.getActiveEntity().name, self.entityName), - "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + "Do you really want to delete the {} {}?".format(self.getActiveEntity().name, + self.entityName), + "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_YES: @@ -170,4 +174,4 @@ class EntityEditor (wx.Panel): return False self.Parent.Show() - return True \ No newline at end of file + return True diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index cdcddde14..7e9d09d80 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -93,18 +93,18 @@ FitSpawner.register() # Drag'n'drop handler class FittingViewDrop(wx.PyDropTarget): - def __init__(self, dropFn): - wx.PyDropTarget.__init__(self) - self.dropFn = dropFn - # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() - self.SetDataObject(self.dropData) + def __init__(self, dropFn): + wx.PyDropTarget.__init__(self) + self.dropFn = dropFn + # this is really transferring an EVE itemID + self.dropData = wx.PyTextDataObject() + self.SetDataObject(self.dropData) - def OnData(self, x, y, t): - if self.GetData(): - data = self.dropData.GetText().split(':') - self.dropFn(x, y, data) - return t + def OnData(self, x, y, t): + if self.GetData(): + data = self.dropData.GetText().split(':') + self.dropFn(x, y, data) + return t class FittingView(d.Display): @@ -372,7 +372,8 @@ class FittingView(d.Display): module = self.mods[dstRow] sFit = Fit.getInstance() - sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, srcIdx, mstate.CmdDown() and module.isEmpty) + sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, srcIdx, + mstate.CmdDown() and module.isEmpty) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) @@ -425,7 +426,7 @@ class FittingView(d.Display): # as Racks and tactical Modes. This allows us to skip over common # module operations such as swapping, removing, copying, etc. that # would otherwise cause complications - self.blanks = [] # preliminary markers where blanks will be inserted + self.blanks = [] # preliminary markers where blanks will be inserted if sFit.serviceFittingOptions["rackSlots"]: # flag to know when to add blanks, based on previous slot diff --git a/gui/builtinViews/fleetView.py b/gui/builtinViews/fleetView.py index 3b4347ff2..466407b7e 100644 --- a/gui/builtinViews/fleetView.py +++ b/gui/builtinViews/fleetView.py @@ -71,10 +71,14 @@ class FleetView(wx.gizmos.TreeListCtrl): event.Skip() def checkNew(self, event): + self.GetPyData(event.Item) + + # TODO: Doesn't seem to be used + ''' data = self.GetPyData(event.Item) if data and isinstance(data, tuple) and data[0] == "add": layer = data[1] - + ''' def UpdateTab(self, name, img): self.tabManager.SetPageTextIcon(self.tabManager.GetSelection(), name, img) diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index b06a0ea5c..8b56833df 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -8,7 +8,7 @@ from gui.bitmapLoader import BitmapLoader from gui.marketBrowser import SearchBox -class BaseImplantEditorView (wx.Panel): +class BaseImplantEditorView(wx.Panel): def addMarketViewImage(self, iconFile): if iconFile is None: return -1 @@ -19,7 +19,8 @@ class BaseImplantEditorView (wx.Panel): return self.availableImplantsImageList.Add(bitmap) def __init__(self, parent): - wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=wx.TAB_TRAVERSAL) self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -53,10 +54,12 @@ class BaseImplantEditorView (wx.Panel): buttonSizer = wx.BoxSizer(wx.VERTICAL) buttonSizer.AddSpacer((0, 0), 1) - self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style=wx.BORDER_NONE) + self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), + style=wx.BORDER_NONE) buttonSizer.Add(self.btnAdd, 0) - self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style=wx.BORDER_NONE) + self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), + style=wx.BORDER_NONE) buttonSizer.Add(self.btnRemove, 0) buttonSizer.AddSpacer((0, 0), 1) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 2d9e63277..f2b4b54db 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -557,7 +557,7 @@ class APIView(wx.Panel): if chars: for charName in chars: - i = self.charChoice.Append(charName) + self.charChoice.Append(charName) self.charChoice.SetStringSelection(char) self.charChoice.Enable(True) self.btnFetchSkills.Enable(True) @@ -598,7 +598,7 @@ class APIView(wx.Panel): else: self.charChoice.Clear() for charName in list: - i = self.charChoice.Append(charName) + self.charChoice.Append(charName) self.btnFetchSkills.Enable(True) self.charChoice.Enable(True) diff --git a/gui/commandView.py b/gui/commandView.py index 10f8fda1d..20a7a33a9 100644 --- a/gui/commandView.py +++ b/gui/commandView.py @@ -25,7 +25,6 @@ import gui.droneView from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu from service.fit import Fit -from service.market import Market from eos.saveddata.drone import Drone as es_Drone diff --git a/gui/fighterView.py b/gui/fighterView.py index 524c07976..083901460 100644 --- a/gui/fighterView.py +++ b/gui/fighterView.py @@ -27,6 +27,7 @@ from gui.builtinViewColumns.state import State from eos.types import Slot from gui.contextMenu import ContextMenu from service.fit import Fit +from service.market import Market class FighterViewDrop(wx.PyDropTarget): @@ -172,7 +173,6 @@ class FighterDisplay(d.Display): keycode = event.GetKeyCode() if keycode == wx.WXK_DELETE or keycode == wx.WXK_NUMPAD_DELETE: row = self.GetFirstSelected() - firstSel = row if row != -1: fighter = self.fighters[self.GetItemData(row)] self.removeFighter(fighter) @@ -187,7 +187,7 @@ class FighterDisplay(d.Display): dropSource = wx.DropSource(self) dropSource.SetData(data) - res = dropSource.DoDragDrop() + dropSource.DoDragDrop() def handleDragDrop(self, x, y, data): ''' diff --git a/gui/itemStats.py b/gui/itemStats.py index a7c008b12..0dfb24181 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -65,7 +65,9 @@ class ItemStatsDialog(wx.Dialog): except IndexError: itmContext = None item = getattr(victim, "item", None) if srcContext.lower() not in ( - "projectedcharge", "fittingcharge") else getattr(victim, "charge", None) + "projectedcharge", + "fittingcharge" + ) else getattr(victim, "charge", None) if item is None: sMkt = Market.getInstance() item = sMkt.getItem(victim.ID) @@ -477,7 +479,7 @@ class ItemParams(wx.Panel): trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName), "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), "Modifier Percent": ( - lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), + lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), "Volume": (lambda: value, u"m\u00B3"), "Sizeclass": (lambda: value, ""), "Absolute Percent": (lambda: (value * 100), unitName), @@ -1254,10 +1256,18 @@ class ItemAffectedBy(wx.Panel): if self.showRealNames: display = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized) saved = "%s %s %.2f %s" % ( - (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized) + displayName if displayName != "" else attrName, + attrModifier, + attrAmount, + penalized + ) else: display = "%s %s %.2f %s" % ( - (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized) + displayName if displayName != "" else attrName, + attrModifier, + attrAmount, + penalized + ) saved = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized) treeitem = self.affectedBy.AppendItem(child, display, attrIcon) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 8f2cf73e7..4c7062f5a 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -26,14 +26,12 @@ import time from codecs import open -from wx._core import PyDeadObjectError from wx.lib.wordwrap import wordwrap import config import gui.aboutData import gui.chromeTabs -import gui.utils.animUtils as animUtils import gui.globalEvents as GE from gui.bitmapLoader import BitmapLoader @@ -52,9 +50,9 @@ from gui.preferenceDialog import PreferenceDialog from gui.graphFrame import GraphFrame from gui.copySelectDialog import CopySelectDialog from gui.utils.clipboard import toClipboard, fromClipboard -from gui.fleetBrowser import FleetBrowser +from gui.fleetBrowser import FleetBrowser # Noqa - TODO: unsure if this is needed here from gui.updateDialog import UpdateDialog -from gui.builtinViews import * +from gui.builtinViews import * # Noqa - TODO: unsure if this is needed here from gui import graphFrame from service.settings import SettingsProvider diff --git a/gui/projectedView.py b/gui/projectedView.py index 8a3135121..f9c666f60 100644 --- a/gui/projectedView.py +++ b/gui/projectedView.py @@ -23,9 +23,11 @@ import gui.globalEvents as GE import gui.droneView from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu -import eos.types from service.fit import Fit from service.market import Market +from eos.saveddata.drone import Drone as es_Drone +from eos.saveddata.fighter import Fighter as es_Fighter +from eos.saveddata.module import Module as es_Module class DummyItem: diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 6a15678e2..ddb7b42d4 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -932,8 +932,8 @@ class ShipBrowser(wx.Panel): if fits: for fit in fits: - shipTrait = fit.ship.traits.traitText if ( - fit.ship.traits is not None) else "" # empty string if no traits + shipTrait = fit.ship.traits.traitText if (fit.ship.traits is not None) else "" + # empty string if no traits self.lpane.AddWidget(FitItem( self.lpane, diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 8a88f9e17..4326cfb47 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -18,8 +18,6 @@ # ============================================================================= import wx -from gui.bitmapLoader import BitmapLoader -import config import dateutil.parser from service import settings diff --git a/gui/utils/animEffects.py b/gui/utils/animEffects.py index 67605919a..a705374a0 100644 --- a/gui/utils/animEffects.py +++ b/gui/utils/animEffects.py @@ -1,4 +1,3 @@ - import math diff --git a/gui/utils/compat.py b/gui/utils/compat.py index bf49eb4aa..e0d336b22 100644 --- a/gui/utils/compat.py +++ b/gui/utils/compat.py @@ -14,6 +14,7 @@ except ImportError: class OrderedDict(dict): 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. # The inherited dict provides __getitem__, __len__, __contains__, and get. # The remaining methods are order-aware. @@ -35,7 +36,7 @@ class OrderedDict(dict): try: self.__root except AttributeError: - self.__root = root = [] # sentinel node + self.__root = root = [] # sentinel node root[:] = [root, root, None] self.__map = {} self.__update(*args, **kwds) diff --git a/gui/utils/drawUtils.py b/gui/utils/drawUtils.py index 91af8444e..559dac395 100644 --- a/gui/utils/drawUtils.py +++ b/gui/utils/drawUtils.py @@ -3,7 +3,6 @@ import gui.utils.colorUtils as colorUtils def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor=None, fillRatio=2): - if sFactor == 0 and eFactor == 0 and mFactor is None: return DrawFilledBitmap(width, height, windowColor) diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index ed9e8bc2b..59b54319d 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -29,7 +29,6 @@ class exportHtml(): class exportHtmlThread(threading.Thread): - def __init__(self, callback=False): threading.Thread.__init__(self) self.callback = callback @@ -192,7 +191,8 @@ class exportHtmlThread(threading.Thread): fit = fits[0] try: dnaFit = sFit.exportDna(fit[0]) - HTMLgroup += '
    • ' + ship.name + ": " + fit[1] + '
    • \n' + HTMLgroup += '
    • ' + ship.name + ": " + \ + fit[1] + '
    • \n' except: pass finally: @@ -203,8 +203,9 @@ class exportHtmlThread(threading.Thread): # Ship group header HTMLship = ( '
    • \n' - '

      ' + ship.name + ' ' + str(len(fits)) + '

      \n' - '
        \n' + '

        ' + ship.name + ' ' + str( + len(fits)) + '

        \n' + '
          \n' ) for fit in fits: @@ -212,7 +213,8 @@ class exportHtmlThread(threading.Thread): return try: dnaFit = sFit.exportDna(fit[0]) - HTMLship += '
        • ' + fit[1] + '
        • \n' + HTMLship += '
        • ' + fit[ + 1] + '
        • \n' except: continue finally: @@ -227,7 +229,7 @@ class exportHtmlThread(threading.Thread): HTML += ( '
        • \n' '

          ' + group.groupName + ' ' + str(groupFits) + '

          \n' - '
            \n' + HTMLgroup + + '
              \n' + HTMLgroup + '
            \n' ' ' ) @@ -263,7 +265,8 @@ class exportHtmlThread(threading.Thread): return try: dnaFit = sFit.exportDna(fit[0]) - HTML += '' + ship.name + ': ' + fit[1] + '
            \n' + HTML += '' + ship.name + ': ' + \ + fit[1] + '
            \n' except: continue finally: diff --git a/gui/utils/numberFormatter.py b/gui/utils/numberFormatter.py index bb1ca210a..cd8140dce 100644 --- a/gui/utils/numberFormatter.py +++ b/gui/utils/numberFormatter.py @@ -34,7 +34,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal # Start from highest possible suffix for key in posOrders: # Find first suitable suffix and check if it's not above highest order - if abs(val) >= 10**key and key <= highest: + if abs(val) >= 10 ** key and key <= highest: mantissa, suffix = val / float(10 ** key), posSuffixMap[key] # Do additional step to eliminate results like 999999 => 1000k # If we're already using our greatest order, we can't do anything useful @@ -49,7 +49,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal # If it is, bail - we already have acceptable results break # Find multiplier to get from one order to another - orderDiff = 10**(prevKey - key) + orderDiff = 10 ** (prevKey - key) # If rounded mantissa according to our specifications is greater than # or equal to multiplier if roundToPrec(mantissa, prec) >= orderDiff: @@ -67,8 +67,8 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal except IndexError: nextKey = 0 # Check if mantissa with next suffix is in range [1, 1000) - if abs(val) < 10**(nextKey) and key >= lowest: - mantissa, suffix = val / float(10**key), negSuffixMap[key] + if abs(val) < 10 ** (nextKey) and key >= lowest: + mantissa, suffix = val / float(10 ** key), negSuffixMap[key] # Do additional step to eliminate results like 0.9999 => 1000m # Check if the key we're potentially switching to is greater than our # upper boundary @@ -76,7 +76,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal # If it is, leave loop with results we already have break # Find the multiplier between current and next order - orderDiff = 10**(nextKey - key) + orderDiff = 10 ** (nextKey - key) # If rounded mantissa according to our specifications is greater than # or equal to multiplier if roundToPrec(mantissa, prec) >= orderDiff: diff --git a/pyfa.py b/pyfa.py index e3364814e..e465b0d5a 100755 --- a/pyfa.py +++ b/pyfa.py @@ -124,7 +124,7 @@ if __name__ == "__main__": import os.path import eos.db - import service.prefetch # noqa + import service.prefetch from gui.mainFrame import MainFrame # Make sure the saveddata db exists diff --git a/service/fit.py b/service/fit.py index 61b8ccc3b..1c4083bde 100644 --- a/service/fit.py +++ b/service/fit.py @@ -667,10 +667,10 @@ class Fit(object): standardAttackActive = True else: # Activate all other abilities (Neut, Web, etc) except propmods if no standard attack is active - if (ability.effect.isImplemented - and standardAttackActive is False - and ability.effect.handlerName != u'fighterabilitymicrowarpdrive' - and ability.effect.handlerName != u'fighterabilityevasivemaneuvers'): + if ability.effect.isImplemented and \ + standardAttackActive is False and \ + ability.effect.handlerName != u'fighterabilitymicrowarpdrive' and \ + ability.effect.handlerName != u'fighterabilityevasivemaneuvers': ability.active = True if used >= total: diff --git a/service/market.py b/service/market.py index 30972795e..c711201b6 100644 --- a/service/market.py +++ b/service/market.py @@ -360,7 +360,16 @@ class Market(): ("complex", frozenset((6,))), ("officer", frozenset((5,)))]) self.SEARCH_CATEGORIES = ( - "Drone", "Module", "Subsystem", "Charge", "Implant", "Deployable", "Fighter", "Structure", "Structure Module") + "Drone", + "Module", + "Subsystem", + "Charge", + "Implant", + "Deployable", + "Fighter", + "Structure", + "Structure Module", + ) self.SEARCH_GROUPS = ("Ice Product",) self.ROOT_MARKET_GROUPS = (9, # Modules 1111, # Rigs @@ -377,7 +386,7 @@ class Market(): @classmethod def getInstance(cls): - if cls.instance == None: + if cls.instance is None: cls.instance = Market() return cls.instance @@ -385,7 +394,7 @@ class Market(): """Creates reverse dictionary""" rev = {} for item, value in orig.items(): - if not value in rev: + if value not in rev: rev[value] = set() rev[value].add(item) return rev @@ -796,7 +805,7 @@ class Market(): def cb(): try: callback(requests) - except Exception, e: + except Exception: pass eos.db.commit() @@ -819,7 +828,7 @@ class Market(): def clearPriceCache(self): self.priceCache.clear() - deleted_rows = eos.db.clearPrices() + eos.db.clearPrices() def getSystemWideEffects(self): """ @@ -862,7 +871,7 @@ class Market(): groupname = re.sub(garbage, "", groupname) groupname = re.sub(" {2,}", " ", groupname).strip() # Add stuff to dictionary - if not groupname in effects: + if groupname not in effects: effects[groupname] = set() effects[groupname].add((beacon, beaconname, shortname)) # Break loop on 1st result diff --git a/service/port.py b/service/port.py index d6645ed23..a15dd0d49 100644 --- a/service/port.py +++ b/service/port.py @@ -35,7 +35,7 @@ from service.fit import Fit import wx -from eos.types import State, Slot, Module, Cargo, Fit, Ship, Drone, Implant, Booster, Citadel +from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel from service.crest import Crest from service.market import Market @@ -823,8 +823,8 @@ class Port(object): slot = module.slot if slot not in stuff: stuff[slot] = [] - curr = module.item.name if module.item else ( - "[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "") + curr = module.item.name if module.item \ + else ("[Empty %s slot]" % Slot.getName(slot).capitalize() if slot is not None else "") if module.charge and sFit.serviceFittingOptions["exportCharges"]: curr += ", %s" % module.charge.name if module.state == State.OFFLINE: diff --git a/service/prefetch.py b/service/prefetch.py index 8d8348322..6e5b668c1 100644 --- a/service/prefetch.py +++ b/service/prefetch.py @@ -32,8 +32,10 @@ class PrefetchThread(threading.Thread): # We're a daemon thread, as such, interpreter might get shut down while we do stuff # Make sure we don't throw tracebacks to console try: - es_Character.setSkillList(db.getItemsByCategory("Skill", eager=( - "effects", "attributes", "attributes.info.icon", "attributes.info.unit", "icon"))) + es_Character.setSkillList(db.getItemsByCategory( + "Skill", + eager=("effects", "attributes", "attributes.info.icon", "attributes.info.unit", "icon") + )) except: pass diff --git a/service/pycrest/weak_ciphers.py b/service/pycrest/weak_ciphers.py index e3a82ca15..48da3c909 100644 --- a/service/pycrest/weak_ciphers.py +++ b/service/pycrest/weak_ciphers.py @@ -102,8 +102,7 @@ class WeakCiphersHTTPSConnection(urllib3.connection.VerifiedHTTPSConnection): # self.is_verified = (resolved_cert_reqs == ssl.CERT_REQUIRED or self.assert_fingerprint is not None) -class WeakCiphersHTTPSConnectionPool( - urllib3.connectionpool.HTTPSConnectionPool): +class WeakCiphersHTTPSConnectionPool(urllib3.connectionpool.HTTPSConnectionPool): ConnectionCls = WeakCiphersHTTPSConnection diff --git a/tox.ini b/tox.ini index 5eae895b9..ad49d6c5b 100644 --- a/tox.ini +++ b/tox.ini @@ -12,4 +12,4 @@ commands = py.test -vv --cov Pyfa tests/ [testenv:pep8] deps = flake8 -commands = flake8 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,venv,tests,.tox,build,dist,__init__.py --ignore=E501,E731 service gui eos utils config.py pyfa.py +commands = flake8 --exclude=.svn,CVS,.bzr,.hg,.git,__pycache__,venv,tests,.tox,build,dist,__init__.py --ignore=E127,E501,E731,F401,F403,F405 service gui eos utils config.py pyfa.py --max-line-length=130 From 622a734405531bb2da0a435c04bd116ea2e9a8ed Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Tue, 13 Dec 2016 23:35:29 -0800 Subject: [PATCH 14/53] remove lazy import. Fix test --- gui/fleetBrowser.py | 4 ++-- tests/test_package.py | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/gui/fleetBrowser.py b/gui/fleetBrowser.py index ab50e58ca..600351eee 100644 --- a/gui/fleetBrowser.py +++ b/gui/fleetBrowser.py @@ -1,7 +1,7 @@ import wx from wx.lib.buttons import GenBitmapButton -import service.fleet +from service.fleet import Fleet import gui.mainFrame import gui.utils.colorUtils as colorUtils import gui.sfBrowserItem as SFItem @@ -23,7 +23,7 @@ class FleetBrowser(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent) - self.sFleet = service.fleet.Fleet.getInstance() + self.sFleet = Fleet.getInstance() self.mainFrame = gui.mainFrame.MainFrame.getInstance() mainSizer = wx.BoxSizer(wx.VERTICAL) diff --git a/tests/test_package.py b/tests/test_package.py index b63076b47..74450c866 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -17,7 +17,6 @@ def test_packages(): assert eos assert utils - def service_modules(): for root, folders, files in os.walk("service"): for file_ in files: @@ -28,7 +27,16 @@ def service_modules(): ) yield mod_name +def eos_modules(): + for root, folders, files in os.walk("eos"): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + yield mod_name -@pytest.mark.parametrize("mod_name", service_modules()) +@pytest.mark.parametrize("mod_name", eos_modules()) def test_service_imports(mod_name): assert importlib.import_module(mod_name) From e3b592977aa4393c24a8280dda4207b98cc988c8 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Tue, 13 Dec 2016 23:49:37 -0800 Subject: [PATCH 15/53] Make some imports more explicit --- gui/builtinContextMenus/targetResists.py | 4 ++-- gui/pygauge.py | 4 ++-- gui/updateDialog.py | 4 ++-- service/market.py | 20 ++++++++++++++------ 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index 823aa0d82..f23125f43 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -3,7 +3,7 @@ import gui.mainFrame import gui.globalEvents as GE import wx from gui.bitmapLoader import BitmapLoader -from service import targetResists as svc_targetResists +from service.targetResists import TargetResists as svc_TargetResists from service.fit import Fit try: @@ -20,7 +20,7 @@ class TargetResists(ContextMenu): if self.mainFrame.getActiveFit() is None or srcContext != "firepowerViewFull": return False - sTR = svc_targetResists.TargetResists.getInstance() + sTR = svc_TargetResists.getInstance() self.patterns = sTR.getTargetResistsList() self.patterns.sort(key=lambda p: (p.name in ["None"], p.name)) diff --git a/gui/pygauge.py b/gui/pygauge.py index 0946e8727..602304e13 100644 --- a/gui/pygauge.py +++ b/gui/pygauge.py @@ -19,7 +19,7 @@ import gui.utils.drawUtils as drawUtils import gui.utils.animEffects as animEffects import gui.utils.fonts as fonts -from service import fit +from service.fit import Fit class PyGauge(wx.PyWindow): @@ -173,7 +173,7 @@ class PyGauge(wx.PyWindow): return self._range def Animate(self): - sFit = fit.Fit.getInstance() + sFit = Fit.getInstance() if sFit.serviceFittingOptions["enableGaugeAnimation"]: if not self._timer: self._timer = wx.Timer(self, self._timerId) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 4326cfb47..155d5a6a9 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -19,7 +19,7 @@ import wx import dateutil.parser -from service import settings +from service.settings import UpdateSettings as svc_UpdateSettings class UpdateDialog(wx.Dialog): @@ -27,7 +27,7 @@ class UpdateDialog(wx.Dialog): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Pyfa Update", pos=wx.DefaultPosition, size=wx.Size(400, 300), style=wx.DEFAULT_DIALOG_STYLE) - self.UpdateSettings = settings.UpdateSettings.getInstance() + self.UpdateSettings = svc_UpdateSettings.getInstance() self.releaseInfo = release self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) diff --git a/service/market.py b/service/market.py index c711201b6..8bd49e9cd 100644 --- a/service/market.py +++ b/service/market.py @@ -27,7 +27,8 @@ from sqlalchemy.sql import or_ import config import eos.db -from service import conversions +# TODO: Find out what this is. There is no conversions +# from service import conversions from service.settings import SettingsProvider from service.price import Price @@ -257,9 +258,12 @@ class Market(): "Civilian Light Electron Blaster": True, } + # TODO: Find out what this is. There is no conversions # do not publish ships that we convert + ''' for name in conversions.packs['skinnedShips']: self.ITEMS_FORCEPUBLISHED[name] = False + ''' if config.debug: # Publish Tactical Dessy Modes if in debug @@ -406,11 +410,15 @@ class Market(): item = identity elif isinstance(identity, int): item = eos.db.getItem(identity, *args, **kwargs) - elif isinstance(identity, basestring): - # We normally lookup with string when we are using import/export - # features. Check against overrides - identity = conversions.all.get(identity, identity) - item = eos.db.getItem(identity, *args, **kwargs) + # TODO: Find out what this is. There is no conversions + """ + # Unindent 1 tab + elif isinstance(identity, basestring): + # We normally lookup with string when we are using import/export + # features. Check against overrides + identity = conversions.all.get(identity, identity) + item = eos.db.getItem(identity, *args, **kwargs) + """ elif isinstance(identity, float): id = int(identity) item = eos.db.getItem(id, *args, **kwargs) From 627977ab515fe134c5f4b69b4a8e4cea7305716a Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Sep 2016 15:07:59 -0700 Subject: [PATCH 16/53] Purging fleet bonuses from code base (cherry picked from commit 68f4570) --- eos/db/saveddata/fleet.py | 47 +--- eos/db/saveddata/queries.py | 67 ----- eos/types.py | 1 - gui/builtinViews/fleetView.py | 147 +---------- gui/fleetBrowser.py | 450 +--------------------------------- gui/gangView.py | 409 +----------------------------- service/fit.py | 13 +- service/fleet.py | 200 +-------------- 8 files changed, 7 insertions(+), 1327 deletions(-) diff --git a/eos/db/saveddata/fleet.py b/eos/db/saveddata/fleet.py index eb644b02e..dc1c06223 100644 --- a/eos/db/saveddata/fleet.py +++ b/eos/db/saveddata/fleet.py @@ -17,49 +17,4 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy import Table, Column, Integer, ForeignKey, String -from sqlalchemy.orm import mapper, relation - -from eos.db import saveddata_meta -from eos.db.saveddata.fit import fits_table -from eos.types import Fleet, Wing, Squad, Fit - -gangs_table = Table("gangs", saveddata_meta, - Column("ID", Integer, primary_key=True), - Column("leaderID", ForeignKey("fits.ID")), - Column("boosterID", ForeignKey("fits.ID")), - Column("name", String)) - -wings_table = Table("wings", saveddata_meta, - Column("ID", Integer, primary_key=True), - Column("gangID", ForeignKey("gangs.ID")), - Column("boosterID", ForeignKey("fits.ID")), - Column("leaderID", ForeignKey("fits.ID"))) - -squads_table = Table("squads", saveddata_meta, - Column("ID", Integer, primary_key=True), - Column("wingID", ForeignKey("wings.ID")), - Column("leaderID", ForeignKey("fits.ID")), - Column("boosterID", ForeignKey("fits.ID"))) - -squadmembers_table = Table("squadmembers", saveddata_meta, - Column("squadID", ForeignKey("squads.ID"), primary_key=True), - Column("memberID", ForeignKey("fits.ID"), primary_key=True)) - -mapper(Fleet, gangs_table, - properties={"wings": relation(Wing, backref="gang"), - "leader": relation(Fit, primaryjoin=gangs_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin=gangs_table.c.boosterID == fits_table.c.ID)}) - -mapper(Wing, wings_table, - properties={"squads": relation(Squad, backref="wing"), - "leader": relation(Fit, primaryjoin=wings_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin=wings_table.c.boosterID == fits_table.c.ID)}) - -mapper(Squad, squads_table, - properties={"leader": relation(Fit, primaryjoin=squads_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin=squads_table.c.boosterID == fits_table.c.ID), - "members": relation(Fit, - primaryjoin=squads_table.c.ID == squadmembers_table.c.squadID, - secondaryjoin=squadmembers_table.c.memberID == fits_table.c.ID, - secondary=squadmembers_table)}) +#Purging fleet \ No newline at end of file diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 07060d7c9..84633b5a8 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -21,7 +21,6 @@ from sqlalchemy.sql import and_ from eos.db import saveddata_session, sd_lock from eos.db.saveddata.fit import projectedFits_table -from eos.db.saveddata.fleet import squadmembers_table from eos.db.util import processEager, processWhere from eos.types import * @@ -209,52 +208,6 @@ def getFit(lookfor, eager=None): return fit - -@cachedQuery(Fleet, 1, "fleetID") -def getFleet(fleetID, eager=None): - if isinstance(fleetID, int): - if eager is None: - with sd_lock: - fleet = saveddata_session.query(Fleet).get(fleetID) - else: - eager = processEager(eager) - with sd_lock: - fleet = saveddata_session.query(Fleet).options(*eager).filter(Fleet.ID == fleetID).first() - else: - raise TypeError("Need integer as argument") - return fleet - - -@cachedQuery(Wing, 1, "wingID") -def getWing(wingID, eager=None): - if isinstance(wingID, int): - if eager is None: - with sd_lock: - wing = saveddata_session.query(Wing).get(wingID) - else: - eager = processEager(eager) - with sd_lock: - wing = saveddata_session.query(Wing).options(*eager).filter(Wing.ID == wingID).first() - else: - raise TypeError("Need integer as argument") - return wing - - -@cachedQuery(Squad, 1, "squadID") -def getSquad(squadID, eager=None): - if isinstance(squadID, int): - if eager is None: - with sd_lock: - squad = saveddata_session.query(Squad).get(squadID) - else: - eager = processEager(eager) - with sd_lock: - squad = saveddata_session.query(Squad).options(*eager).filter(Fleet.ID == squadID).first() - else: - raise TypeError("Need integer as argument") - return squad - - def getFitsWithShip(shipID, ownerID=None, where=None, eager=None): """ Get all the fits using a certain ship. @@ -331,14 +284,6 @@ def getFitList(eager=None): return fits - -def getFleetList(eager=None): - eager = processEager(eager) - with sd_lock: - fleets = saveddata_session.query(Fleet).options(*eager).all() - return fleets - - @cachedQuery(Price, 1, "typeID") def getPrice(typeID): if isinstance(typeID, int): @@ -462,18 +407,6 @@ def searchFits(nameLike, where=None, eager=None): return fits - -def getSquadsIDsWithFitID(fitID): - if isinstance(fitID, int): - with sd_lock: - squads = saveddata_session.query(squadmembers_table.c.squadID).filter( - squadmembers_table.c.memberID == fitID).all() - squads = tuple(entry[0] for entry in squads) - return squads - else: - raise TypeError("Need integer as argument") - - def getProjectedFits(fitID): if isinstance(fitID, int): with sd_lock: diff --git a/eos/types.py b/eos/types.py index d42cd0fe8..1fbf2676b 100644 --- a/eos/types.py +++ b/eos/types.py @@ -39,7 +39,6 @@ from eos.saveddata.implantSet import ImplantSet from eos.saveddata.booster import Booster from eos.saveddata.fit import Fit, ImplantLocation from eos.saveddata.mode import Mode -from eos.saveddata.fleet import Fleet, Wing, Squad from eos.saveddata.miscData import MiscData from eos.saveddata.override import Override diff --git a/gui/builtinViews/fleetView.py b/gui/builtinViews/fleetView.py index 466407b7e..9058bc35f 100644 --- a/gui/builtinViews/fleetView.py +++ b/gui/builtinViews/fleetView.py @@ -1,146 +1 @@ -import wx.gizmos -import gui.fleetBrowser -from gui.bitmapLoader import BitmapLoader -from service.fleet import Fleet - - -# Tab spawning handler -class FleetSpawner(gui.multiSwitch.TabSpawner): - def __init__(self, multiSwitch): - self.multiSwitch = multiSwitch - mainFrame = gui.mainFrame.MainFrame.getInstance() - mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_SELECTED, self.fleetSelected) - - def fleetSelected(self, event): - if self.multiSwitch.GetPageCount() == 0: - self.multiSwitch.AddPage(wx.Panel(self.multiSwitch, size=(0, 0)), "Empty Tab") - - view = FleetView(self.multiSwitch) - self.multiSwitch.ReplaceActivePage(view) - view.populate(event.fleetID) - view.Show() - - -FleetSpawner.register() - - -class FleetView(wx.gizmos.TreeListCtrl): - def __init__(self, parent, size=(0, 0)): - wx.gizmos.TreeListCtrl.__init__(self, parent, size=size) - - self.tabManager = parent - - self.fleetId = None - self.fleetImg = BitmapLoader.getImage("53_16", "icons") - - self.imageList = wx.ImageList(16, 16) - self.SetImageList(self.imageList) - - for col in ("", "Fit", "Shiptype", "Character", "Bonusses"): - self.AddColumn(col) - - self.SetMainColumn(1) - self.icons = {} - self.addImage = self.imageList.Add(BitmapLoader.getBitmap("add_small", "gui")) - for icon in ("fb", "fc", "sb", "sc", "wb", "wc"): - self.icons[icon] = self.imageList.Add(BitmapLoader.getBitmap("fleet_%s_small" % icon, "gui")) - - self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.checkNew) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_RENAMED, self.fleetRenamed) - self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_REMOVED, self.fleetRemoved) - - def Destroy(self): - self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_REMOVED, handler=self.fleetRemoved) - self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_RENAMED, handler=self.fleetRenamed) - wx.gizmos.TreeListCtrl.Destroy(self) - - def fleetRenamed(self, event): - if event.fleetID == self.fleetId: - sFleet = Fleet.getInstance() - f = sFleet.getFleetByID(event.fleetID) - self.UpdateTab(f.name, self.fleetImg) - - event.Skip() - - def fleetRemoved(self, event): - if event.fleetID == self.fleetId: - self.tabManager.DeletePage(self.tabManager.GetPageIndex(self)) - - event.Skip() - - def checkNew(self, event): - self.GetPyData(event.Item) - - # TODO: Doesn't seem to be used - ''' - data = self.GetPyData(event.Item) - if data and isinstance(data, tuple) and data[0] == "add": - layer = data[1] - ''' - - def UpdateTab(self, name, img): - self.tabManager.SetPageTextIcon(self.tabManager.GetSelection(), name, img) - - def populate(self, fleetID): - sFleet = Fleet.getInstance() - f = sFleet.getFleetByID(fleetID) - self.fleetId = fleetID - - self.UpdateTab(f.name, self.fleetImg) - self.fleet = f - self.DeleteAllItems() - root = self.AddRoot("") - - self.setEntry(root, f.leader, "fleet", f) - for wing in f.wings: - wingId = self.AppendItem(root, "") - self.setEntry(wingId, wing.leader, "wing", wing) - for squad in wing.squads: - for member in squad.members: - memberId = self.AppendItem(wingId, "") - self.setEntry(memberId, member, "squad", squad) - - self.addAdder(wingId, "squad") - - self.addAdder(root, "wing") - - self.ExpandAll(root) - self.SetColumnWidth(0, 16) - for i in range(1, 5): - self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) - headerWidth = self.GetColumnWidth(i) + 5 - self.SetColumnWidth(i, wx.LIST_AUTOSIZE) - baseWidth = self.GetColumnWidth(i) - if baseWidth < headerWidth: - self.SetColumnWidth(i, headerWidth) - else: - self.SetColumnWidth(i, baseWidth) - - def addAdder(self, treeItemId, layer): - id_ = self.AppendItem(treeItemId, "Add new %s" % layer.capitalize()) - self.SetPyData(id_, ("add", layer)) - self.SetItemImage(id_, self.addImage, 1) - - def setEntry(self, treeItemId, fit, layer, info): - self.SetPyData(treeItemId, info) - if fit is None: - self.SetItemText(treeItemId, "%s Commander" % layer.capitalize(), 1) - else: - fleet = self.fleet - if fit == info.booster: - self.SetItemImage(treeItemId, self.icons["%sb" % layer[0]], 0) - elif fit == info.leader: - self.SetItemImage(treeItemId, self.icons["%sc" % layer[0]], 1) - - self.SetItemText(treeItemId, fit.name, 1) - self.SetItemText(treeItemId, fit.ship.item.name, 2) - self.SetItemText(treeItemId, fit.character.name, 3) - boosts = fleet.store.getBoosts(fit) - if boosts: - bonusses = [] - for name, info in boosts.iteritems(): - bonusses.append("%s: %.2g" % (name, info[0])) - - self.SetItemText(treeItemId, ", ".join(bonusses), 3) +#Purge fleet boosts \ No newline at end of file diff --git a/gui/fleetBrowser.py b/gui/fleetBrowser.py index 600351eee..bb28be251 100644 --- a/gui/fleetBrowser.py +++ b/gui/fleetBrowser.py @@ -1,449 +1 @@ -import wx -from wx.lib.buttons import GenBitmapButton - -from service.fleet import Fleet -import gui.mainFrame -import gui.utils.colorUtils as colorUtils -import gui.sfBrowserItem as SFItem -from gui.bitmapLoader import BitmapLoader -from gui.PFListPane import PFListPane -from gui.utils.drawUtils import GetPartialText - -FleetSelected, EVT_FLEET_SELECTED = wx.lib.newevent.NewEvent() -FleetRenamed, EVT_FLEET_RENAMED = wx.lib.newevent.NewEvent() -FleetRemoved, EVT_FLEET_REMOVED = wx.lib.newevent.NewEvent() -FleetItemSelect, EVT_FLEET_ITEM_SELECT = wx.lib.newevent.NewEvent() -FleetItemDelete, EVT_FLEET_ITEM_DELETE = wx.lib.newevent.NewEvent() -FleetItemNew, EVT_FLEET_ITEM_NEW = wx.lib.newevent.NewEvent() -FleetItemCopy, EVT_FLEET_ITEM_COPY = wx.lib.newevent.NewEvent() -FleetItemRename, EVT_FLEET_ITEM_RENAME = wx.lib.newevent.NewEvent() - - -class FleetBrowser(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - - self.sFleet = Fleet.getInstance() - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - mainSizer = wx.BoxSizer(wx.VERTICAL) - - self.hpane = FleetBrowserHeader(self) - mainSizer.Add(self.hpane, 0, wx.EXPAND) - - self.m_sl2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - mainSizer.Add(self.m_sl2, 0, wx.EXPAND, 0) - - self.fleetItemContainer = PFFleetItemContainer(self) - - mainSizer.Add(self.fleetItemContainer, 1, wx.EXPAND) - - self.SetSizer(mainSizer) - self.Layout() - - self.filter = "" - self.fleetIDMustEditName = -1 - - self.Bind(wx.EVT_SIZE, self.SizeRefreshList) - - self.Bind(EVT_FLEET_ITEM_NEW, self.AddNewFleetItem) - self.Bind(EVT_FLEET_ITEM_SELECT, self.SelectFleetItem) - self.Bind(EVT_FLEET_ITEM_DELETE, self.DeleteFleetItem) - self.Bind(EVT_FLEET_ITEM_COPY, self.CopyFleetItem) - self.Bind(EVT_FLEET_ITEM_RENAME, self.RenameFleetItem) - - self.PopulateFleetList() - - def AddNewFleetItem(self, event): - fleetName = event.fleetName - newFleet = self.sFleet.addFleet() - self.sFleet.renameFleet(newFleet, fleetName) - - self.fleetIDMustEditName = newFleet.ID - self.AddItem(newFleet.ID, newFleet.name, newFleet.count()) - - def SelectFleetItem(self, event): - fleetID = event.fleetID - self.fleetItemContainer.SelectWidgetByFleetID(fleetID) - wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleetID)) - - def CopyFleetItem(self, event): - fleetID = event.fleetID - fleet = self.sFleet.copyFleetByID(fleetID) - - fleetName = fleet.name + " Copy" - self.sFleet.renameFleet(fleet, fleetName) - - self.fleetIDMustEditName = fleet.ID - self.AddItem(fleet.ID, fleet.name, fleet.count()) - - self.fleetItemContainer.SelectWidgetByFleetID(fleet.ID) - wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleet.ID)) - - def RenameFleetItem(self, event): - fleetID = event.fleetID - fleet = self.sFleet.getFleetByID(fleetID) - - newFleetName = event.fleetName - - self.sFleet.renameFleet(fleet, newFleetName) - wx.PostEvent(self.mainFrame, FleetRenamed(fleetID=fleet.ID)) - - def DeleteFleetItem(self, event): - self.sFleet.deleteFleetByID(event.fleetID) - self.PopulateFleetList() - wx.PostEvent(self.mainFrame, FleetRemoved(fleetID=event.fleetID)) - - def AddItem(self, ID, name, count): - self.fleetItemContainer.AddWidget(FleetItem(self, ID, name, count)) - widget = self.fleetItemContainer.GetWidgetByFleetID(ID) - self.fleetItemContainer.RefreshList(True) - self.fleetItemContainer.ScrollChildIntoView(widget) - wx.PostEvent(self, FleetItemSelect(fleetID=ID)) - - def PopulateFleetList(self): - self.Freeze() - filter_ = self.filter - self.fleetItemContainer.RemoveAllChildren() - fleetList = self.sFleet.getFleetList() - for fleetID, fleetName, fleetCount in fleetList: - if fleetName.lower().find(filter_.lower()) != -1: - self.fleetItemContainer.AddWidget(FleetItem(self, fleetID, fleetName, fleetCount)) - self.fleetItemContainer.RefreshList() - self.Thaw() - - def SetFilter(self, filter): - self.filter = filter - - def SizeRefreshList(self, event): - ewidth, eheight = event.GetSize() - self.Layout() - self.fleetItemContainer.Layout() - self.fleetItemContainer.RefreshList(True) - event.Skip() - - -class FleetBrowserHeader(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 24), - style=wx.TAB_TRAVERSAL) - self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) - - self.newBmp = BitmapLoader.getBitmap("fit_add_small", "gui") - bmpSize = (16, 16) - - mainSizer = wx.BoxSizer(wx.HORIZONTAL) - - if 'wxMac' in wx.PlatformInfo: - bgcolour = wx.Colour(0, 0, 0, 0) - else: - bgcolour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) - - self.fbNewFleet = PFGenBitmapButton(self, wx.ID_ANY, self.newBmp, wx.DefaultPosition, bmpSize, wx.BORDER_NONE) - mainSizer.Add(self.fbNewFleet, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL, 5) - self.fbNewFleet.SetBackgroundColour(bgcolour) - - self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL) - mainSizer.Add(self.sl1, 0, wx.EXPAND | wx.LEFT, 5) - - self.tcFilter = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) - mainSizer.Add(self.tcFilter, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - - self.stStatus = wx.StaticText(self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0) - self.stStatus.Wrap(-1) - mainSizer.Add(self.stStatus, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5) - - self.SetSizer(mainSizer) - self.Layout() - - self.fbNewFleet.Bind(wx.EVT_ENTER_WINDOW, self.fbNewEnterWindow) - self.fbNewFleet.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) - self.fbNewFleet.Bind(wx.EVT_BUTTON, self.OnNewFleetItem) - - self.tcFilter.Bind(wx.EVT_TEXT, self.OnFilterText) - - self.tcFilter.Bind(wx.EVT_ENTER_WINDOW, self.fbFilterEnterWindow) - self.tcFilter.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) - - def OnFilterText(self, event): - filter = self.tcFilter.GetValue() - self.Parent.SetFilter(filter) - self.Parent.PopulateFleetList() - event.Skip() - - def OnNewFleetItem(self, event): - wx.PostEvent(self.Parent, FleetItemNew(fleetName="New Fleet")) - - def fbNewEnterWindow(self, event): - self.stStatus.SetLabel("New fleet") - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) - event.Skip() - - def fbHItemLeaveWindow(self, event): - self.stStatus.SetLabel("") - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) - event.Skip() - - def fbFilterEnterWindow(self, event): - self.stStatus.SetLabel("Filter list") - event.Skip() - - -class PFFleetItemContainer(PFListPane): - def __init__(self, parent): - PFListPane.__init__(self, parent) - self.selectedWidget = -1 - self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) - - def IsWidgetSelectedByContext(self, widget): - if self.GetWidgetList()[widget].IsSelected(): - return True - return False - - def GetWidgetIndex(self, widgetWnd): - return self.GetWidgetList().index(widgetWnd) - - def GetWidgetByFleetID(self, fleetID): - for widget in self.GetWidgetList(): - if widget.fleetID == fleetID: - return widget - return None - - def SelectWidget(self, widgetWnd): - wlist = self.GetWidgetList() - if self.selectedWidget != -1: - wlist[self.selectedWidget].SetSelected(False) - wlist[self.selectedWidget].Refresh() - windex = self.GetWidgetIndex(widgetWnd) - wlist[windex].SetSelected(True) - wlist[windex].Refresh() - self.selectedWidget = windex - - def SelectWidgetByFleetID(self, fleetID): - widgetWnd = self.GetWidgetByFleetID(fleetID) - if widgetWnd: - self.SelectWidget(widgetWnd) - - def RemoveWidget(self, child): - child.Destroy() - self.selectedWidget = -1 - self._wList.remove(child) - - def RemoveAllChildren(self): - for widget in self._wList: - widget.Destroy() - - self.selectedWidget = -1 - self._wList = [] - - def OnLeftUp(self, event): - event.Skip() - - -class FleetItem(SFItem.SFBrowserItem): - def __init__(self, parent, fleetID, fleetName, fleetCount, - id=wx.ID_ANY, pos=wx.DefaultPosition, - size=(0, 40), style=0): - SFItem.SFBrowserItem.__init__(self, parent, size=size) - - self.fleetBrowser = self.Parent - self.fleetID = fleetID - self.fleetName = fleetName - self.fleetCount = fleetCount - - self.padding = 4 - - self.fontBig = wx.FontFromPixelSize((0, 15), wx.SWISS, wx.NORMAL, wx.BOLD, False) - self.fontNormal = wx.FontFromPixelSize((0, 14), wx.SWISS, wx.NORMAL, wx.NORMAL, False) - self.fontSmall = wx.FontFromPixelSize((0, 12), wx.SWISS, wx.NORMAL, wx.NORMAL, False) - - self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") - self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") - self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small", "gui") - self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") - self.fleetBmp = BitmapLoader.getBitmap("fleet_item_big", "gui") - - fleetImg = self.fleetBmp.ConvertToImage() - fleetImg = fleetImg.Blur(2) - - if not fleetImg.HasAlpha(): - fleetImg.InitAlpha() - - fleetImg = fleetImg.AdjustChannels(1, 1, 1, 0.5) - self.fleetEffBmp = wx.BitmapFromImage(fleetImg) - - self.toolbar.AddButton(self.copyBmp, "Copy", self.CopyFleetCB) - self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.RenameFleetCB) - self.toolbar.AddButton(self.deleteBmp, "Delete", self.DeleteFleetCB) - - self.editWidth = 150 - self.tcFleetName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fleetName, wx.DefaultPosition, (self.editWidth, -1), - wx.TE_PROCESS_ENTER) - - if self.fleetBrowser.fleetIDMustEditName != self.fleetID: - self.tcFleetName.Show(False) - else: - self.tcFleetName.SetFocus() - self.tcFleetName.SelectAll() - self.fleetBrowser.fleetIDMustEditName = -1 - self.renameBtn.SetBitmap(self.acceptBmp) - self.selected = True - - self.tcFleetName.Bind(wx.EVT_KILL_FOCUS, self.OnEditLostFocus) - self.tcFleetName.Bind(wx.EVT_TEXT_ENTER, self.RenameFleet) - self.tcFleetName.Bind(wx.EVT_KEY_DOWN, self.EditCheckEsc) - - self.animCount = 0 - - def MouseLeftUp(self, event): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - else: - wx.PostEvent(self.fleetBrowser, FleetItemSelect(fleetID=self.fleetID)) - - def CopyFleetCB(self): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - return - - wx.PostEvent(self.fleetBrowser, FleetItemCopy(fleetID=self.fleetID)) - - def RenameFleetCB(self): - - if self.tcFleetName.IsShown(): - - self.RenameFleet(None) - self.RestoreEditButton() - - else: - self.tcFleetName.SetValue(self.fleetName) - self.tcFleetName.Show() - - self.renameBtn.SetBitmap(self.acceptBmp) - self.Refresh() - - self.tcFleetName.SetFocus() - self.tcFleetName.SelectAll() - - self.Refresh() - - def RenameFleet(self, event): - - newFleetName = self.tcFleetName.GetValue() - self.fleetName = newFleetName - - self.tcFleetName.Show(False) - - wx.PostEvent(self.fleetBrowser, FleetItemRename(fleetID=self.fleetID, fleetName=self.fleetName)) - self.Refresh() - - def DeleteFleetCB(self): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - return - wx.PostEvent(self.fleetBrowser, FleetItemDelete(fleetID=self.fleetID)) - - def RestoreEditButton(self): - self.tcFleetName.Show(False) - self.renameBtn.SetBitmap(self.renameBmp) - self.Refresh() - - def OnEditLostFocus(self, event): - self.RestoreEditButton() - self.Refresh() - - def EditCheckEsc(self, event): - if event.GetKeyCode() == wx.WXK_ESCAPE: - self.RestoreEditButton() - else: - event.Skip() - - def IsSelected(self): - return self.selected - - def UpdateElementsPos(self, mdc): - rect = self.GetRect() - - self.toolbarx = rect.width - self.toolbar.GetWidth() - self.padding - self.toolbary = (rect.height - self.toolbar.GetHeight()) / 2 - - self.toolbarx = self.toolbarx + self.animCount - - self.fleetBmpx = self.padding + (rect.height - self.fleetBmp.GetWidth()) / 2 - self.fleetBmpy = (rect.height - self.fleetBmp.GetHeight()) / 2 - - self.fleetBmpx -= self.animCount - - self.textStartx = self.fleetBmpx + self.fleetBmp.GetWidth() + self.padding - - self.fleetNamey = (rect.height - self.fleetBmp.GetHeight()) / 2 - - mdc.SetFont(self.fontBig) - wtext, htext = mdc.GetTextExtent(self.fleetName) - - self.fleetCounty = self.fleetNamey + htext - - mdc.SetFont(self.fontSmall) - - wlabel, hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) - - self.thoverx = self.toolbarx - self.padding - wlabel - self.thovery = (rect.height - hlabel) / 2 - self.thoverw = wlabel - - def DrawItem(self, mdc): - # rect = self.GetRect() - - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) - - mdc.SetTextForeground(textColor) - - self.UpdateElementsPos(mdc) - - self.toolbar.SetPosition((self.toolbarx, self.toolbary)) - mdc.DrawBitmap(self.fleetEffBmp, self.fleetBmpx + 3, self.fleetBmpy + 2) - mdc.DrawBitmap(self.fleetBmp, self.fleetBmpx, self.fleetBmpy) - - mdc.SetFont(self.fontNormal) - - suffix = "%d ships" % self.fleetCount if self.fleetCount > 1 else "%d ship" % self.fleetCount if self.fleetCount == 1 else "No ships" - fleetCount = "Fleet size: %s" % suffix - fleetCount = GetPartialText(mdc, fleetCount, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) - - mdc.DrawText(fleetCount, self.textStartx, self.fleetCounty) - - mdc.SetFont(self.fontSmall) - mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) - - mdc.SetFont(self.fontBig) - - pfname = GetPartialText(mdc, self.fleetName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) - mdc.DrawText(pfname, self.textStartx, self.fleetNamey) - - if self.tcFleetName.IsShown(): - self.AdjustControlSizePos(self.tcFleetName, self.textStartx, self.toolbarx - self.editWidth - self.padding) - - def AdjustControlSizePos(self, editCtl, start, end): - fnEditSize = editCtl.GetSize() - wSize = self.GetSize() - fnEditPosX = end - fnEditPosY = (wSize.height - fnEditSize.height) / 2 - if fnEditPosX < start: - editCtl.SetSize((self.editWidth + fnEditPosX - start, -1)) - editCtl.SetPosition((start, fnEditPosY)) - else: - editCtl.SetSize((self.editWidth, -1)) - editCtl.SetPosition((fnEditPosX, fnEditPosY)) - - -class PFGenBitmapButton(GenBitmapButton): - def __init__(self, parent, id, bitmap, pos, size, style): - GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style) - self.bgcolor = wx.Brush(wx.WHITE) - - def SetBackgroundColour(self, color): - self.bgcolor = wx.Brush(color) - - def GetBackgroundBrush(self, dc): - return self.bgcolor +# purging fleet \ No newline at end of file diff --git a/gui/gangView.py b/gui/gangView.py index 93e48c117..7415ea49e 100644 --- a/gui/gangView.py +++ b/gui/gangView.py @@ -17,411 +17,4 @@ # along with pyfa. If not, see . # ============================================================================= -import wx -from wx.lib.scrolledpanel import ScrolledPanel - -from service.fit import Fit -from service.fleet import Fleet -from service.character import Character -from service.market import Market - -import gui.mainFrame -import gui.shipBrowser -import gui.globalEvents as GE - - -class GangView(ScrolledPanel): - def __init__(self, parent): - ScrolledPanel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(100, 20), - style=wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL) - mainSizer = wx.BoxSizer(wx.VERTICAL) - - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - self.draggedFitID = None - - help_msg = '''Set fit as booster to display in dropdown, or drag fitting from\nship browser to this window, or right click fit and select booster role.''' - helpSizer = wx.BoxSizer(wx.HORIZONTAL) - self.helpText = wx.StaticText(self, wx.ID_ANY, help_msg, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE) - helpSizer.Add(self.helpText, 1, wx.ALL, 5) - - self.options = ["Fleet", "Wing", "Squad"] - - self.fleet = {} - for id_, option in enumerate(self.options): - # set content for each commander - self.fleet[id_] = {} - self.fleet[id_]['stLabel'] = wx.StaticText(self, wx.ID_ANY, self.options[id_] + ':', wx.DefaultPosition, - wx.DefaultSize, 0) - self.fleet[id_]['stText'] = wx.StaticText(self, wx.ID_ANY, 'None', wx.DefaultPosition, wx.DefaultSize, 0) - self.fleet[id_]['chFit'] = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) - self.fleet[id_]['chChar'] = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) - self.fleet[id_]['fitSizer'] = wx.BoxSizer(wx.VERTICAL) - - self.FitDNDPopupMenu = self.buildBoostermenu() - - contentFGSizer = wx.FlexGridSizer(5, 3, 0, 0) - contentFGSizer.AddGrowableCol(1) - contentFGSizer.SetFlexibleDirection(wx.BOTH) - contentFGSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - - # Header - self.stBooster = wx.StaticText(self, wx.ID_ANY, u"Booster", wx.DefaultPosition, wx.DefaultSize, 0) - self.stBooster.Wrap(-1) - self.stBooster.SetFont(wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) - contentFGSizer.Add(self.stBooster, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL, 5) - - self.stFits = wx.StaticText(self, wx.ID_ANY, u"Fits", wx.DefaultPosition, wx.DefaultSize, 0) - self.stFits.Wrap(-1) - self.stFits.SetFont(wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) - contentFGSizer.Add(self.stFits, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL, 5) - - self.stCharacters = wx.StaticText(self, wx.ID_ANY, u"Characters", wx.DefaultPosition, wx.DefaultSize, 0) - self.stCharacters.Wrap(-1) - self.stCharacters.SetFont(wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) - contentFGSizer.Add(self.stCharacters, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_CENTER_HORIZONTAL, 5) - - self.m_staticline2 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - contentFGSizer.Add(self.m_staticline2, 0, wx.EXPAND, 5) - - self.m_staticline3 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - contentFGSizer.Add(self.m_staticline3, 0, wx.EXPAND, 5) - - self.m_staticline4 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - contentFGSizer.Add(self.m_staticline4, 0, wx.EXPAND, 5) - - # Content - for id_ in self.fleet: - # set various properties - self.fleet[id_]['stLabel'].Wrap(-1) - self.fleet[id_]['stLabel'].SetFont( - wx.Font(wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString)) - self.fleet[id_]['stText'].Wrap(-1) - - # bind text and choice events - self.fleet[id_]['stText'].Bind(wx.EVT_LEFT_DCLICK, self.RemoveBooster) - self.fleet[id_]['stText'].Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow) - self.fleet[id_]['stText'].Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - self.fleet[id_]['stText'].SetToolTip(wx.ToolTip("Double click to remove booster")) - self.fleet[id_]['chChar'].Bind(wx.EVT_CHOICE, self.CharChanged) - self.fleet[id_]['chFit'].Bind(wx.EVT_CHOICE, self.OnFitChoiceSelected) - - # add fit text and choice to the fit sizer - self.fleet[id_]['fitSizer'].Add(self.fleet[id_]['stText'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) - self.fleet[id_]['fitSizer'].Add(self.fleet[id_]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, - 1) - - # add everything to the content sizer - contentFGSizer.Add(self.fleet[id_]['stLabel'], 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) - contentFGSizer.Add(self.fleet[id_]['fitSizer'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.EXPAND, 5) - contentFGSizer.Add(self.fleet[id_]['chChar'], 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) - - mainSizer.Add(contentFGSizer, 1, wx.EXPAND, 0) - mainSizer.Add(helpSizer, 0, wx.EXPAND, 0) - - self.SetSizer(mainSizer) - self.SetAutoLayout(True) - self.SetupScrolling() - - self.mainFrame.Bind(GE.CHAR_LIST_UPDATED, self.RefreshCharacterList) - self.mainFrame.Bind(GE.FIT_CHANGED, self.fitSelected) - self.mainFrame.Bind(gui.shipBrowser.EVT_FIT_RENAMED, self.fitRenamed) - self.mainFrame.Bind(gui.shipBrowser.BOOSTER_LIST_UPDATED, self.RefreshBoosterFits) - - self.RefreshBoosterFits() - self.RefreshCharacterList() - - def buildBoostermenu(self): - menu = wx.Menu() - - for option in self.options: - item = menu.Append(-1, option) - # We bind it to the mainFrame because it may be called from either this class or from FitItem via shipBrowser - self.mainFrame.Bind(wx.EVT_MENU, self.OnPopupItemSelected, item) - return menu - - def OnEnterWindow(self, event): - obj = event.GetEventObject() - obj.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) - event.Skip() - - def OnLeaveWindow(self, event): - obj = event.GetEventObject() - obj.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) - event.Skip() - - def CharChanged(self, event): - ''' Change booster character ''' - chBooster = event.GetEventObject() - - type_ = -1 - for id_ in self.fleet: - if chBooster == self.fleet[id_]['chChar']: - type_ = id_ - - if type_ == -1: - event.Skip() - return - - sFit = Fit.getInstance() - - fleetSrv = Fleet.getInstance() - - activeFitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(activeFitID) - - # sChar = Character.getInstance() - # charList = sChar.getCharacterList() - - if activeFitID: - commanders = fleetSrv.loadLinearFleet(fit) - if commanders is None: - fleetCom, wingCom, squadCom = (None, None, None) - else: - fleetCom, wingCom, squadCom = commanders - - if type_ == 0: - if fleetCom: - charID = chBooster.GetClientData(chBooster.GetSelection()) - sFit.changeChar(fleetCom.ID, charID) - else: - chBooster.SetSelection(0) - - if type_ == 1: - if wingCom: - charID = chBooster.GetClientData(chBooster.GetSelection()) - sFit.changeChar(wingCom.ID, charID) - else: - chBooster.SetSelection(0) - - if type_ == 2: - if squadCom: - charID = chBooster.GetClientData(chBooster.GetSelection()) - sFit.changeChar(squadCom.ID, charID) - else: - chBooster.SetSelection(0) - - sFit.recalc(fit, withBoosters=True) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - - else: - chBooster.SetSelection(0) - - def RemoveBooster(self, event): - activeFitID = self.mainFrame.getActiveFit() - if not activeFitID: - return - - location = event.GetEventObject() - - for id_ in self.fleet: - if location == self.fleet[id_]['stText']: - type_ = id_ - - sFit = Fit.getInstance() - boostee = sFit.getFit(activeFitID) - booster = None - - fleetSrv = Fleet.getInstance() - - if type_ == 0: - fleetSrv.setLinearFleetCom(boostee, booster) - elif type_ == 1: - fleetSrv.setLinearWingCom(boostee, booster) - elif type_ == 2: - fleetSrv.setLinearSquadCom(boostee, booster) - - # Hide stText and, default fit selection, and enable it - location.Hide() - choice = self.fleet[type_]['chFit'] - choice.SetSelection(0) - choice.Show() - - sFit.recalc(boostee, withBoosters=True) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - - def fitRenamed(self, event): - # fleetSrv = Fleet.getInstance() - activeFitID = self.mainFrame.getActiveFit() - - if activeFitID: - ev = event - ev.fitID = activeFitID - self.fitSelected(ev) - - def fitSelected(self, event): - ''' Fires when active fit is selected and when booster is saved to fit. Update the UI to reflect changes ''' - fleetSrv = Fleet.getInstance() - - activeFitID = self.mainFrame.getActiveFit() - sFit = Fit.getInstance() - fit = sFit.getFit(event.fitID or activeFitID) - - self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - - commanders = (None, None, None) - - if activeFitID: - commanders = fleetSrv.loadLinearFleet(fit) - - for id_ in self.fleet: - # try...except here as we're trying 2 different criteria and want to fall back on the same code - try: - commander = commanders[id_] - - if not activeFitID or commander is None: - raise Exception() - - self.fleet[id_]['stText'].SetLabel(commander.ship.item.name + ": " + commander.name) - self.fleet[id_]['chChar'].SetStringSelection( - commander.character.name if commander.character is not None else "All 0") - self.fleet[id_]['chChar'].Enable() - self.fleet[id_]['chFit'].Hide() - self.fleet[id_]['stText'].Show() - except: - # set defaults, disable char selection, and enable fit selection - self.fleet[id_]['stText'].SetLabel("None") - self.fleet[id_]['chChar'].SetStringSelection("All 0") - self.fleet[id_]['chChar'].Disable() - self.fleet[id_]['chFit'].SetSelection(0) - self.fleet[id_]['chFit'].Show() - self.fleet[id_]['stText'].Hide() - - if activeFitID: - self.Enable() - else: - self.Disable() - - self.Layout() - self.SendSizeEvent() - - def AddCommander(self, fitID, type=None): - ''' Adds booster to a fit, then recalculates active fit ''' - if type is None: - return - - activeFitID = self.mainFrame.getActiveFit() - if activeFitID: - sFit = Fit.getInstance() - - boostee = sFit.getFit(activeFitID) - booster = sFit.getFit(fitID) - - fleetSrv = Fleet.getInstance() - - if type == 0: - fleetSrv.setLinearFleetCom(boostee, booster) - elif type == 1: - fleetSrv.setLinearWingCom(boostee, booster) - elif type == 2: - fleetSrv.setLinearSquadCom(boostee, booster) - - sFit.recalc(boostee) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - - def RefreshBoosterFits(self, event=None): - sFit = Fit.getInstance() - sMkt = Market.getInstance() - fitList = sFit.getBoosterFits() - - for id_ in self.fleet: - choice = self.fleet[id_]['chFit'] - chCurrSelection = choice.GetSelection() - chCurrData = -1 - if chCurrSelection != -1: - chCurrData = choice.GetClientData(chCurrSelection) - chCurrSelString = choice.GetString(chCurrSelection) - choice.Clear() - currSelFound = False - choice.Append("None", -1) - for fit in fitList: - fit_id, name, type_ = fit - ship = sMkt.getItem(type_) - choice.Append(ship.name + ': ' + name, fit_id) - if chCurrData == fit_id: - currSelFound = True - - if chCurrSelection == -1: - choice.SetSelection(0) - else: - if currSelFound: - choice.SetStringSelection(chCurrSelString) - else: - choice.SetSelection(0) - - def RefreshCharacterList(self, event=None): - sChar = Character.getInstance() - charList = sChar.getCharacterList() - for id_ in self.fleet: - choice = self.fleet[id_]['chChar'] - chCurrSelection = choice.GetSelection() - chCurrData = -1 - if chCurrSelection != -1: - chCurrData = choice.GetClientData(chCurrSelection) - chCurrSelString = choice.GetString(chCurrSelection) - choice.Clear() - currSelFound = False - for char in charList: - choice.Append(char.name, char.ID) - if chCurrData == char.ID: - currSelFound = True - - if chCurrSelection == -1: - choice.SetSelection(1) - else: - if currSelFound: - choice.SetStringSelection(chCurrSelString) - else: - choice.SetSelection(1) - - def handleDrag(self, type, fitID): - ''' Handle dragging of fit to fleet interface ''' - # Those are drags coming from pyfa sources, NOT builtin wx drags - self.draggedFitID = None - if type == "fit": - sFit = Fit.getInstance() - fit = sFit.getFit(self.mainFrame.getActiveFit()) - - if fit and not fit.isStructure: - self.draggedFitID = fitID - - pos = wx.GetMousePosition() - pos = self.ScreenToClient(pos) - - self.PopupMenu(self.FitDNDPopupMenu, pos) - - def OnPopupItemSelected(self, event): - ''' Fired when booster popup item is selected ''' - # Get menu selection ID via self.options - menuItem = event.EventObject.FindItemById(event.GetId()) - type_ = self.options.index(menuItem.GetText()) - - if self.draggedFitID: - sFit = Fit.getInstance() - draggedFit = sFit.getFit(self.draggedFitID) - - self.AddCommander(draggedFit.ID, type_) - self.mainFrame.additionsPane.select("Fleet") - - def OnFitChoiceSelected(self, event): - ''' Fired when booster choice is selected ''' - sFit = Fit.getInstance() - - # set type via choice box used - chFit = event.GetEventObject() - fitID = chFit.GetClientData(chFit.GetSelection()) - - type_ = -1 - for id_ in self.fleet: - if chFit == self.fleet[id_]['chFit']: - type_ = id_ - - if type_ == -1 or fitID == -1: - event.Skip() - return - - fit = sFit.getFit(fitID) - - self.AddCommander(fit.ID, type_) - self.mainFrame.additionsPane.select("Fleet") +#Purging fleet boosts diff --git a/service/fit.py b/service/fit.py index 1c4083bde..d2a9a24b4 100644 --- a/service/fit.py +++ b/service/fit.py @@ -150,8 +150,6 @@ class Fit(object): def deleteFit(self, fitID): fit = eos.db.getFit(fitID) - sFleet = Fleet.getInstance() - sFleet.removeAssociatedFleetData(fit) eos.db.remove(fit) @@ -202,7 +200,8 @@ class Fit(object): self.recalc(fit, withBoosters=True) def getFit(self, fitID, projected=False, basic=False): - ''' Gets fit from database, and populates fleet data. + ''' Gets fit from database. + Projected is a recursion flag that is set to reduce recursions into projected fits Basic is a flag to simply return the fit without any other processing ''' @@ -216,14 +215,6 @@ class Fit(object): inited = getattr(fit, "inited", None) if inited is None or inited is False: - sFleet = Fleet.getInstance() - f = sFleet.getLinearFleet(fit) - if f is None: - sFleet.removeAssociatedFleetData(fit) - fit.fleet = None - else: - fit.fleet = f - if not projected: for fitP in fit.projectedFits: self.getFit(fitP.ID, projected=True) diff --git a/service/fleet.py b/service/fleet.py index fe59e8af8..8f2c09db5 100644 --- a/service/fleet.py +++ b/service/fleet.py @@ -17,202 +17,4 @@ # along with pyfa. If not, see . # ============================================================================= -import copy -import eos.db -from eos.saveddata.fleet import Fleet as Fleet_ -from eos.saveddata.fleet import Fleet as Wing -from eos.saveddata.fleet import Fleet as Squad - - -class Fleet(object): - instance = None - - @classmethod - def getInstance(cls): - if cls.instance is None: - cls.instance = Fleet() - - return cls.instance - - def __init__(self): - pass - - def getFleetList(self): - fleetList = [] - fleets = eos.db.getFleetList() - for fleet in fleets: - fleetList.append((fleet.ID, fleet.name, fleet.count())) - - return fleetList - - def getFleetByID(self, ID): - f = eos.db.getFleet(ID) - return f - - def addFleet(self): - f = Fleet_() - eos.db.save(f) - return f - - def renameFleet(self, fleet, newName): - fleet.name = newName - eos.db.commit() - - def copyFleet(self, fleet): - newFleet = copy.deepcopy(fleet) - eos.db.save(newFleet) - return newFleet - - def copyFleetByID(self, ID): - fleet = self.getFleetByID(ID) - return self.copyFleet(fleet) - - def deleteFleet(self, fleet): - eos.db.remove(fleet) - - def deleteFleetByID(self, ID): - fleet = self.getFleetByID(ID) - self.deleteFleet(fleet) - - def makeLinearFleet(self, fit): - f = Fleet_() - w = Wing() - f.wings.append(w) - s = Squad() - w.squads.append(s) - s.members.append(fit) - fit.fleet = f - eos.db.save(f) - - def setLinearFleetCom(self, boostee, booster): - # if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.wing.gang.leader is not None and booster is None: - try: - squad.wing.gang.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.wing.gang.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - def setLinearWingCom(self, boostee, booster): - # if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.wing.leader is not None and booster is None: - try: - squad.wing.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.wing.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - def setLinearSquadCom(self, boostee, booster): - # if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.leader is not None and booster is None: - try: - squad.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - def getLinearFleet(self, fit): - sqIDs = eos.db.getSquadsIDsWithFitID(fit.ID) - if len(sqIDs) != 1: - return None - s = eos.db.getSquad(sqIDs[0]) - if len(s.members) != 1: - return None - w = s.wing - if len(w.squads) != 1: - return None - f = w.gang - if len(f.wings) != 1: - return None - return f - - def removeAssociatedFleetData(self, fit): - squadIDs = set(eos.db.getSquadsIDsWithFitID(fit.ID)) - if len(squadIDs) == 0: - return - squads = list(eos.db.getSquad(sqID) for sqID in squadIDs) - wingIDs = set(squad.wing.ID for squad in squads) - fleetIDs = set(squad.wing.gang.ID for squad in squads) - for fleetID in fleetIDs: - fleet = eos.db.getFleet(fleetID) - for wing in fleet.wings: - wingIDs.add(wing.ID) - for wingID in wingIDs: - wing = eos.db.getWing(wingID) - for squad in wing.squads: - squadIDs.add(squad.ID) - for squadID in squadIDs: - squad = eos.db.getSquad(squadID) - if squad.leader is not None: - try: - squad.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(squad) - for wingID in wingIDs: - wing = eos.db.getWing(wingID) - if wing.leader is not None: - try: - wing.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(wing) - for fleetID in fleetIDs: - fleet = eos.db.getFleet(fleetID) - if fleet.leader is not None: - try: - fleet.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(fleet) - fit.fleet = None - return - - def anyBoosters(self, squad): - wing = squad.wing - fleet = wing.gang - if squad.leader is None and wing.leader is None and fleet.leader is None: - return False - return True - - def loadLinearFleet(self, fit): - if self.getLinearFleet(fit) is None: - return None - squadID = eos.db.getSquadsIDsWithFitID(fit.ID)[0] - s = eos.db.getSquad(squadID) - w = s.wing - f = w.gang - return (f.leader, w.leader, s.leader) +#Purge fleet boosts From 7bd8ca5a5552538fa6333d4021a4dc8df9039bf1 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sun, 6 Nov 2016 16:38:21 -0800 Subject: [PATCH 17/53] Removed actual fleet files. Cleaned up __init__.py that had references. (cherry picked from commit b6c5183) --- eos/db/saveddata/__init__.py | 1 - gui/builtinViews/__init__.py | 2 +- gui/builtinViews/fleetView.py | 1 - gui/fleetBrowser.py | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 gui/builtinViews/fleetView.py delete mode 100644 gui/fleetBrowser.py diff --git a/eos/db/saveddata/__init__.py b/eos/db/saveddata/__init__.py index d409e8ca7..7dbf2a45d 100644 --- a/eos/db/saveddata/__init__.py +++ b/eos/db/saveddata/__init__.py @@ -8,7 +8,6 @@ __all__ = [ "booster", "drone", "implant", - "fleet", "damagePattern", "miscData", "targetResists", diff --git a/gui/builtinViews/__init__.py b/gui/builtinViews/__init__.py index 46a500f7b..0537a1e16 100644 --- a/gui/builtinViews/__init__.py +++ b/gui/builtinViews/__init__.py @@ -1 +1 @@ -__all__ = ["fittingView", "fleetView", "implantEditor"] +__all__ = ["fittingView", "implantEditor"] diff --git a/gui/builtinViews/fleetView.py b/gui/builtinViews/fleetView.py deleted file mode 100644 index 9058bc35f..000000000 --- a/gui/builtinViews/fleetView.py +++ /dev/null @@ -1 +0,0 @@ -#Purge fleet boosts \ No newline at end of file diff --git a/gui/fleetBrowser.py b/gui/fleetBrowser.py deleted file mode 100644 index bb28be251..000000000 --- a/gui/fleetBrowser.py +++ /dev/null @@ -1 +0,0 @@ -# purging fleet \ No newline at end of file From dfa728a48620a25dcc8d0d144d52c54e70f059d2 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 7 Nov 2016 10:27:51 -0800 Subject: [PATCH 18/53] Handle Lockbreaker, Void, and Focused Void Bombs (cherry picked from commit ea3e5e2) --- eos/effects/usemissiles.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/eos/effects/usemissiles.py b/eos/effects/usemissiles.py index f212d2e5e..91f608e91 100644 --- a/eos/effects/usemissiles.py +++ b/eos/effects/usemissiles.py @@ -4,9 +4,27 @@ # Modules from group: Missile Launcher Heavy (12 of 12) # Modules from group: Missile Launcher Rocket (15 of 15) # Modules named like: Launcher (151 of 151) -type = 'active' +type = 'active', "projected" -def handler(fit, module, context): +def handler(fit, src, context): # Set reload time to 10 seconds - module.reloadTime = 10000 + src.reloadTime = 10000 + + if "projected" in context: + if src.item.group.name == unicode("Missile Launcher Bomb"): + # Bomb Launcher Cooldown Timer + moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay") + + # Void and Focused Void Bombs + neutAmount = src.getModifiedChargeAttr("energyNeutralizerAmount") + + if moduleReactivationDelay and neutAmount: + fit.addDrain(src, moduleReactivationDelay, neutAmount, 0) + + # Lockbreaker Bombs + ecmStrengthBonus = src.getModifiedChargeAttr("scan{0}StrengthBonus".format(fit.scanType)) + + if ecmStrengthBonus: + strModifier = 1 - ecmStrengthBonus / fit.scanStrength + fit.ecmProjectedStr *= strModifier From 39b7d9fdeb37ebc2500bf80ed7858708ae36c27a Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 7 Nov 2016 12:09:20 -0800 Subject: [PATCH 19/53] Change to shorthand unicode (cherry picked from commit ab32a3a) --- eos/effects/usemissiles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/effects/usemissiles.py b/eos/effects/usemissiles.py index 91f608e91..ff551fadd 100644 --- a/eos/effects/usemissiles.py +++ b/eos/effects/usemissiles.py @@ -12,7 +12,7 @@ def handler(fit, src, context): src.reloadTime = 10000 if "projected" in context: - if src.item.group.name == unicode("Missile Launcher Bomb"): + if src.item.group.name == u'Missile Launcher Bomb': # Bomb Launcher Cooldown Timer moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay") From a440ed3b370f1c9020a6f3f053fd650850b837fd Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 7 Nov 2016 12:18:36 -0800 Subject: [PATCH 20/53] Drop unicode (cherry picked from commit 7fe7056) --- eos/effects/usemissiles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/effects/usemissiles.py b/eos/effects/usemissiles.py index ff551fadd..49ef3c5a0 100644 --- a/eos/effects/usemissiles.py +++ b/eos/effects/usemissiles.py @@ -12,7 +12,7 @@ def handler(fit, src, context): src.reloadTime = 10000 if "projected" in context: - if src.item.group.name == u'Missile Launcher Bomb': + if src.item.group.name == 'Missile Launcher Bomb': # Bomb Launcher Cooldown Timer moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay") From 38bf143704d01a32abad29ad58f803fbe895058f Mon Sep 17 00:00:00 2001 From: Indiction Date: Thu, 10 Nov 2016 14:28:21 -0800 Subject: [PATCH 21/53] Fighters for export and import functions (cherry picked from commit 04c30e7) --- eos/saveddata/fighter.py | 4 ++++ service/port.py | 52 ++++++++++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py index 54249a094..b579ddeb6 100644 --- a/eos/saveddata/fighter.py +++ b/eos/saveddata/fighter.py @@ -120,6 +120,10 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def amountActive(self, i): self.amount = int(max(min(i, self.getModifiedItemAttr("fighterSquadronMaxSize")), 0)) + @property + def fighterSquadronMaxSize(self): + return int(self.getModifiedItemAttr("fighterSquadronMaxSize")) + @property def abilities(self): return self.__abilities or [] diff --git a/service/port.py b/service/port.py index a15dd0d49..aa5cf48c7 100644 --- a/service/port.py +++ b/service/port.py @@ -394,6 +394,11 @@ class Port(object): d = Drone(item) d.amount = int(amount) f.drones.append(d) + elif item.category.name == "Fighter": + ft = Fighter(item) + ft.amount = int(amount) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize + if ft.fits(f): + f.fighters.append(ft) elif item.category.name == "Charge": c = Cargo(item) c.amount = int(amount) @@ -498,7 +503,15 @@ class Port(object): if modName not in droneMap: droneMap[modName] = 0 droneMap[modName] += extraAmount - if len(modExtra) == 2 and item.category.name != "Drone": + elif item.category.name == "Fighter": + extraAmount = int(extraAmount) if extraAmount is not None else 1 + fighterItem = Fighter(item) + if (extraAmount > fighterItem.fighterSquadronMaxSize): #Amount bigger then max fightergroup size + extraAmount = fighterItem.fighterSquadronMaxSize + if fighterItem.fits(fit): + fit.fighters.append(fighterItem) + + if len(modExtra) == 2 and item.category.name != "Drone" and item.category.name != "Fighter": extraAmount = int(extraAmount) if extraAmount is not None else 1 if modName not in cargoMap: cargoMap[modName] = 0 @@ -631,16 +644,21 @@ class Port(object): droneItem = sMkt.getItem(droneName, eager="group.category") except: continue - if droneItem.category.name != "Drone": + if droneItem.category.name == "Drone": + # Add drone to the fitting + d = Drone(droneItem) + d.amount = droneAmount + if entityState == "Active": + d.amountActive = droneAmount + elif entityState == "Inactive": + d.amountActive = 0 + f.drones.append(d) + elif droneItem.category.name == "Fighter": # EFT saves fighter as drones + ft = Fighter(droneItem) + ft.amount = int(droneAmount) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize + f.fighters.append(ft) + else: continue - # Add drone to the fitting - d = Drone(droneItem) - d.amount = droneAmount - if entityState == "Active": - d.amountActive = droneAmount - elif entityState == "Inactive": - d.amountActive = 0 - f.drones.append(d) elif entityType == "Implant": # Bail if we can't get item or it's not from implant category try: @@ -772,6 +790,10 @@ class Port(object): d = Drone(item) d.amount = int(hardware.getAttribute("qty")) f.drones.append(d) + elif item.category.name == "Fighter": + ft = Fighter(item) + ft.amount = int(hardware.getAttribute("qty")) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize + f.fighters.append(ft) elif hardware.getAttribute("slot").lower() == "cargo": # although the eve client only support charges in cargo, third-party programs # may support items or "refits" in cargo. Support these by blindly adding all @@ -920,6 +942,9 @@ class Port(object): for drone in fit.drones: dna += ":{0};{1}".format(drone.itemID, drone.amount) + for fighter in fit.fighters: + dna += ":{0};{1}".format(fighter.itemID, fighter.amountActive) + for cargo in fit.cargo: # DNA format is a simple/dumb format. As CCP uses the slot information of the item itself # without designating slots in the DNA standard, we need to make sure we only include @@ -993,6 +1018,13 @@ class Port(object): hardware.setAttribute("type", drone.item.name) fitting.appendChild(hardware) + for fighter in fit.fighters: + hardware = doc.createElement("hardware") + hardware.setAttribute("qty", "%d" % fighter.amountActive) + hardware.setAttribute("slot", "fighter bay") + hardware.setAttribute("type", fighter.item.name) + fitting.appendChild(hardware) + for cargo in fit.cargo: if cargo.item.name not in charges: charges[cargo.item.name] = 0 From 08b5abc7adaddca8a1223c17dece5ab4646b0040 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sat, 26 Nov 2016 22:57:53 -0800 Subject: [PATCH 22/53] Handle unicode, utf8, and windows-1252 (cherry picked from commit 0d4f24a) --- config.py | 84 +++++++++++++++++++++++++++++++++--------- gui/bitmapLoader.py | 4 +- gui/graphFrame.py | 15 +++++++- gui/itemStats.py | 11 +++++- service/pycrest/eve.py | 8 +++- service/settings.py | 9 ++++- 6 files changed, 104 insertions(+), 27 deletions(-) diff --git a/config.py b/config.py index e2c703f99..fe865a47c 100644 --- a/config.py +++ b/config.py @@ -35,6 +35,15 @@ class StreamToLogger(object): Fake file-like stream object that redirects writes to a logger instance. From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ """ + def __init__(self, logger, log_level=logging.INFO): + self.logger = logger + self.log_level = log_level + self.linebuf = '' + """ + Fake file-like stream object that redirects writes to a logger instance. + From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ + """ + def __init__(self, logger, log_level=logging.INFO): self.logger = logger self.log_level = log_level @@ -52,12 +61,6 @@ def isFrozen(): return False -def getPyfaRoot(): - base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] - root = os.path.dirname(os.path.realpath(os.path.abspath(base))) - root = unicode(root, sys.getfilesystemencoding()) - return root - def __createDirs(path): if not os.path.exists(path): @@ -81,32 +84,32 @@ def defPaths(customSavePath): # Python 2.X uses ANSI by default, so we need to convert the character encoding pyfaPath = getattr(configforced, "pyfaPath", pyfaPath) if pyfaPath is None: - pyfaPath = getPyfaRoot() + pyfaPath = getPyfaPath() # Where we store the saved fits etc, default is the current users home directory if saveInRoot is True: savePath = getattr(configforced, "savePath", None) if savePath is None: - savePath = os.path.join(pyfaPath, "saveddata") + savePath = getPyfaPath("saveddata") else: savePath = getattr(configforced, "savePath", None) if savePath is None: if customSavePath is None: # customSavePath is not overriden - savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")), - sys.getfilesystemencoding()) + savePath = getSavePath() else: savePath = customSavePath __createDirs(savePath) if isFrozen(): - os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem") - os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") + certName = "cacert.pem" + os.environ["REQUESTS_CA_BUNDLE"] = getPyfaPath(certName) + os.environ["SSL_CERT_FILE"] = getPyfaPath(certName) - format_ = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' - logging.basicConfig(format=format_, level=logLevel) - handler = logging.handlers.RotatingFileHandler(os.path.join(savePath, "log.txt"), maxBytes=1000000, backupCount=3) - formatter = logging.Formatter(format_) + loggingFormat = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' + logging.basicConfig(format=loggingFormat, level=logLevel) + handler = logging.handlers.RotatingFileHandler(getSavePath("log.txt"), maxBytes=1000000, backupCount=3) + formatter = logging.Formatter(loggingFormat) handler.setFormatter(formatter) logging.getLogger('').addHandler(handler) @@ -123,12 +126,12 @@ def defPaths(customSavePath): # sys.stderr = sl # The database where we store all the fits etc - saveDB = os.path.join(savePath, "saveddata.db") + saveDB = getSavePath("saveddata.db") # The database where the static EVE data from the datadump is kept. # This is not the standard sqlite datadump but a modified version created by eos # maintenance script - gameDB = os.path.join(pyfaPath, "eve.db") + gameDB = getPyfaPath("eve.db") # DON'T MODIFY ANYTHING BELOW! import eos.config @@ -138,3 +141,48 @@ def defPaths(customSavePath): # saveddata db location modifier, shouldn't ever need to touch this eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False" eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False" + + +def getPyfaPath(Append=None): + base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] + root = os.path.dirname(os.path.realpath(os.path.abspath(base))) + if type(root) == str: # leave unicode ones alone + try: + root = root.decode('utf8') + except UnicodeDecodeError: + root = root.decode('windows-1252') + + if not Append: + return root + + if type(root) == str: # leave unicode ones alone + try: + path = os.path.abspath(os.path.join(root, Append)).decode('utf8') + except UnicodeDecodeError: + path = os.path.abspath(os.path.join(root, Append)).decode('windows-1252') + else: + path = os.path.abspath(os.path.join(root, Append)) + + return path + + +def getSavePath(Append=None): + root = os.path.expanduser(os.path.join("~", ".pyfa")) + if type(root) == str: # leave unicode ones alone + try: + root = root.decode('utf8') + except UnicodeDecodeError: + root = root.decode('windows-1252') + + if not Append: + return root + + if type(root) == str: # leave unicode ones alone + try: + path = os.path.abspath(os.path.join(root, Append)).decode('utf8') + except UnicodeDecodeError: + path = os.path.abspath(os.path.join(root, Append)).decode('windows-1252') + else: + path = os.path.abspath(os.path.join(root, Append)) + + return path diff --git a/gui/bitmapLoader.py b/gui/bitmapLoader.py index 307a11267..b1c427b35 100644 --- a/gui/bitmapLoader.py +++ b/gui/bitmapLoader.py @@ -31,7 +31,7 @@ except ImportError: class BitmapLoader(object): try: - archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') + archive = zipfile.ZipFile(config.getPyfaPath('imgs.zip'), 'r') except IOError: archive = None @@ -85,7 +85,7 @@ class BitmapLoader(object): except KeyError: print("Missing icon file from zip: {0}".format(path)) else: - path = os.path.join(config.pyfaPath, 'imgs', location, filename) + path = config.getPyfaPath('imgs\\' + location + "\\" + filename) if os.path.exists(path): return wx.Image(path) diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 993998d10..4f7682cff 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -48,9 +48,20 @@ class GraphFrame(wx.Frame): try: cache_dir = mpl._get_cachedir() except: - cache_dir = unicode(os.path.expanduser(os.path.join("~", ".matplotlib"))) + cache_dir = os.path.expanduser(os.path.join("~", ".matplotlib")) + if type(cache_dir) == str: # leave unicode ones alone + try: + cache_dir = cache_dir.decode('utf8') + except UnicodeDecodeError: + cache_dir = cache_dir.decode('windows-1252') cache_file = os.path.join(cache_dir, 'fontList.cache') + if type(cache_file) == str: # leave unicode ones alone + try: + cache_file = cache_file.decode('utf8') + except UnicodeDecodeError: + cache_file = cache_file.decode('windows-1252') + if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): # remove matplotlib font cache, see #234 os.remove(cache_file) @@ -174,7 +185,7 @@ class GraphFrame(wx.Frame): if not isinstance(defaultVal, basestring): defaultVal = ("%f" % defaultVal).rstrip("0") if defaultVal[-1:] == ".": - defaultVal = defaultVal + "0" + defaultVal += "0" textBox.ChangeValue(defaultVal) diff --git a/gui/itemStats.py b/gui/itemStats.py index 0dfb24181..82957c1e2 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -40,7 +40,14 @@ from gui.contextMenu import ContextMenu class ItemStatsDialog(wx.Dialog): counter = 0 - def __init__(self, victim, fullContext=None, pos=wx.DefaultPosition, size=wx.DefaultSize, maximized=False): + def __init__( + self, + victim, + fullContext=None, + pos=wx.DefaultPosition, + size=wx.DefaultSize, + maximized=False + ): wx.Dialog.__init__( self, @@ -827,7 +834,7 @@ class ItemEffects(wx.Panel): If effect file does not exist, create it """ - file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % event.GetText().lower()) + file_ = config.getPyfaPath("eos\\effects\\%s.py" % event.GetText().lower()) if not os.path.isfile(file_): open(file_, 'a').close() diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index 2322c704e..fa55c1116 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -45,7 +45,13 @@ class FileCache(APICache): os.mkdir(self.path, 0o700) def _getpath(self, key): - return os.path.join(self.path, str(hash(key)) + '.cache') + path = os.path.join(self.path, str(hash(key)) + '.cache') + if type(path) == str: # leave unicode ones alone + try: + path = path.decode('utf8') + except UnicodeDecodeError: + path = path.decode('windows-1252') + return path def put(self, key, value): with open(self._getpath(key), 'wb') as f: diff --git a/service/settings.py b/service/settings.py index 68c1a2105..6a208ec94 100644 --- a/service/settings.py +++ b/service/settings.py @@ -45,6 +45,11 @@ class SettingsProvider(object): s = self.settings.get(area) if s is None: p = os.path.join(self.BASE_PATH, area) + if type(p) == str: # leave unicode ones alone + try: + p = p.decode('utf8') + except UnicodeDecodeError: + p = p.decode('windows-1252') if not os.path.exists(p): info = {} @@ -188,10 +193,10 @@ class NetworkSettings(object): def setAccess(self, access): self.serviceNetworkSettings["access"] = access - def autodetect(self): + @staticmethod + def autodetect(): proxy = None - proxAddr = proxPort = "" proxydict = urllib2.ProxyHandler().proxies validPrefixes = ("http", "https") From 7532bcda085fe41280c8f285a9005bde7e3b633a Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Wed, 14 Dec 2016 10:42:04 -0800 Subject: [PATCH 23/53] Clean up some stuff left over from cherry picks --- eos/db/saveddata/fleet.py | 20 -------------------- gui/gangView.py | 20 -------------------- service/fit.py | 1 - service/fleet.py | 20 -------------------- 4 files changed, 61 deletions(-) delete mode 100644 eos/db/saveddata/fleet.py delete mode 100644 gui/gangView.py delete mode 100644 service/fleet.py diff --git a/eos/db/saveddata/fleet.py b/eos/db/saveddata/fleet.py deleted file mode 100644 index dc1c06223..000000000 --- a/eos/db/saveddata/fleet.py +++ /dev/null @@ -1,20 +0,0 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -#Purging fleet \ No newline at end of file diff --git a/gui/gangView.py b/gui/gangView.py deleted file mode 100644 index 7415ea49e..000000000 --- a/gui/gangView.py +++ /dev/null @@ -1,20 +0,0 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -#Purging fleet boosts diff --git a/service/fit.py b/service/fit.py index d2a9a24b4..204b8df32 100644 --- a/service/fit.py +++ b/service/fit.py @@ -34,7 +34,6 @@ from eos.saveddata.ship import Ship as es_Ship from eos.types import State, Slot, Fit as FitType from service.character import Character from service.damagePattern import DamagePattern -from service.fleet import Fleet from service.market import Market from service.settings import SettingsProvider diff --git a/service/fleet.py b/service/fleet.py deleted file mode 100644 index 8f2c09db5..000000000 --- a/service/fleet.py +++ /dev/null @@ -1,20 +0,0 @@ -# ============================================================================= -# Copyright (C) 2010 Diego Duclos -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -#Purge fleet boosts From 84dc93ac548f99cb43c849cd2a2308ec2b3839d4 Mon Sep 17 00:00:00 2001 From: blitzman Date: Fri, 2 Dec 2016 15:25:31 -0800 Subject: [PATCH 24/53] bump release (cherry picked from commit f160aed) --- config.py | 8 ++++---- eve.db | Bin 17221632 -> 17221632 bytes 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config.py b/config.py index fe865a47c..c0b943585 100644 --- a/config.py +++ b/config.py @@ -18,10 +18,10 @@ debug = False saveInRoot = False # Version data -version = "1.24.1" -tag = "git" -expansionName = "Singularity" -expansionVersion = "1099916" +version = "1.25.0" +tag = "Stable" +expansionName = "Ascension" +expansionVersion = "1.9" evemonMinVersion = "4081" pyfaPath = None diff --git a/eve.db b/eve.db index 2acb14f37b2c7d0c8431011f690bd5543c865e24..78230ae47e45ffb4647b552153a9459eeb7677fc 100644 GIT binary patch delta 139232 zcmeFa2Xqui*CyQEGs+_|iXcrEISY|Ql*l=Y97Ga1njD87mi2~Qx~wJSl4VsO^Ou<*^Oor$bC)TQimrJ&$7N=LJB{%bf!`H+LfBoZJ}5 z*}0t{XXVoQGjnS|&d3dhoSsW*O`RT^@1=5)wWnUv(n%+`=2G8;n1E}8{7d{I}(VGBtgx{wkZ zvak*0;DuEoV-`|54a#Uw2@K58Kn_Ts0NFpiF=W4VI;n436l9;Y5Xj!CQy_b#Qs#Q5 zdP7F1&~x`l84uY#r6**!lva>kQ|O7iq)?T0PJxg*C69&dnA{7pLo!up`{Xi^?ULq8 zNWX2;WPmnFgCJWcMMAbp@`G%dNC~t^><8IAu?}RjgxQc$31lCU3G}Q@6M`U{#8cKA z$M=D36yF}QVLa7$gLq0NBAzU!ejKGzFOEv9ZXDTGojBanF4c~!3=keiH>wrq4jJa4 z;;QK&JE-BHXR7XqfUM@AC#&k9n^tjnK~`2jf~=$tgRH1h8B|a^LY7xUAthidQrGuKFkQ8;N2$smsp~r<7^AMoW6Pt}_2m$(QrDwCOOTE+_ za`FYL{c#tl@^jBql8NW3Kv$oqBKY_`8i8DRj!JsgxytnX6Ianm<5$uX#4e+Y4=$|+ znVwITJ0p*7x;~f6c6iQ2$YEJ#$bp$w$N}j?Ap57&IXlzosx|4QAwNlXgy5pqSG-L7LL&S&gZ%f5nhG3^F7Y#;ydXwu3A~{k~EaVdHl1e8+AQy_hksZ_j^=|;Nb#*kjgRPvt5up7lA*%#7186BbG zmPF^fCeisWN&O+UNf@&fO%f%qBvFCONft;c36CMaPej?s?-IvAzD*p6-dui@*bU&% zL~2xjBvLQ*I*}UTt3*%8-xKCR{+2)&{F*QT@?`>@{~`g&$j=k%K|V{M9_48Qid24* zfKrivNl+kvra?x29FMY*e~Rx8`6!-J`7xea>%(|doNWIgo;sih@fyhcadg4GxWSNj z<4{EM_cZRvcWB&^zl%e4$hYJCA-{E?Nab6ONsu=k9U;GQREPZ90UMHUI9wpFt1vYA znkqqlH5%e7W#J0SqI*gYoPg2f4;oFBo4a+@w6gtrdV7Ft>FD2-uhMHlUZp`@xsp!h zcR8KR_EI_?Ou3jwrG6oe4CZ`VFUWJkB)&`|JNqJyYV2%UZOAiebcfSv2FO!s=+%^y zX>^Crh1qT^QPpFYA$5K&3%F$Fx{zxhwTlqNE74mS(bjU*~ z!yyj}OFEF!8v1@=NF^zB)xH$ehO#$>Y`RG+djsVC%yqz;hllbS#lCs7HlODYdpl!O6FS)1em zxh8Qw;;LG%&J7xlCm-}5^_Z%wd>`H^kB;p>Dr}Ye ztWC*H=mNPop)F)i0vUC70$rSyPysSC0hX#PN}xlN4yNuYhB7sX>Tw`-s{`6Wkg?bEoT zMv2wsERU>|GbqxK^KE3PGSoY#eq_a*xFJa%_i}bd?Un!BJ+j$-S-zRmsYMfcVoq9%X*z0qisl8R<@j!0 zTa9ctH@ghOHaES5KG59oA~I>Ne-Ra9u5%#_vetQ+g1N?dJeIlYIoO@K(wFp0`7i0J z&@WI7X3JR=lG%6`HfSz$rVeD_X;i=2?=;<~)G1WF+3REsr29!U3bV`S%^{V~CQFjZ z{{AzRp6SgA*of)%adbkaUymCgpMQ$BVS4fj>caHXCumWohsRLaG@uMWdnO_vYBAWY{Ep@EpbIM@^N^g%qi>9YfP0MjS?kzLb~{iuG^ zVS5P-+jL;xD9FA0U>Bxcdr>8(?R(IQOk4H@L2lgL5^~)x%F5bZFnd$MPRj1m9it)h zcc2xSa<ej}PNn23&ruZ#hkP9}WYcb8+g!(tl-lTz?vC)oVH%;9@ zIhwoy7GWB{9!6^#Q#=N8WN~T8;p^xr2N%tT99RT1HuYbNq1e=CEv(Gca}E8{eKk5* zQ-{JikZlVqLAEMD>oqlBMI{lrib|{T%1)3CR?LE|w}Q$he0euJf||?bLRMRbrfI6Y zlx|dh$vnu=CG?9WAI5Jo=FNu;&O@JS3d-#b>A#qM(Jw9ssmq~Tdt{^em|U~1keV#2 zEGZL-8Q)~0!!W*H)EV;Eg~*Wc`9dUbe3H=88&9pGC_Y>K^Yrw(k5i7=C!{#A$LpS5%VmM<;g3VXjSqA2{(SIoahk@I5CE z33PK=3VergT#EO47s_vE#*Sk_V_aZW#&=G<-|K+(Il4gOxBNhkEtRzU5&w-dezF{{ zZw34%V|8m_kbDpLYbTDB>&aw9GUElCgI7uMz+G@VMV1y!3J4-=Y00Ill&Mk*xiz&A z(EGOXu+$E~C{H^TzV~4_?^4tfn{f1O`Hxa$xeXZ(k$&^}1~7tQOSzX%E?`}TK_OFo z$mDA?l$0kvC|FaNfVwBrdhg?a)fpa>u6UD)S7kU`vA6ZE4_t}yV6RTPp8?By;H{6M zYp&C+0J3tVPClZ8Wtu_+^xEgw*y|u*8HS^!cV5&V1u_iq(|bMu^b>dTcoyhMcPb?! z-QNdH@azWY?Wwin@!dlM$9cK~yNi?EU|P7b@lQrQbRM{`@i&27H4WW=1Ue&bqh0U6} z2^3MWN{vQ9UB#)!9$p8^^zimWgVah#CF!XAonlh*r2bOA@AZf_Ila5a?mpVJgGR9q z$f@)-3YQkpG$gRi$FYrX_mNY&e1^Xp$Acfe@?&^=+h8^m4Ein0VzNqiPN$- z1fXzD;}~@FzXZ^auJJALM^|PVEh4hE`?m#%Ww6Ze1;7vnANf%^RbWuZkBo34T^}$x zurKsp;$CHZFGKGE-LB}b_|hoZks?av_)=eZOI+>PqCJ3!b|NB7zY%(C(tYaa4bYpB z?)_t_PoOsy`r%UOiA;@zKDiVPKJ|qj?Xw$t9ibb1C>OOz*S+(8kN(kv^z z=nK6&K(8qD!Mb0d+k_sjON8zza+fwtbep9q!b|l^K!jFAB<}V=r))Bah#z%S8v#NO z@O%heOlv&uc&0=5aYOkl?$12K0o>>$xAPuUGRwuC5+1o1pfvuV2v^yi?7+-v-_&7b zH@+7jeuo<#$@o@W-qT|i^h>0>ly##fzl_amEp^&Y`GRGx_W*ttz#gCJS_JfnqmoL~ zp!NfUd9DEfcNrL6Rsnn`KwK>s>M%M9;5exL12ITyYF&HeOZns4YPly^S7v% zY9mV#msBfk}bGmeGGo8dc02IgXhJC2O%2p%{xsv@}W$S9A% z{=Fl^gnMfo=>#7-(%K>T!I6d%HC%9{HAC>3gI=;3PB_wF3WjZtG}M4$t0R@N_NgP8 z65QgTm+*#99P~oPaMO_lyEE){(6ZOC$B~FaFkEybQi*#w67jf3Pe(#i{P?xq5kC#k z#}PjTfz}ZRLopn7#9>NjD0VoIu2FU@fPoplaV(&G-*7CTRxGJ+Y5~1dA7KhMlr(nN&FABj<^*h|msHnG5YliFUZ6stktKOox~RVu9<4C~bGtr4tLx6_Zms@t0*c&Tp3>cQ|r-42s8JXg0@L-32b9dmud zF?BmywBdJkdpX=%FYLo`Qe6r2HLOsVQ4Wu)Y4Z{6Q&a7fn^$UTJp{k0sdy%1DK(iA z{OTPH%CPw^mHDP`$r?7^LKzwE-I@sb`K_^#t8Wd1d~%Dr9PeA*Al+|aUT?T|i>j~a zYpV9b8)!_1tJkTRy{@AsjBeL)tLoWF)<3&{UKPTuRe@q}F-j(15xgo&?@^%7M<+t%4 zLEefV54j_r4CgMr^)f8?qUC^LSv)f2xBG1jUfDJqzFRjO3*S)pW4KqS>(MTd+M~F8 zsC)#EXnTJIsoLIrjO%T$KPLUR!_km052Lzl&km9P%b`HXpAYted~~oh{5NsAg>= zSNlT_FYE(3q!2CGHn_kJ%d+(^fF;}dteObfa}|u!)_o=2w9Cpc$WANh_x3ADZ?l}9 zuhnwOM2ltfAfuK=LN=lQu-Y0frE;pjWDaEAC9ri{cs|{yW6JOH8KRh<^1Rkzd_r#ov_)lJ=ggb-que^#0J7Cf^0^{GIaH_k;pF{mteHFh7 zExOA6xF;xq%2(r#)y2Q^j>l!A1FX2+@qJAU3M(CZP-*3J9Py}&a_Q>jvXWGGn7Rhf z7^;674q56zkRsNOfi}~X8j|zR1ZJ8*O??v>)bnQN+5XRD&*446R zLRAkX8&%k7gNPpcUMf`LQD}|qI{qQYb%ip077BB?z+>E`HOi;}lH^0cmG&tef>GRa1sq`HA?ZUhe(mMKH6l=>j(y=(<9t|L4}cE^xy$+QafxG?5y61deE+43PW4 za;r}gxb9_ToZJNQ)xHwA=6PwKT;n*b=qi`MI<54Z^s0_>8Ev8J9g&3^hbzdo6c|&k zxTHc;X|+VNsfbj*q;RRUM&1b1t<29*^&YVQ4`?SMXlv`7;4=L-p!H6c+SGAIno8`OY{NdW{Dg4 zDAF3q(F?6(2(HJ%#@<&ho_qxjgqPDEXXN5Rzv6z)qWFGsI&JNCxI92yuD>E(k}_N2 zbYA1T6-sM5p_pwkyk<@wU-cN!AV!hqkQ7C^Bb8UY@kCW;XkKa_YHn)IYd+O1)%4V~ z)il!7$gz%_ZMSXZm4~Yp7Z=k^Tq#vL3;HCU$16c60mO7&eiMW`vhmtd-RYYOP$;hO zC|v>iQqtuYK}gV+EA*#<8=+(AjNg9_>;he^0pv#kcupIwmhFoCL%?!?ByrNcfM(F+ zgnphX*tS6EU-)N0pC|Ow{Jh0=+9o%qtZE z(2+qWpZx&s7_{^m0??8{BcD>pLezQ>4Q$O}50^hip+;VEM{hB&z4F6Zbahw&Dw;W! zlq=|$n|c$NnXP={fljXufytYcEvT)=$ep5@kf>pD<9QC z&-Dp`5!uS1T4+#MGb);x-Aa2501>Y4SlDX%Wh(W)L8e(Xlm>V@*}lK&Vk?dDc?2O_7NP9pz!q(4mA;O#KkKa{){k3t>eQepYUP z!LDK!ff|?PvSZL`Y$s54z3hXwRi%r_P~isYMn7bz)*Xs#_gcP$imH87#AR)hmi&SI ziG}JoDX~}EuhGbN(TV09>C5wYjH*U)<{Go~2u2%zhT=NDkni6`Yurh||F%SwlCq*~ zy?>AvJVm?wglxhqY{X<#b_wZj#t!HgZTm=92HBqOi*nPt$7v@eX(!1g+DVEV{s&p2 zn~KI^x1a$@)<_6j420mq2v$rv6{D+@EW^YZCebjVhRHQdv|-W>6L6S}!^9jW=`dl3 z$vaHsVN#D^_(*o9`!Mx~X+TT~VtNo$g_t(P6e6Y*HBCU?{vBDj3&POmf^fAAWxqi(1 zV-6tm0l^J)!7T>KU(i40rSI~?W*V#K$Giz?nqP1#(3=18sK!aVu1v_kG!Sr>BWN;Fn!*Gf8P_e!eTJ*zw+_pTyK*=MK!W=SQhszL5w zh1r*Mfd0!R9V~#54i$Jo9xe!l{I~!;t#qV-GJ3S26Xdaievr(IWR4{BC7C`!KbGAooBqRbX$<|wmB znNiB@Qf8Vm>vYv}O#LL3y&b{-vBF2$BmMtO;eS$qnV9rx0p{k?@dA376NMO2rOyfj zAU~(&p>(pa3goFms@&6sn950K3cEv|EyVb3m%b<@!}+psCgi!*bn*Gs7|x^%tI0Ah zuEs1wy0n@uzPy?)zOs5KG^ z-dZyf^4m3YA#blWdn53cT1c8CkGV>T$6Sr_MsAlsX;kAGqG^<*&eWEY?QQoWS-H(# z7^mENZ#l?TdySAS_v#>9?3o7Hd{1AVJvix?s zPr2=7A`E_+ z$#z~AQ^LQlr#|bq^>nx2*Vlu5wZ0>n)9dx4Apao#sPyLss^K>q$f(|Kr~~CMpT9 zP3Q_`-6kY2dvEF>jh1AeO~|8MY7-T;elwmx_T5Z2;kOy4DEn`w3JTbaCy)cT(2at& z^nfhAWejAQt>qzuw<4o*$kr%G!&VrfY}`gwW7-BIm(ANKqn4z1bF?^IO5V+J^^TVp z=IY%3A1zlw$^6&RazRYJfN==4)n>??TC)Gb6R)XL?o5}nqq zsbU9HygeXj;MD(uc}<|(aio&5vS$L=m72Vl`^PM z!RZz3UMBi7>6Zz>Oa^9RFq4FtFwEp(CK7)~>?|k2n90UWJiZq<7{4HPfq^YR$B3reHH2o2l7M(-xd< z!QZw!$=porc9OiA@Xh3JCW135oC)Gg7H8r(lgOD+&g60?nltH~3Fu5lXJR^&)WKM% zj;)0)gsDY1_Kta4*re^}rjagC)9-9{+cf-}_9z1j6nIF$wdFIUv4!zw;sAqCL6YZIF&jfrX<1;azN%~;yBY9@;Gn1cL z{mk%Zwm&of*$aR@0@yo%Jq6fnfISH8>`uV01?*z^(}FmcT9w+mbOEa{4Z? z9|QX|uzv&lIUe-}g;tx_Y#g}-5s)Yc5BF>Y|K_Pf!Wm{188on@z2H*N#jTV7S;G>&4<)yp>=9XWyM1J zWMLMq@g{1rMwitXvg+b0kX06AnWm{sj;)$Xi`^hAF2;gOQy~Y7IE}r0&U6IjawbET z&B2mU6PiOevgKfssj=qNhP32ZAk8^g6lzR4s2`0n8_7FeMc8eGT}arSgk4M6&4gV} z*!|?Va7AIi6!ua1+%6ne*m;E=S=gzC9bDMig&kkm35FeF*g1wBW!Pzk9cb8@h8=6z z$%Y+n*!hMXac&5ItgqAYB0{;9Zl8qU+w>Wbx6|pFzRN)IDt9t4Usk@)z;i2iGf<4m zy$l+5?q@WGe2~!=@(1Aq_Ap}*^dB=&iOQo4tTU9K7NY)@#|u$f%Fhci4N!hrh=21a zPZlkqKzvr7h-*-{JMye|7{Un{QDxh_|+o1`1K;{i2hiF z#hLQwqTY~i7NORax5BOMT_$Q&d7tTzs*%XuO(SQPgH$qctwux6$Qms1fFlq`5RTF~ zFzD!h}-mCdWllZvizQmlVCNGxWNAGvDp2x?!?0KjN#p<>W#61HY=fPM(;rxmLHDT(u5EHDH|h?PW#4@D>$>aZ5&4zB!CfZhcmAesyqueV znb4m}ufC~^qaKd>I3jQ~z|jy#BOHx!G{MmnM`X#XZ=xO)QJ_V{CYAX!s-(=H%^okt5~n!|AhWqPaY^&UB`x+XX<53v%iYSF9-91Zh0TKV ztq&@AmP9^kdD30}BgySWOB}6mw8qf}M_U~2aI`ORd(q+TR8(C@s=AJ-x{lg9CE5)H zivYKD)JBzPp91DBPX4^)^T!<|-1oB6?q;6)>Y9N{n~770OvsNpWhk5UUER(& zy5Q(q@?G6-<&+X_<8HW7L^o|>RJX*a?uk)75~HFMqk1Mr^-7HDofy?;UsT@?y<8@m zHIYjGkY$$OlF@m6m+vjncI}7jI`-4xCA^B)2yGaSSMNYcsIg7P(1& zofiW;$@h{RF9zZmgd?WJ@nW!JZi#m45L`ZKh<4x1A=)x1EP|OsP*_6}mkdo@GAwb) z@O?{Swbl~t;aEiNkJauw7Q6nXc3mUQjQorF&8y{KxEk(Ra%ay7A4OiA^mN|{93ydz z!Z8}h7#w49j4OG%Z+yVUqgs%}&h%9b&yHUBSR)U$c^ccQZ41h4rMSR?$qQWMMg@aS zTG^){%;6&Y7U(NzjT}@G21xjkSrQ zXbiuF-Wo(j3>n5cx6tbwD+(s0v8K8beLc}CaVyXxQ|b0r@{u*%j>k}BG=A#}IqX~f zVl)XFL-Zo>JTq-wWrA7QDl6<1t1V zHV_lhXcOE-<|#5~ad!d+O2bbHm}3|oXJRVBOh@D7e2iL#l7jM(j|2nq?KO-`hVyo! zMj9swvLq8H*I%Pk1(nj+gxHl%rlnwA8j5aUj4~7o9%kKJb)jFoWyLw(x2S?5h`k9` zXHUr0g3lQy7@o}b+;j`0JM%w{Bi`Z8hTrbmsf@uKt&XcptI2H$zOG{SwV5FgEN`pYOKb*S0@X0ow`FG zg2+{Z8*6M2!mJCDjFCq|8p|Ekb###j%66jej=>L;1cTQY;z*;+4rY=txWbqV8izQN zYzRCYNw|Ztr^A8CtFfve>l$0D$MD`3JYZt}Iw`~8>K>%#5Ge@D#_ECxZ0xKa#Jtbg zMm;zlK`ZqbRkFRMdJvOKaDI=?1#Id__XYyRxeHJyM|wq|L+X7h5AcYoQb7{NC;agMU+wi^Q}L2y;zX0}ov=I7D2cty zfBusB+Q-#{C?qhC4^RcP5Ikh!C{F?ECCJIfKFnC&FZk?E8Z)@fcfxT>Ex~jK{rM5K z*BgTVZ0x|C=!p&$vnnD=V}fMt>#!4j+DWE%QmdUrYs1%$gvN;5BiPqO%|@P#-I$s^ zh-uq59f@=BW1u4`7-w#AB%$z(G0gMc=13+(-73i6#$X4zavLqo4|lS~!5z1wFZhc} z{=FcU8yh<^=x+BN

            f!&m43oBi+eQce2&bIPx*e1e1LsnMW1owL4kv%y~Cfb`-(7 zhz~ysr&MyR8;-z9k|)YMrQ~GLJNfir*ZbhdBS+tMagpUMim~IiH!kuQt5#@aZIX8I z1RN7_Oe)b1p6ohPF=gfJu224NzK6K|4#66e&Ui@IfOZOgn)Fedf2dZ<%Wpd*Z)>6J4;f4OgY9n_#!VUK@L#{OTF&fk5={^4Nuf&ZBued0ge z)Bf(e_IJOwe>lDU-6QUA-g1BUq$58&@PGd0X7`sDp55t&_jez?e>m{{!@chx&VT=K z4g9;`;6I!S|K)SYE{E0G_fhwkOX5Eq7XRVSSXVrP_g~JA|8Rx;hoj^_+$h0j#N+?V z$?{)5n09u;{D+I?Km1|ai;GJ|dL7gsfvZp#VPK_lEBIm%Hbsj_{W-s{eTM?}7SK1) zr=9N}z`hJ?${BuT0S5@^C+imY7Xc3SfbXZQ_?PGd9s^Diu>r%pE(T)6G7)nY&T!8{ z8V0TcR7J8uy}bi}1&R}>bWeQ{MrTu^K*8m;LCb(r#DyVEybc6C1WXsu;O2F)G)8yR zLWU_`2TB(zUKmvBNlzfn?twDvaQZ57x@o!B#bDT~X^ntp6Cg%H z(|VDxrA=%|3eYA-PilvJ1+-h7YU^F5ib)H|EjV;C)c-*ec0~W@)6kng!+p+)?1Y}x zThPCnz7?qKR=oxNtLctFMgHw$w2-BRkP{KT?7ge=T?i=ttpR=*c{C8uoPcD2GAg2 zjY7p-z8KKN@VJ|!JdDiD&9%nuz>*3W^vq>B_E-P73YZU=E7(OsHOqNdfT^3=!?osS zDZV0X-CRYST=NTEpNe>PGdmQAbwX@DAh+IH-IP8Re+1;#TkC9Tmr589&D?s!Wo>RH z2OziJ+UumkN~ZuDi96T6C)>j-V;C_v5!tAFSzoyqP^5^hd(-P;u6^sUEZo2j6=&Pc7%c?rk&@HHjHe?SiRPW%}wHfyVG{j_5{Ie-0CsdP@hB+`gM!;`ZG~no5ep+6S-0~@B>rWd5!E3UUa0Qj9i#UDeXpuZ)(@`+H4 zc4mq6svXKMg{~3$;c}S&n}vN;)08WLF6^V)!15Tu%)&mZS1vymdI+6gqon+2=-H)6 z8H+i|{8HAzwWXXKCO4Mb%F%MZY51f``5Eau{b=huoFELT#;+A-K`%!)s2N!SFVoDG zg?^x781!nShc&F23cV)jwfa`1ek5Gzw=3c4%=JhQ52>^hdIQpH->r=C*xZ=(x>qa5 zLjPM@q9yznCKe?U{;Bd&B;11ZdM;Ihp$h}97g5EIg_OC2h!|SsOX!`2zMv|WV&?8b zUr`lPaC1+ge^&Jy=zWC#tQvLA{YkHHtVZLVFy{IVtI<#~RQx`?I#wm-SfOWE$FOW3 zCG=y}e}_I+w1kLSHM#)^>yPMJV>9%R=%k2QHDPRK;e-*fq$Z5bESxhUKCXEHx@aL0 zPs9A7i&AV*FN~^BIC(VaA4b)eNcU;5pjLJ0DMBx*l>|LQ=%;GkM@ukg3Ggx;R%>2N zdc%<8t=qrRiwRTtN1wt>Zy$Sjnp`WWmL(e*)KdmzX`bN?ldDfwE5RNd= ztJOt|GVc(2uex)g?e$Wa#)E3@G8H`-B;4DFye=~6v1w2w(|Jfq${=*LKJ5?jA1 z^y5NLtB)pY{#@vL>OY5mM(CF!Izs>!bkW9}o@n?f^t*IU{4UUPkx0X#Zm(%0Nl&Zv>0ZfhW3&Nmw5_PQ>$ z{yiKSudjOpsD#l5eLWixFYX(C?o|(6wB3AIS}b!kX^&I?!+&Ag?)^ z9*b>A4{}dLMve>E+K?WE*Bp^YBO6gK_#;Q&4X{saln(q@;3zk@*Nv_K{vwizs;+Nr z271Pkv0euoj|6siSmDuY_`d_-UonAsax4imazO{2pE#6*>x7y-uw|EOK z+K=n6y}5XwF5a_C?BB(Ec{%s>a?Q4*M{;iX#hZWeMqs=t7;g~9n}zYlVZ4bLZz#r_ zi}6Ndyy+NkK!)AOa8KTujCU#H9m{z4GTzCIcQwNfXDD#zc4xfx8E=EuXRVCid3!Y8 zDvh^I<1N&9J2l=~jkj6jE!TMawHf5r>fD!&_h{q&+Ia6a-p7sibff*$ir#YrjyIg+&F2n_jp%q+I^Lmv$JC-qB9% zZfAFHb;sM@@fLWz9UgCucVBFi$6My{_IbRO9&fA1TkP?6d%X3Y*oKeydf8I49GS@5pp@U)k9%VBj*HMF8OR~kZgycz)6^(u|&J-%uS+3Ok& zUVX34h8%Fc2jt)zbpEif-5^JNJs5KIHw_@i-}Hl=baM>klv~u3P5%~68zJv1$U6-3 zZiBq@Aa)_7zKORa)flnUem`W*hUN{~+JyS>Mt) zKks%I$OYfgNanZ$`!pxq=?9toeQn6JyE@3syTc)K?lpzXzh4@1+5O3oD<5=#T>V3N z$f6(SL2h`6HfrAdV;JPNM_x2G?0Q6_(B7XSA@@Hn19|8%jgv=yZV&nCFE+@}exc$& z^@NK5>{BZKbI)8MFFm6&xcZ!m|N09m{%>B;P;mPtb->^MYJj}|E8XqkZ*;exelH98 zH<#$Wz+=&5QL~IU1(MvsB?==b#zBiPK`s7mP%^eR0LJk_*MvNs0k<}OD#2F z6zbnnM@_^7Sn8{^EsUjsnuMi~rHPu1aorN7rqJD5s43`OEUnelPz3GNR1~kJqnd_7 zvvg7EA2*h6YC2sPt_!V2HYC1cKpeCOyvxHOrM6!sw*8 zN2is%Jv!Z6uUIbR>xWO8m!EQBda&IhmRapaMs3E~Vj12pw;7s{#X|GFcAqqhfgURK zC(RB(FHd@VLvw%VVkOnSOY`y2tBc2o|JD@sZE+*LRl}yALH8uRwKlQ}bZ^qz?ug8SF4mIm zw2{xCi?w9ChEXu>|M(%rBIDotloqjG?BLr1^9+l4@6e%9%ev6Tdxs9)TVh^i5$_#3 z%xh`KvdYquo}gowRDn$4ZO94-klxj=%{k~XLa*5t&D%0e=u_Ilek~)#Qmt!2+nWGmD59IKT|?;O zg+8#|V(62FzO>yh(5DLh$M)zmEi*{(ey{x+=yOQ#VWkZcETTkvjOx$}x_HUa6_Ih7rJ;6()Y(MPoRrs z+kk!zy0!xl>$U-#yB0$i%eH}T-Kb3-po<4K?uOpba)k6j=eq5I{t4+Z54w|)en$G> zN!`gvPm@0EM)%L5e_8VC@^n9=V#&+bJ;etBEZC0|(@eYu%_U!G3vDd{*isqL^emtk z!*@ZuB29ok0=7=o?ubOkYtakX_F+he$U}hs0=DyUdmTlMHAqYd+cnllq4)d`(y{;h z6AI>JheSrLLb68AQv&kz1E7f`H|v|#0r#!117ZW3&=08JM;ov4cMH|o!7ACBcN7{*6Lf)n85#c z>NrGBZABe0|KF*T&b3=Bs>n{_zMU=yw{A^#+*QEN?sjc%>%qW17)L14tv3SpWEi6z z+nTJh55r?FQ`%6U(O0O%>fP+PB*AVUMw$!YKicEIxsc#G0 zwZt--=iPQW&?x>FR@Ynlwp40k#g*L*N_4xLfD;(DQ~I=<2FU;5>NZ20+m4KF3dgQj zqT9=W(;5DtDc7FHotXl5|4N(NJ_B&JfIVDs@@2qz4C`ph*=Z{r%L2xeH03(b$il1Y z9{T{-0rIN4#~+^29exH(5swgEUEi@j5HG5u*Sdz252a<1xKqzerB6p1bFu~OH9)S{ z$s2Gn!x65PJBeMa`$|c!FQ?;}QR4b?rZ!5k*3H+|nU!d_(grSyGVP+zwU|PzEu5Rk zF1Vxg3c~YPQJWuZ5{oqkI-~-Ip9jwBJO#0<1?*#Si|9;a zXA#3g9{al}fa?MA*0}FT-GVOE3GphuZ!d_?rQv>$h#l}{>FZsov*m^QK#jiZE1-iSa$ps&1KpYfe#~%e>FeFns8t*jIHp7J z@@^LqeS+Z&kD=YofF}hU9IbiZo%*3O0uKAs^KAFcfV?^%J~p=dbD(n~GPaWE$Q}&< z`Tys!lROvnpbERfu}3_A?(rGmH399hZ(KG+Q=8`%`iLW*E2Db@-W1Uzo_el|re^Y; zfFsSGanWx9?=p<`T+y=!;C%r{-|(2;liDt?%EvU+H}6S35iiQe%I9syO`F>{ z8L*Vd^u*!%z88S}MC8Ocx7Yp9sI1HbnD~hl-)|^j8FB8U2HyQP0EI9b710k>WHpH^ zC(V{?^hXt0Eq2Cna(I8#ku{WIn)jyus3U86hBK%-K1@j<~L1}SH!FmuZY?2 z$129F`92%=4#^*1G`o)7Du(uX!J{ybu!gPf-a^G z^XwBxQ|*hls8T( zp68(dB=qUMXoUKO^s$wDr9*!v^yR%CK>to%ig~tcjBKY z<7V~g0$oGDkGs$Z&BN+S`uLuGHPAgspHR2&K{x$Q1Am7u{+Tju@gR&=*2dyKn+9!$j>$2) z#c4ZZTmZ!6c-qmJ80c-pNjG8+Ko^ta865{xL+e7n&zwIPMQZIK^u)m^QmdF8&s;o& z+EYLA`;sAOw$_0{Uoqq*^ueUh@*PT@f5x^!x1Q!?EzPP8a%w;ZvZ`68gU3UqTmEIs17m)+$ypA)ZqsHVL|z5YHJCi;m-O zMk15F;RuL-V79Eu%5CST~7AKX0yFPc#=A!;s=fa`@;_ z#4=rA+H{{I1F5k#7$+7_SB2b~85T>#atOxxpgBqk6T z6J>%&eLj!r5BT?S+RF5S=`+1!s1`9w+1&)3Ip2TC;4HvshViA=488`) z+@G15rS=Rl0rutCywav2wBw_70OO^l&JLlb6~l0azTyz-TZS@RUAoNBx`4w4oK?$n zownB_Tg7$#`e@eGf8t{6uBCr`6x{e;-1fFJSm&$jxw4Z96I zO`JWuwab>_gi& zZjQjr*qY0*z2}ONVSxDzce(vMlDds$46n*HMt%aglHnaWe3Tnt0mGlPg>Qa8L!y-a<_X7$nl1RK;#J_w4{i)83))e9dXC{!=d%~q)A>1IpS^HjQd>N&E| z9QDhQ2r|_#gApuLzvzI#o~E9ihcH<^TN6QodS(a$RXyX6V4ixq6@uC7sTl}nsHZ9; zn5v$nRybKb>5X85`gtVAsfj9Wm}&V)&1sHcnz~qzV3yb#+cHW&~wUD^jhuDYxOf_!xu-Fun3obJ6+UEzzMP^E2| zENj)3)e#h{wDpo@qq?dyf-PzRx-ZK%wO}HGU20(y1bfxhUI_N9tNS6~_rR8;s(lSz z_^GA~S+h;)`~cv+BBrKK+uOHCWwC zwQW@QQQceBeRN$}wS=y#pq5NVP+8rNO0!l~56~WB)*9*os?l2NL8{+6>OrdQ`syL7 z`$pc8{EqS|so;OHU`V!rRc|o%Mudk3SlWVx%L2LT! zDkjqi)Lb4?A*a~pB(On!ZIykoZ$S*i3yW(YW)e%_3ob{+J)g? z-$=nr7>Z5n)X)nChA*txF%|-U=AY$tA3v1i`1J0&ioXHI@XfP~AqS)D0}pfJ8X<*# zuth62ex=K^>W6IXi7K-=zd(`|8>07f06%obJ3L4FZO8Q^oOoHVyWdmbQFv=Y`N`TE z>kn%-Vc9O=A)S8$-~>KBdsLZi{x5*1IPtyUlz?`?SgF(L*}v%nvH&rH5X||+D-dnM zG>6Y#xyJ41V6;@zYR0Rk23HBb0E}&LY0ph*)r~TBL(1TXZG3%!k3OV3;0}g`o%JDq z;Jt0J6OODBcoDHBe7C|mUKb6+fUyXr#0xiu8a4rb?1Z0IG5i8}%z5??LGz4_fR8)z z8+&kXBTCBjIiJ5ew4w1N;3%g%6NVEkZ!hb zfd62;=|ISqvamukHY}!!-*h%)ds#FOvj^kNJwpzb-3N>vg(-gXD9@2)Z|6OnbhG5< zm3cMVC}yAhu)#U)N_raS_mS;l*Gb6{qpt`1qWk<2h(^tXR~U8H*SZL}2r_(N7hKN=-&i*&N={Na(meUila zv67uZ82$eGq)Au*^8kPQ*a>|Hg$zF+6EU*{SUIKQMgZ)6ikOvKg334uDof+B8l%eYknwj$fz7e44 zzLg%2tFpMdZ6Uxb5wqz)*-FT%8-ojFF%>g=CjD-ikHd~*LCNnH)&Hqt`yyYbcg(X} zne;8~p6hZQl~E$*Us8#*#+7=pLktfov96;4{}3+}q+=hs?f_zDf^=f8>uc;sD)FhO zq&05M05uHH%7JbvIQKtF#r|(i#^%BqJm=wt(zfwycIkqY;vNFXui2$bN{0JTKp#36 z6@7KG`({92zVvIY`zs)3b4%Y&^MJM4n9VJHFD>v$1`OtBdmuOQxB$p6R-{K7FHiK9 zHY?xgNd?crK%tDD&-L5@#BWukU*%%Yr+}6C)wuLZ*;msWe7 z2IRLYvZC?Q`2yA!=gO`gy1qd57V zy95?$9c_L~GWniYrlx$G|xf{Vh1ugCHQZ|$sPD)Q7 z3yda|yAh?Q_7}@FKsUa$m)2oLza8qyu(y2AIuejyoGN`~A1kI*p}rj3Pr70K9Wa1l ze@V4P0`jX;Wnh>M^Qlk+M-H^R`rFO|n;8$%o(hF&huRp%DD6Z017a>nRWMlg3tbCX zk>L>Oa_CQh{4XqJ{D3<_pJZe<}y-46Wz1$MH&D zxftLGj-DVJ%AsyU8!?j8x&=#{C$Px=nq1>a`ZIWr@~!8{;!oXUAkDYDqv4eXGUyA zRBdQqMl(GsqB{uX_b19s?Wv0I0r~xjGE2#;)CQ2>pD45C+m#jp4(DX%$R3rbYDWra zSLRC>DmMZi!*{k%s2m40k>{T@kP-U1P?W;NyMxUf9<0#RTiF50tgofnnCdK91t^4WCnzYB| zF~%|RUt#HhD`o@M!{Ya&u1LiCr_fKhVmfSnC-l>9(J*dB=wG;D=4KObYNhk;?$8B| zLApc((f{QB_WyJOHt{N0y6TSimNxM!Si0$9fF39^cgJHI^fE$!;Bg+hcoi)DWNygl@%ww^t=VVg3w=i!S-xbg#Okm3wm|ZW!VcY!B$J?F1l9G>j>RbR{%Xi z=)5g{Bcbz#`@Mx;;(He7Oc44}KYMuq!5Wu&>;9P{g17XaCv@KGe}T~X6992S=T8G9 z37tO~kS28gl)yrv^EU>vh0fm|$Q3$&lVGWMAMN~D!AcRqUpFWuUEvQJ6boH^4#E0L z=(WmxflO}|dY#}3(02;Ge((b5CxqT0_-p7Vh2A)%4)n7^j|{e!WT=Fb8C5E0!iR_MaQm1xUsG$3K&N>9rb=q}=% zUe+4W#s4CdzE%v%p~3)_e%9}y`-yY<+Zsa`|9?;h+VT`hl3HS-hQ0E?L2y%gD7}=i zbuHXH@(okt;_Uw)Yi|Ko)wA~f-)pla1T5@8+#O(HcXxMp$ItHAuC+i!EG$G30Z|NW z0mbg_?pEv;+xI&&>-YTs*Y%v|ob$fVxh~lEeD0YwYu4mFvto?!G8E;9b~iMF+rZsD z42f{_s;KK}_zkz6>-)2**Z*~RUUhW?jR$Znue!P+CbSbnSq~R0?V%<#6hlRRf?*~! z6a&9?p&M>SLow9g`VnT+Jbgs;CC)`V=QPV2L4!& zZmbpU#K7-d=*C&mP7M6cg>Jm{8|#+&tqa`*8|$)r@`xtdHo(nqUFfFTkwpf6=R)VQ zV{|w02Be#2XPxT^9$t2of}6J`o#Kdqdji+1jwf(W=I#(@b+~z3(uF#s;SPZwX2A^S zN5BjoC_Jz!+`KXAA_Cdi#2b@tR*(eu0v={|5F05MbN8H}6u6gi_uS&y;pS}#UpE>E zcLLW(vdv!%{2rWcMTsJCui^T*662UzAR5!Ak`>^wnR&R`%VdE&jk~LtSpqk&>u$r! zR)YHl*UM!W!u_7R)61iCHhkvp8x_W)s%2wty<5e`aP!+@`sS#WWGuq!8LOXR|QwS?Z&hjQr-2dye`jCSwZM7iv%#?sVq%@M$y`?!94O$Ej&w z$A$QHk)CMJkOypMgItR2gWq(yIezyj7;OXgDGl*^kZo}ua71l_-(w-neKLf(&xE2> z0?vol#P5YrBz2tYtu*J_a@sHnf|SqUH>mp2iy)rjS9Fo!-?OAYzxPLC+t(l z0nfs_;`hZ&fBe3l*&V;{B9I9IA0n9k^Q@xy{Why7et*t3;rGvM7xK55G>4h9`Z+`K z>oFItrB@+2^_Nn=7(5e))b}+TL7ZpDwXym)O5c?B!)zlouhzCSPL1 zFR}TT*a%E)3MMuP!)9SDYO!;e*hNh2C?<9n6FZHGUB|=@WMVfmu``+2rA+KtCU!3q zJDK%Z(Hn7!EzbJH!BQ$FwmuWvpouNf#P(=xt2B%|V&62ehnm<=P3)~E_E{5qu8IBE z#9nM-UpBEvo7k^S?A<2zaf{-6x`_?m#Aa_|<2SJhoY)XfY!1gaigRhZ#)%!|#BOq8 zXF0LUoY-+r>^>)Uq7%E)i5=?1ZgpbkIhW=XZb&mU$0m#czRw=;#cE4|(Ia$f5f94LFRR$zVL( z0KeuV%xybT7r)MiDg69Ie|>^#r<1L-iAr z@LTI-W&GAT$;^U!r_kyR4Nsx97@C|ehu>zWN8`8U8D@32IWr8u?awlIhqHg-xAQqP zSwpvTgYetqJX)Qh_xS<%?RUY9mS7lg0sW(4@I^Czhh9WIHVnUH!0)I_z3@Bsaxwgl zzl^qQn0N*4+A!rx7yP=eqBAroSKH$^^jbdrhFxoi-S{B5$<9 z@4}nR9d#3(ogw;GcKpWNYL1p*SaF*{{B1VuCEj5fyZR1uuf5CM>+iCR-FT0=x7W+c)g&FBJ;xW3t!O4tS-k^Yto^P4k`|Su+8LBV&kd)?i zJ4Ba5`Vi&wDkRKh5MQ#;g=Y(7(Tgux=)&FoxJ~d+J;mK+-Aw(mz(qJzEbHhZq}PFW zfa315ZkFT|#7bG*UDnN})S#ijC1L_N$JIP&J8+r6xn4ovL9t>IH&_3`0_W)_ z6b}ck6c{PxDvt5fkR)&gU1Dz|8rEs(@r%8aYDg9sC*3Q70n;F!z0;*oLP=I}wu%Ki zH>bzSGC9F_crx6oS<1`@J>kc?HKBwuH-TdP#BGAUEUVP7gy$NHFVC|7qrlxBD=Q!y zjb0+6>y`?`K{VBJ%KCqyQ`i7HWav)Qmdi58O3)Q^jk(&gB56O zB|Nvf=zO*NVDT*m{Siy`dZ6~g_Z%fuPXu-qXT4*o{u0zfcxLD()L>!z3j9j(HFg09 z@~~O-{xt(Y;&Ie0+1zf{oCF-f!)7T+@ip1ij1dbYS<2GlTAjdRH8M*hkCnA{0;h_o zA}qDff?c*XA!S{~9g1V5#XqZN^#OC3zo7))0YkK!a@Od0+b{adI@d=;D)#ets`I3f5hO7BBKvsUhEB4H)iJR6Q zZ(5))G4UCn$LAJv;Lgj#{ArN^cR}X%^k7e>n*6!jzvXJUi*a}PR&C)nb9dENo8T_a z-6L9~o|yRb%ySCcB-|vXXPydsR?o!kbMIKiqtJ=J~cA+LVb;$viXLp>CKu@;HCBN5wF8V{R|c_RBFs znfR2<%iKN_ZayXRs?Y&lhG_r~)Ud;5xcS3mUVS>EYc&n$`XL=Lu9>(=?=`yPUbx3` z{iII$;pS5^FQpR(0h5dCr*~pSz?V0?W_4yo5C*+V=QXD@D}tFk(4x+)2l*9&gmBD{0;?F~0?5#G7`qVF|*N&C7Y%r8L7 zjPltZCc8}R&IF-Fd=|}fRq3Xw{|${|!P{fKu3Xa(2&;YdYm22>MUYtU_DC=HeBb^CKf1$E|+H{?P&89TvvE6paX4#fZ`EB z&t-I;J%MX#EPR*K#kRYE%|sF}lxkNJBt9zS`Bu*xw-~xSKT|@xYw+wOg8tCAXT5+} z(DwYJ-_gDwu!p$A<&~2X+HVB+=BM#8ORL+nmeNmPus)^(>mJ0&=2cVQAp$g*N9@%_ z-{BHSjB{Rn+%q~B1d4IaYlwS~jy-`G=h*7vNZ?xFc!A^Ghq^kx1B(@HuPN?BI$_8$ zi4|=x705awv7+r2Dy{CsT9+6Gy&~L)bj}782AbC#ug-9lT;D-w zN{#X1yH~|=TLoUE1Xi8@XU|ahiWerl?v(7#dR9EE!Xkp@)gw0$ z&#EwdOYuFr0QU?0qMOhIrhw^?NZxOkrN=AaQSO`7O~1P*>vP48zO31G6MC|4SG+Hg zwd|OlS3%+qU)H+Ada-JDkw=uZeZ5}OKvx8f)Axe)WV$Y5P4!sWy8%#GoLP@pdb7+F z)`lzVB}(Xh9W1_*mi3{&PXI`KupsL@OCOe*&qYLEDS>x>;(ngDG^9@^JcYI4oz>E} zI_NzQ>YYpS?HdOCBrrE6^gRa@y_ky9?|{@%_=+nzQmN_a0b!Kq-*u&%7u24;T;1Bm5NofPElw z_s%<9KQIf1AG5f9=N;*uF%Tw=*1jJ#uC-oG;@v1SmA2{gd=0lccbu|>wP)==6Mfu#+d&@5Pl1Nz|_d- z{}DZmvDnAH_%(ep;MejAHOXlIjKRt1{EVK;81w~o%~tB`G zT&>+NmWYnOYvQ-_AC}&3e=s^5d&$#KagF`tX*D4F%hRSn43_0e5JP2o0>p4xsQ@uj zR>ne%mQ@z{cv)q+H%SgD4lzXzVfp8hLs7CuRSsn-nl4XgSvW(U?qa5UxExjxVvZcv z24b!}!w(`-p3xj)ksOY4G)Bqc=vs`+t%JbbIj?43_L!6T5Pl7lrFTmJlydW!&mZ>J`k_vXqGkaa zlq)8psLN7>ee>V<6)}_P7qeZxEA~5u_9t<7!~UoU|Fe%d3lG(?|9%|7Z^C&G9Dr;# z=j3{MKs?-h8s;4`;0N3VxITIyT9%pJ`EcpH*ACnSD8@Zf2eBT9&%?ZT4(biJjq6Vi zUJQ32cRv^s1$TAsem~?V-1WHo+t7A!^SfR?x}lrlZpQRJo_}S9yES*`_zNXt=2I-6 ze1Gj_y*Zy_`S=e*>o<4hfh@yN>&-p6J80NxxcRLTpEAQy0nL1hV~Fewq0c%cs={48`WLJWQt%8E{YJ?p`Bn!99h$U4ur(02B@*Mt*@il)ER4 zY5{jRcdMh;!Od^8_{<)yhkG8^FC2~fV&=D8d}2rMfIFJ&*Nn*pcMNxL9>d0&72KUZ z<|y1Nx%=4I0JxJ}94?HV2w2D6H^yFudn0#08;1sH-oo8)$IXB{mAk)>dkA+rbNfo; ztH8a7yS>JI6Y)( zDQMN^4_v=xiUK!o&vnNYq))jG_|83cPb~-cFYewubslzdC+^-q^#$B|?mq3R12?bj zzGq#raQkrmW!E>jv-9?JlcqHR_=RPzh)By^G0U%k&T=HmCu7za*E4UJdGNC`>6XpM z<0mQ6;2?~Zl3TX?bx1*aEG31vLBEUCqTpb`WpoorMZ>U^6I{VPgVZE&MZxv;F{A?j z$Jkx}`{;im{%Rt%1N9VA7g$qZayAMu*ZUs8J{c^`UlvWduNv@fu^JJSlS6LPpLFL1l&<@RcSR%KL%z~ zDakE+Lp_C|@muixJhD1_FOQWJ=4uK>FX7*R424Ak`wIV|`cw)-?YFSUgK+p*3(d#} z93;HMk+m~Af!Q;R@Sf{VGnNB~3tXtD84rMXI-Z>@TECmZO<-ooAatUI!cloGuJOWu zr<8}nG2ybX2^Rbh>nR*2xP?uyfVV8f_8YLsB6J4D(@YFbmJrT4ypR(!&w;}P=XHNg z5jnv#`QbTC7K&&CoGrXddL&W=x=qVGylH^c-jba2K3z)(UW};DI>ZEOxQ+;_z{Lnr#HK36|uRBg8_pF+N&Wi_nocdiEMHTmE*z ze`z_I&8~HW2p(r4w&#&$iwK^GoS8EMyj}42EV*dTHgKxo!@3hR=QEfsog%hldSd$> zS#}FN+l1z#JG1N)q0izmskytr2L*?He>p4d`^(um=IJcvD4)r5rn}7Jg6zH0-n=rZ^c(LGRlsX#q%DhD2N`!t4TqbZGC5&+bV+C%J@{ED8WM&0{ zRPLnIG3h`t)Acz_31cPTYTR`st`*mN*)n!6$R%z=_}q}Hj=c&Nw;_C|*>#>)fzyuR5i4F!B5Zys?*!Cb^GSilC}F}f;2D9Hq^%R!*mF)`ZAzVJ2VNA|n6^(G z4!k0;HN{U%16~)W?PQtAMjP=>KHnbtN$9xDcf~5dZ$CH z@Db*jz)6%axhH&I2%M(RHyPF2EbhAaE}+!OZ0vg{eB(TCO(_rjC{nS-GG!|0i}1um zaEgm%#y5@KW2Umq_$hEV-I&TuC9!blySLud7}R8)@Z9T}cPjG2BA!F{J?ZXpfINif zX(^X$B+y%&^?V!GPEa=927IrepPYtzVabWWlFQBaW_6lY2h1K@mE3&qS!mjHAbS)Y z_!NlhYbhX(eok>DdxHxL@7JYCW}RwL;r$+chm3k>+y zLr$y*3Y1rQK$MW#zUijY@@ia^sf@h3GDJCf^>~O1a#C4{%5oCM8B;ZE!3$ASUW3dr z)t1+mfcR%;aQ$aaQBPhw3}bOx}VTY8okT zg`H;_EpP1sF;?D&UfMKK-iDTJnj&wvLQIpl4}wtS6tpB$sGP!*HbYKDCu53`U8w^g z=g4U+rIB(PqXqH~MvLSfEZs}wohFE7@=kWb<#IYpOq`s~PPkIu#b}kht20EByqhI^ zoxB^PsA+?|2ZO6=lf0)R#5Q?v0f-cNFQXm&r9aayc^{irn)b^3*?|Y-{p{)w$_HQ& znU2T@*eQ<72iX;#ln=7JI3pio*>+w&#G<(%AI=YPnZNvMx+)*Z3%Vg6LH}a9Eg#JV zaaTUt0^)&uEIY(Q`B*cEC-QOBPg90`oP7eu^g=!X6V&utKG7KBoqQ5Dh3SKQvLVE0 z`4p>xnewUH5Z~oflOcY}r>j8xkx!3Bz1PWSQ0L5g`3xfu{<^H$Q$C9tXwD{|D+Q5V zJ~tG?Pi7Azm~+eLQ3mFG@&y|<@-`QgFZ2f%mM>Z$il+TKHOF_J&Uzs#n@?;=)VwZp zz5jo+xLRm<|23s!P>|cBA4pyZ2;lIAbQ zhUn59%8@z^P@Y3wQe96#l{mGc5Oj8y8XS@-Bo$Db!*&Yw1T^>?PzNlHIiyl(GN3t! zPc+>P(27C!5;Ps%pQSyA$}~L*(1}A`n*I&Ym4mAng`sFHJvjBJu;qY097a&sTfhJg zVKk#AU@(W-G-CnaFAfW72D=8l+=a6)qi}W&V>xW2@NfWL?t;&L3cm%I!r_*%(589f z4KBCrFKH&~iUlv2!Mw^rGuc^Za0s9XcGg)ON>T(n>s$^MD1x1J0f+iDi=A}|hYmE0 zofWURA+`ZDi=8!&!vvZg1X#%-oMy9=y4G-7LbK7&TGn%jr8#KpmW><|X%2cn%Qgi<0ZwqZO7nKGG@R!2o!DZF zzsT+XL=$C_wYq0v>QM(E{d!*9#Gwl@@FT z;59TrNrJgemJb{%_M(ODK%Y5Hp~W`9Hx6kO{fx+(jl(@!dI-QPYOXA_3>DhS>t?Py z6pw4Q^1A8Qnb<4Y)@XK&-&|U)08WKvcAty?d1;y5BmGP|TW@^GB65Pu!mJk9pjXW_ zFAQfr$;Y@HSM_gb-U;wI!QU}!i^M2q!3(0y{QiXjIImc zBBu#gw_qw1uS5VlUg=xFuKumyQZ$_wdV@a-u7L9`#9(guB6u9mw>SqFFIgjZa&E(X zAsS=1)mQKhObC`Dcdcaw-^2Nqo(0zw*PqJ`ljLO>gskjFo#d8ln;w=QP)s{>J;1~* z{v&uXKi;pat_!V39kDW#T5|J?^rzMHfcPdCPVbin@1!zdW>v!bo)@hd2*lgE%=-ya zzh)QsnKyf*%Pn_#DAtyQ0&nj!u8hK6i_YCDEE<<(1&9lyu{3`3tM)9b^R%MH82;CEYBZ(8QNF}oQHGXs+bRN$^~}v;CVay z)27})u@sSa6s5XoQwkL2M0gjHz9|zd)*|x0L`G~z&u$Z|1NmyZ)0TWdQ7rk|x)q}> zXwo*Z7?5uiighchiLDinF5hyROIs&^+3g9*Eq``*+KO?@){T4TFQWTHVZXy7(|(7| z_vxauIR2seXg100sXenrzFZW-E?-8)Haq1jsPELeRe-1>UmF2YL%xpQ!dz3n&c^9F@(uLC=KAstbeI4146gsoDH_Q)oj6ld`Q{La z7V@n?h*t6~mY6p3?Gg~}<=ZTwo#Z>EA-c$SMnQCU$#+p(%)Mmx0bX-o`CfU5{(RRv z^C0&iS|&d3e1NX|evZH|(k7lc?UKZiAEUd}hx zGsnp<`hXJTmuQ&gRq{)Ap=;z~vYli#5Bn|I1@ z8SR$eGTJA=VEwBe$Viz{GQPX`2(X<@&`s|<&P{&F32BQq?hDRjIPR`nBNWg zGYff3{)}2^z9WA@Gd16rzc6|vvkei~| z0g);H&_R5af3Up#A^%{t?6>?AK`oN}a}0!={0oI=ahHGL>Mfq~Zxp&EtNeQqgs=R^ z43R_r(-$I_GL2ClWg2qLl3$Tc5QP+(k-wrCAc`qUFPM)OqoOjjC~8j#TiUN@^L@wb z?7gFMbeSth&F--vJNp0t`mH>)CKDY^28YVDwmX22wz<30+AV;8GuV3`L0?)2JI;#N z{1NpET89j^@-aF0Hd=Qc@QeE#qxHp+Nj8boNm>uX(uQ~banO5WJFVLA&cDtrPd-XU zw%L3+)TZQUKu!jEv(rY54Yu4IYSJcH&^B(s<{d_x76Hm|A9PjdMr{>1ycGRXWd`|b zyJ(9AUiCPQpe-2yZkgs=Mq82FHg13B%cQLb09{zH`~kELC2s4*pn#ROX9M)(u$H#Z z2XMQxz)MO&tFldGJ_U19N<4sDj|D5^jc7m!e6X#1bxLJDYd8$0pdCM(#~1{ZWR_PMLVAZxK&sPTS~E<+s=b^ zrF3?lT`o>xv?~s@m&1D6jTwv$-xEOGTWI$jz*!F0Xb;Qbiwp`&w1;IXJ{kbXLVH=3 z+~?4m_T>OP;V_Q&g#&nH!pp$>ZUH`WpLp6|8@JAEUpO73{j9*gb2vr&S=@g(yr%<* z+wR8UGaZNkxHBkHhz_Dz*}XVar-SHI?0gNrNL@O(8Ne$>kr8wVt-zj-2U|vmQABop zGXfE;pq__+gNk!HLPuW#f;pU^W7Pl+IeesJiGU^y{PWRq0`OUie-S#~3ecAO)THCE zbL|~D)TI;NfG%m7y%+dp=9SwoLs-$Z5-w0IJ{NFey?A>$@KEko;28$b z6g_yP;Fqu>QtE-(@}z{yoNlJz{WAhdt6GxfSv)A?o;A;a!SiY3EINCkrkMlQF$8c&p%! zFip~TgHv%hmY@r%dfL?!EEbImg$2_tl&;9r8zfDgb7 zwBy4-NRX@Wd!}d%g4~gL9Drj6Z?13N8XZh|b0?77&ZnWUPR53jex{4}*o} zSY!nF5c*Vme&IicF=~gMuMxW3iY$lX@D(VEi2&@1*oVsOr34=VA7kyhj9_*}$LfHE ztyqLzz%f*RdsE@hF5uX0a7)hq>;jI%lCZZC%r4+KhEaQaa9C#Zh4^2Tmf3u`!C{m{clfTKg-iobQzl7r4BgXfAun}=@QGU>%u<;)$!6Ju&ACz-($LU zxdM7&yO{3<%=Mz{1>w&Yza_U~buIJ&cAFyyKO8%;CD6kVFupp4%2X^2E%h;mGDo=J zedx#@-v=+}hZ|bz=@oiJ2fGI$xf$A)pjTgroa|XG;E3wAdlMa?td(J6ZK9-NQIXB_5EZ($WX3kp^vnRDAH{&mN zdfy3Hnxm;$X?mXuEGy7lgWi7wR^kVn8scmp(1@H>1UJRWKET*^R_E+8jlkJHHiDv- z2rvd``#1~SRK&N;oj$XR86fabY5L*;93=2q4PqNpIPtYEWR&S8&X$Q<>l`8YEzXvS zTJ4;|+3bgteQOGq1?O|&Y~SWWu}CP|pq#&<-a5rSL31xe@Ev`kQ`{3Y&p^3X{JPAc($}uBI-i?vopBuJ!iOK^p zUx{i3u~=E+3lXijmas&{DABN3EGv{~RAWnm5E7=v^QdW+JxT7SpG~HJcM?gGMRs};m zRaOm$$WT^eDsOqItcE>ed7~s*A>J!VEH6GOYs?Ut${Lp5@51;7{-+Y#U(dgn5z+&c8G@35i0sm&O)BKnu zx-<#L@Fm@%+vw6I0AIMpz=2_d?0n%iAd;@T8iF3NgNn7JhgHz$=i;E!qaT1U4*Tfw z4L}S7LrZ$K3y{d6Exkr%bMl#>VHCYy0l=rl*dRb}m=B)`8dlMp?tmQJ=M=p~ZFlBp zV04Y3x2yvz#3_{CJ^%!8SWj$4z-i>Lh2G5s*f^xnyW4<34nOEUYM7JX@-P*n_pG<& z(?^q;KA=dQ6NGhidezJb>R5uneGI;edVI=PLb1>vJCB z@R)w1hC1>6IGvkyJ^f*W^%)MQ>Ca=p4Gvf*Du+hmyu<0aL^A-qE?D17bQAED`Pkeg zDG>0SLoP{50K8&gPnC3M0eoI)-!Hj2i2{lH9G2WLOa}5*0Y^KDZM_x9D~4l`q(`qF z$me^G@x3JdCJ?W5j%iXB^b&!2SqzRDQkK?$0t}pPl2;s{2!{!h4~x46hsl!9Ilu@G zQzc)tqQKD{5+&cQfbn6O?W6F&BrUUjwBHh)Z6EeriJsyz78V~`49Ov_X8Qoziu>Th zi*BTzc7LtgRR)S?2p~a`+?qDB?gPdXo}MGM2PKq6&u$&BaVhC>nU+Yn0gLVY029`- zQ3nh+v>0B(}(Ds!8V(g<^#Cs%{Zy8v=-==o;IT+D7RDNXL{Q55dPJoVn0L|b{X;T z1B)y~r`O#>Uu}bp#u6KnU;0z=3QiWFaY0J0gki#H5Qz&Z>|LoN(5!hE)-SCT1GZ`2 z2KP#Lfq@!2^p(*;8%u~(hLlDsw_{>xbd}PMuSBVpS3*%n3(N>lq-G!$HZfA|Rc^CCM)Wu!qAVm*jT`bb!-aDR&9LVGci}+%n)egFtsF zH%8mQQylV3dCY)w9E?()ae#{)98#VW0DfyVu)35NWf{mv=)kU0-pPP_JXl{TFFM%3 zhX9vb;9w~qjGI8dK*s=u8Tf*S8zJR84B)GEfup4S7z+aVnr`42DL=-7z)w8bBq@J7 z;423iUw;JPo25v$Diwek6UbNY0%u4CFp380xX*T}U_pQZOzX4R|J_%CM8h~0H5~ZT^0rfbnmWnUo-P%`@EFvAhub9;n+oX6VUJYoJfJ&=1Cqf2=*{7fWI*p2)Stsi$#4`fG%fSq zQoqqUTf=`io3d6`wjN_uXHZb~{D(LX(d>pydfp2{v1zqOCDt13i) zW!G4Ufy!>QChHJocRz?>%ATSSBb7a<8`jav-s})#UCQ1DkQ0@?=m@NnlzlLStS)6A zyFx|Tj|qV_MA?r@XPvGbK&NI6R}P>nv(8cuvax!uanKajdgU0)uZ_wvR5a^W<+vwAigLUj zM4EDf#htF4V8ygYIl&5MzjBfl#UbV7Fo+|{DF?(cL3Fy;Cl+?Ea`+ssr&wxhz3^RW397p6??(cfYf&ajjjJK=ZI`tG8)zHt#Qs0J0kY6oOka8vAGTx3BUKSYEN z&7r3g6>&n%$P6ul2gJ{!xQ*k)8AI6v=ND(d9AFoLTnz2+MQl@CdyE#KzkUc^h0$SD zM3P*gBPqT%x+VJqEyNT&#C``Y)&5IUEYo+O$H+?~9<$~-pKPgvLN(6U@TjHEam+3o zX?VSNUF4e6SHoL+ENqeyPg(P0AJ(Xg=5LJA@Tsdr-QD0gk($s9kMw$|{l=9VzoOK7 z4Z&E|5A=MKD`bMpR7e#`?{Aq^d zz7vWonxd%3O34EqV_-?4n1PoRq!M4DsIDn|+zLn~Zx9vFDx#cGOy3eoB*OD3>=Y4A z_5GV6#_PbauiIkMzHVEdtAnmg{wN=x*f3X_gd_RN<`rY=V9Ys&C*ih zVL)LH!P2U{06z7vSx#Ct0KljIH7iJ~(g7A8?1{7*9bvGOL2b<3(AflYOQ3dMX*EhO zm|Ft1^Giwj0p+>TQoEp(Gzi2^f!c+oBvg`MZVJ@)m)2wha8samF=0w8l}AZMtGeT(#Cp#S=^_Mk8h>;hnUNMaLI2>U@>9vFNyUUFVOqjm5^T>$>jJ zb{5+?c1+!D(sq`?+`6utTiSjQaMcsv<#elCP)fm|9n2R!>YAmLet1 zkd%s%Gx#xwQc`LcKn90kDRm>@C5LiS8ft&=8x9quv{rzR94bj^iGVL0s!3^IFd_th z=Tt-5(Ewh*IMk7LEF~(#7fR~Zm3F)WWZ^yyq@C3Oo*WuUJLdtosa>~;wDTb#JNFqR zrI!KtaTp?{Pe;@RIV_aYGjS1AQ}Q6mL&}xrlOy)Gs0oIqz8dzGo z_7Q`B57El!w~(^a@+c^}Y6^U*o0fNgq7#lFD&Je^=cpK_LI$IM@D_^5Dqdx2;Y{>t z4K+o1ud=$aj7l(Cqk!8HfwIe8sJH#c-3$oCH#z}L#4fiMgQkLOXA@`WUs5oY}+`xh#(JdO>9_+0J zKkGmE46v-`eJxA+kP_hX8sExtVaRxJRV_j9^_`bWho0afr)ruH(nIO60gCCG;v>rJ za1SWL5eLdC;%i^&Z~}Oi#^3bP;k$^lmUh55FQatChyZh=IxLPYVl1;jc+9Gj1#3)t z)Qe%_$XUS_>EuN$z}$~ovN*PIJ9{oIV1J62YSlmtdxc`&YKqsjJ4RG}t5D3Di6Y;X z1B+6hg(5P$&WAP}gnbk%;*QLojW(bIF&>V}8Wmf#tt~(cXvC_;Bblgjdre_QWqFs0 zQnleoR;muq5=~BEyuYRe2*M4k-|JADvT0G5)Jyo1J(cbsH8m>MzkQj-BH*kx9zUh^ zn-TRq?a1nSnHC;Fo%*3EdS&sWMcJYFttt9?`O~5%sM3c8voBi{gLKLGek?}5k?1FJLRznqNDN{g=XulJV9aFx+_n5LiALg z7KP}eJnaV2UwOt%;6ci>&Jcr@42)v7VM+$05z2Gs_s`DY`p=wVl=2)E*fvgikq2V3 z@}dpIH05PZ2t|3>3?fu{h53?ghVlv>mMubg?G7==rM#{KIZt_`gIJ)vsSdGNd4twt zTdKUR3=yNe9S0GsyhGn)i&NfVptr45-WP{ht-K!sk)(WZK&(?f41n06e6&DpQ9ky8 z*rt3c29ctCV%fDr`OGpXUHRMzVvpkb0^`88U-^RG)^O8c8Strx^kRYvmdq{^rXcAcsO zKx9#s?hu};S_HyJRZ-e@Up1s4L=H6s-K*VC4b2CUR}F0oQ9zxJqOccHr(g}d#tY!`1 zphlL9LT~W}b{@Rc_;NU(;g1I81JNhBizwFVl2O`5ouDL22UUZC!H(KkJ_TO5>%d@7 z1rLCa2)CIwUc|W^f{xQ>s zC}=JnhyiUDw2*c+0__wOO}kkc9bn|5kXYJ{puu~DC!WPR2G5Ox*6RY6AuSyQt)@N0 zFnYuY+Ddz8BIFg3vZK{eeT)Ny*A7~W+G%?#=mQ-+0GhxlR7c02Bji|qSg6ad=5i3M zV_aTn@mg$N`&v_Gb02s>*Uf1ga&`$KB-ZlV? zW#*GvpB3&Vp1SG2^kFM#EBBi2S0Cnuv$pWcPoL2?oehLn@diVDk@?lIH4OXcqg#q^ z+^@MhYf_ZYyf~P7cxeLTTwgKH&2Nc`YHThj255>_(m9I7m0JgE3cPhov2&pK>u-f; z5XGK?Lc0MmzYRVEu)+%p{JD!jk*Zf2T8z4F&8`jB;d)w(9B^6vv=Flp@8T~AkyleJ z!UH5x_0aBbX^N#7@S{QzqJXA|hqV!f`KGmyrbxtE&XW946w?%I@!a5&4p5jh#d?aP zB}<^N3WY0jgFc3qT!%(879(-nD4NB=Ogq?Bk(h==w*=#h4V<@oQS@}Mc8eyGeFuGD zUsR$N+Bwp&OUb}k*c)dw#ZJuamzKmK6*a{!7(3fsP*ib=Nawp*=nkuh14W4WelS3m zp&eMgG(~Y_&ocB_R&BC9zb-DGJ$+$qtVPkJDaG^zx6+PiDxIg86;QO*6wTNXcc5T) zI<9Pfb02aoN9+FYmi&LS^?PZDwET;fU%?UosrrJavgrS-Fr&1y49G(((DbaDIX-^? zGH?YNbWm0;z(Cw2eS`p-bv%Ed?i$4vgF@D>br9Xau4_<)YDWzAr8x9C)-Wx=V5~>Q z-NdBZI#bgmdCaAFJA%yC6zfm{<1rjs@lY$zug7kbfQD*aqy>1a>p%(h(bAItR$%X{ zgnM#Nj_a?c0KcoVj)OnNdD#5=P)nZ{Zy` z&OJkikByN-GjikRm8R<$JsjR5O>uL;5++@E%8I2i4&726act}W+#xED? zJP?@Z4#hT2@d~v&u@X_Kk(%N)o~TSj?`%!g6kkx86It!vsVTmC44_07>27`*E}H+N zC#}i{#Xc>>FZUQqJ`2SGP4NeZtb*xc)z(oLY@q3ctR}0rZn|KvXCX?O2gOy9Hw!LV zbbBVCk>wXrEV!x*p`;g>zOXe(WZr`7V~MTtP}KLe2{~MMv|zVbrnE&ok3_KLqs!4D zbg+qAcS_$cKJHbI&>7} zV6Ubfvfgtm9YqG%YYIhdBb37lKPU=oA(B0J($Q2ziW<$j_SqdZ&FDHurhiJD?G&T<+foqdX?7~`2n zr&~cWO;e1+<(`g$Le&)Gu|Le|+fZn|LhJ<3Rdl8p8!oHh`;f^AY3|7@^)+;kA^&iq zXYZlTuYsd`sq?2m^ivnGGVHG|K((|FQWutn7^*IW0b~D5UF3uqsj_GC>|@l$Mu>6h z;%*QV)F^bq_Q`4#Dw*A-E@5`PqAqCy@z2iS`p=vqRAt*H+h?fJwIL$ZXbepDIqFh& zE%Vf+V<6_M%P@S|7pcqGwM3~g#UYlu)R;k#E7avyhy;~A_Ge$E#d$^e zU5ReYeq2o~3vp6SWchnWb+O0(?B~=~BOxxUt4ly!Q`s`F{f3$p1aU`Af}w1`udYFt zX@97$Vd;IMt~EnssB3#dyinH_f_Sa2W10U>T@T~O{!v}u0^+lZ4<3>ItD4*p;=8&5 z9j*PRx`DN+Kk5b->=}nn-H2j#=+%uwAw1MgHi)b$dy&%Nt8PZEcVt&LcZBd$x8#P% zqi$&qkzd^k6Uk9X-C75thzc{090BUKDiB6>8+r$aS>28cblBAGxFm;DO>sa3sccQs zQBqAcpqw0K)KuiNqr93{0HUIr)&`=ox+6P8HFZZLh#Kln4~W|8PL!{so|>+MXrQK} z<~kayY`bPhQ*{?=r=x|s3qz)(wYr-nw5`haig0vL_b}?L?qSL9rtbBI=%wzhkBOe6 zkGhY=)?a0>H#i2R{d&97H@_|rk4VZ+Z~Fqxd#pBK+ATdvS1`i;_d z;@$=0TlzZmyB=7)j$y5UM_PW*2cP2p)?|Gx`ZEJ8-oLP(Mvl|JjvTht|6}Zb9wGjl z;^Ysp=!H*@k`s6Q5S{fIV-$h&zF>SYSz_ak^ReI$xGO@>!Oz6`ZSHhQl90VltY{(v zn>P-SP+6RB_=DZ{uo9AP0Z_a?Y~SG}>D~f=@zD1Dx(SjSDn+19@L_$7geepSiWh<% z?JSbpSD+{Nc67$s^{AhLuou|d%Z`xdvuEiftb9=*9)Lq^PJDq!@;U`>CU^p!mwckZE%~)LC*u)2^rwL%8M@q@ zQ|n2-{@^jfJ29K&y8<{)9G$3FB;N<%se)Hy88({Pgv<(}5wvdyW3Jlnpr@ z7$$g~R7lG90z6X$-#ARlj&Udu3!p5m8?pbQl$~9Aq)=>@9!uF_?F8b%C>CHVMN2u@ zwPU55@ir7p4z%>ZrGmHX%1AjPz%hbTX}^@?EO>=D-wtmnC)!zHg7Ds<&no4_kR7;6 zaJmaCv^f_-u||a7T~f+<6}V3L@78aYa^(ZVbYrK-V@Fc1PT(yf`2N09t{5P0_%ZMO zStJP`GN8aT!3VJ*>=yvW6K%}@oZC&wuNPRn^5nd!5BuevnD)zi6>fSv7DoB>SvA__ z&_=%aHW&&YqC0cc5+iWjD=)f@YUik;@$J%dyBy}|+N36K8BFl&)1YWA#?H7HyoPdp zC636gDPoxdrRmTnaB-_}jvGax(4MV}TZ7$SZm=#%GdJSa;#Ha(sLa9LwK&$nGP;p~ z5Jfe`Ru;#7D2i$6+CEuIR=_5WQ-kSdHN-GUJ75P6xY-i{tXhD5rRgT>oFfp7H^V6I zu!U}+%Xet=y|@E4=;lLk3GINR@MrzC_T*sPaWISqhh~YzUBjYxzyv7pT0A$oFIc2) z=zS|J)sk=*=I(74Lv<~{3wOGG9An0Bmlom~?vvb(#WMLGP4QaqPIq$Q5Y2Xvd+XUr z+J&*JjK3D*1B~iBT@XSuyW&2hw(ed7MH4MVCM@JT$8gBsSN@|k-T93GEwyC)@psYP z>QJ;1N5s3KOz)yh9om|EyeCZgyZfQgW{B~58qz&Bcy!f{$fx^3_v%B@LsJyUlAG?K zgK+c~Cy6)c>E2p!KP`aKqdnbwid0QR0G9vp=G=7O3n8?rZoCyGcE1@EL$yQ7F~uw> zG;1)v0&Ktgo1oB)!T3tLzI6XGBGsmQ@l~LB0NcW$nVj);V6#6!V{~YbNy5f}!o@0r zR#W2Hqm>U{LGgE+Xf3Ulj-bDAXvGxY96QE5gyrDS?zzPG(eS=$0Jln#~dxdNQ+dl0vKHo4oLgfe^t&QI!C@JpUbO4T=rUgy#_BxoEVMi zgIKj&zLt)%0axOFaTDp&qOW-g%c>_2?&~Q415x_oL&`3Hp z3^ZQQC_0r6nj~m6oz4Q9DrgLyW+s~~XdInh2AVEtJe@`jw1*3tNM}lcW_e*D6ce0D zbY?PeuJBBwv&`UKAZRC@J&dauD`*#OXRSD1&}FITK#*AcjJ+mR8IOVyPcX#Zq;sR- zw^oF_L+1{F#QJ9Jd-{d})V{^b4dTI(DX%U7rkX>Wa>pm23wexY=2)qPIEE#oC#-+`B@~IX z@Ts4VJD_+a6bU78$V()_elhCqtE2#3@ezw(Clz&7(pK%_Ps0DnARMqpi}C_GzbEUU z&{kyPFXJBhlNV6HZs48Jy)n|b^58An5%;kQ`m`<->`@`sOFb-2PiJ5~#-Igw=mo`k z-6qPRDIQ0~zD~mT1=Id!48u>+VjbG}6rYLfdWJ%E?9-zB-jJSc1H*#nr}(9(XCJ{w zv>5*2ZNrQ*P#hPE1RX9T15L{To1Pt!kfjY}q=L_C0X#fiohaicnwvHT;!W@vdR_xT zE@+2j)6;Vlt>d!Be*W})BUoD?PQYV5^!zo7*YQw`v?wlweR#xiPdlWrMS5ZfKh**l zaL5ZZGRJcteN@7mB!8Dp8`=_VIQ9Up5uP8kqboI}m*>DYG%jnQmvz8zwAjkS2FpOv zl+$a9su+`Bv2p3Mrf7m}c-0b$Oij@YWAH09CBo~sEMKCkzfJOTeb;=uA>Ut@LjRCO zC=zGkle-^3L!p^riL+rseF}!6ofcv?zD@OMI20W<#T-<@Psk=`m%kPI&GhLF6x}t& zZM+ch1#Qi#8GDI=3CNbu7}%UWwIKK1jPwP)fpfH`V4q3KWIfmTw2yC-@>S6}r~bn= z62~xgKT_iup&no(>1g!;dQHbT^`IGIqI$45#1!=q+OK1pdZ;Ueq8=^)5vm?;2QfoE z0vp*eQ$50RZMJ&U2V$Ojlx6z@^(dIIg+SJjIw zf3K?-S^nNuFQL2ptCzb$WT;mPLcCC~unc~!US;OSJN0U7i1+F> zR$V`;*P20mQLnS?{-$1MDg2?{U@82q-arbSI`w822)%j}DRjE4H&GN$PxV$c2ygWk z##Co^^|q@DWG?mgB#1ofostlF)jJr|odwjp#UYBQcLzcEtM`y#XEF62((5#-_bm`s z^?qLnhx)(-5u`pqmO4wS4>9mLgVl#UAj+wa{2?l+k5F}-mDR_EA*!j5J44h|pA>XK z)>WUhfT*WF%?i;-ecAw`iTcbFqPhC4Hbg7+*?5SyY6h~@*+I=dX2NLsYgUqI0PFibXbDeT}knj#6LOz#55jjQW~o%>?xg z%Ztfrze=phc3rxUC6cupT@GRN#T}trEW?Fu5r2rD1cAhT$hdWMBMIa$hOljP0}az5 zZjHyK5POt5xU2BnOE+=B4!fX(bc>a&Hk$9JnFiH3wqgxZ;=hDW^Yr24mKw)>p zouymHL6t<%yL7t=sHUJ7bo(|)+yRbzMz`Z|`eVZHHQn)ppD+RA-buTrff@?GPjp8G zH4~IcclKcv4iF|_+z-0T+C?kwiRF~L$Z$tHK^}B>4XC4_+;o>+eHTIb=pO3_dI~B? z_vV873Np|=7>f>ZdpO=i_r8M$3qK3pM?pFM5+-1LIl7M?*fCOgR+J90<|T~2_{wzu z6;di}&iJbI0IjTyDnizy2YnDy++~h$L=Sd@ggqMHj2^PaB8!l%=pnnL5J8wkoCZY* z6EJ=dJ;K$O5hh^#5PDPFHn^5OQ6SS`ITA= zIzdlZha*Zm{yaTd30g1wuF#V=piLl{fbsXFacIGgt-|vGJ?#in!6@h|hil9gI z^c+aooUjR=p_MpnXJ&AMB(3T07$HFt2!Y@-xVsLn z9mo+}1HoN`YjAgWclY2f?_agK@2&N|dH3G;eREl4?e*(jr>air-c@zZF{Vub?c$K1 z4G%G&lwz}T?l;3D14!7MnD`BbM{yu&9+op;)%Ve4;Bi*^7#=-@2~dnFgW<8TAx<%6 zHar%s`z%v-!(&n0*Y)=qm!`hYn3AKNF%TA9&y-=F(pzMZlp@9rDcId%2PI_;rJITl z$hlZN!dNN4hcaC`_4NEbal>B)wfrhi9fUfX$LG)-dY>1*v z;fAM3z7*Tt^MdKob?CfU7ioAV#)-E~@rGwZKp(kN&1+(KUK03KDq)De*$w*f4|?l| z`>oA1+VHj>s2}`5-TW zfvns|KBqxHo6I3fUmU~(Xv#54-v?EniQ~M`0^>rUn=d;_>Cu|P?nqizoigNsjz`mF zwJAe85ZB+b`jlZ4h>y%HYviGf8E~_FN@iKpa5SdCh8#;vLt0CyPBOJMtV0(HY|7Mu zGKyGQGIgPh7$gGQFm)%tP*6vvUWQ~5vJ2B#%G4I=;eg2<1OBHO^h|wIQZw~k_HDk= zj5lKawtTQB5YO`Bg(?AQCdz?E8MvSEQ+~<;_bCw1EMN&U(94iSIaY%4NCH9!W`X)8 z5RWhjbzWlw%8A)7@Dy_qJeQoa7gGM0vCB7N{dc1eJj=N$nV)i|A~Esyd1NlwK{-DG z;R}e&)xmOHF0{D7Ys`(YfR}4DSZovEltW-^Yy;#H?Et&T;(`uXsn3mZKM=dff;-X= z%3T}$m>FLnpxnveXUuq9hH_s5r!n_1R-rukz;I5)^}P(IDNkcCzjrinfFI?V1^fWD zNPmE78s)i#A3k&VA(WZ&76E_b`%z4k7qgk!!0KVhPTs{}6RX$y6B09+#rx-R_o=|L zKpaC5iEl&Z@+E>ZvwEK|<=YR;#%d3?(CJrS{J?LZH)k->ub2eP{04e+Zh!g}Wo5=f zF=8_p6QNIm`3>}DtDSy>VPP)7YOB{1`mGKa&tM}jW~YgMLqVCbBrL8D^PNqto^a2)dnRBFLUa6Dh0nwbhF0Bf>36_*#pAYrb}yct^vgv8a{ z-KkKeOa?=$SHa~zfksQ`pO0wQUz5cnpBu59qaqj{%pJrN0haSrG$Wv|gv(S^l+wQ$ zY@od6DivLYYZ4^fq+$e+51v@=Qn6ZqVN!FSiY>%QY91-+DHZzwkPn_%o|Atp0O#kB zpBLmm9WYr!8u>p5;4mFDuc){v75U(am*Sw47T%|)ep0QN{t zVJan>`+f;d3djvOETJ3)v%Lokb%gsNow0`Qh_fb63H z^097vcCy3Jv*ZyPdj)dD1LPB1_A2B+7qejMgys)&yhfL>6qU4@f>6m89A<@TD+QsF zEp`dp$%#s~IR62tWJ`#Io#aF%TS`kfNG?>eB~rp+a-otf?7wXs&BjGf;QOB0J zpU~WmO18*Xx7crz8 zj7fZ*6JlKA>*^2_6W?Tpn4I{g3dGdJx9HdbGZWv|fS8l`E*r%B#CPar0gDpF2M+&f z3ZB0wi6x2e>ma3Ni62CQs}etmILys85#|drX&h_rFarCdj-5z5-}JD zd{7iS#1}>B4DnS_10lXEDrzK<6it|GUW(Ql!dnq&IVCV8P^G-f-=4&L`7x1$YEt=0$NaD z6=gy*h^orO-yo_f6aRpysU+ossG}s+^gz~ACZV_k8z_^6-O^Z@>GKCK__!NM)8tcTDQH0n2Z7@XB?!S?a9^%S+8P26n?c z_*YEugr?^u$CQG;4&%L2Ff^b1`~h#tFm9L$Z|vu>xs(ycFO;r>(fIKntEnl4{f+Ah zwR!sIC0GzPu6l(Zex5vBWX1~Xx4iga8;4k2GlRe3`!gf_13m!kF{Bg?F>ZW=AP0X2 zsqeeM@I%y2>7TJ4$xxWmH^2`+w=kEDH4ULmE$~AJUe#UJ%ioZ^8r+VrSk@g^_=zfQ z^7Dr_FhepXz?~WU^NN=KhBQ%Wo<=`^Xy#8DgYd&2Ke>S~Ww-(Ux$wKJuCJRiOv4X9 zmxh*Fi{3kEig7D$NQ7-U2ycz+c<{@!F)xlE&KC_lNkz;M|dHD z1AqQ<%sV5cAA}!*fBtX?*GPxv=efj`{gjh@ZlkmkIhBQYmCQ%9+5nZWSeOo{T|TbE z55s@{;BDAt`1lq-{MLg$TN4lu6w{iIj@C1%y zQ9O>GMukW8RJdqM8Ou|04d{qA5ywY_@VI604rn3TlB1AKY3j+c~=9M z_<6y4!x{1(g}jKZcs$Dn=h2f(_>Tc%D?|LS)iAdThPl9ipI7V@rP$YielmTy8`V}EsaW0m@G|l)0-25kZLIc_X8^Pe9fd)PO zuod`QnVNpsDzA3*5-+-1)zf2c%!cH4V|^-g4D)|8=G{2wQy4{JZo!7--b_>&BeuCU ztM{6gQ{f5VcFad88x=ke?!>`QccCJAfL&RA8V7TVpl$z`)I9&$05KasV`Lv zKkViJdAtfysY&2{9A8myDs>UaO9oaahRuL{;3FJ73a(>7Bk*w!Ud5XNW&uyKda?+P z;op3Qc{-{-&;iB~ag>$^W_TV76s65>y>$`#Vc>c&yYiS~ zl%34|!1tK(0XQ;m1wUfmiQ6-O2R~&#ineHp0zVh|P5ty{Rkm$L^X$Jg83QLOv%}G! zCo8itG6YUh<^(}ZSLSqwn5E1OgqWku6$?Z26|6TJ0v9RsnnEm9<_jxenKEBYC@Yi& z*&$Xd3u-{DQx;}~*q|(|2=UKS@ccbVY*NIt#(`UvMUfCYl*O2y19vHl%R}r{mI&Pe zWl0RgVPz>AR^TyZsjyE@c$5^8)6+@{ti!-_ig-U~;3Y+z)D66%@rQtE?{wkx5zK3?hrNLC`PC20=NMje_zh8(To+ zQ&M3cnSWQrYtqdHl}#ejBFZM%Y-WE&yl2T=LfPC3B0$+v1j3?hX$fIhwhEn7*(#nZ zHM^Bh%(A{L1mR4Vvwz%?5GVCMJ{W&E>grpz#JLu3Y9@sS>{W)MrB3<N>ZH z%610GWev9vmE8%D%NlN9Du-IKh?i@4yxb+ITo2G$8AB;5w;ym(f`!WG23(O~r}FIq zHzc^J{8qqi2_aMg?a6XqLKszO3Xsbh?g*-|2JlR3A}QJnkS3uVMb`nmk#*#*K+%gq zAEYXVqCWvXOK3qc=nfY7at(KDikWFJSV?NyQw*kIYdQ(tsbX0`dImu&m6xR_T*G1e%sd*f%N~?T( zjr%-R$pR=NFS&rM`Sw=;iX z^=rd1vK|9}mu$mVuE~abvJf-21<8hi#$sZwieu+uZ2GT>i8Joj|3qr)M%yC#icA%MA3NhD%Mc}vv zF2cMB_Zox|*CJk0k0r3pUd1VB8`#Rc6$Vw%4{(qyO8a(Saz+7h`dC;j_U$nEoQe1$ zl*8|Yed&aOW(nu;2Q!h&2Uv#H2Tjw-h56J{miaLGl4~@$0tY{ulUyf(6<8UIa^dJy z7JgzO=z(E1l`ah)EA_79(3f5UW>*09l1inYfG4y5H0AQ5h``Bx*BsUlH(HCob*YCN ztuNcqXtgK%ZCc;QW0}Dr&zrVUgvj`8=8NcX5hC9>WQYFgy5db`+`xs5*ZiqWKj31< z+d))jE0A3R*L}k*DgztV@GoQc{7<5{u*ZQ{6K|wQ(c)Kg-k+n4BZq?5F~7niERp-b z{9uIZo%ali@&<3tfPFvQ+!t?(f@Nai2P0hHtQ0i@xRY=0M-)XJ2jV0n>cmZ`;<6Yv zE&G|h(4EWH0JFp4PG{^yWicIC*x_*dn6^^c^WYO4-q**A%86O-G@{3OcXx^WR1Rj2 z<(y2;T?&)s3Lw0Ch=)v(tkvS)-t-NSJcbJ25 ztL49f+1qf3A~O}rfFH8n6OOW}kboba@DCBleFZUiJ!g(IHlk=Tc)emSi?WRtW%ZW1 zJmsS3kzgLz-O(tGXt-~dPpofh+CVWdGc8}4TcECDYJq>q`|-G2=ckxSm^O^8Z)g;#|8X7yn`PH|I34E)v%%mb1CinGC)xO@^!RPi=2D`R49s)XUe`U|55 zgpq8`$vC36hbp1_So3gXqmfu8^mglS%;UgvUgmv8camWAb}N>3L}^bGj80=M$~;Ff zI*qjivlzK6qqAEBn3oAgZ?{^QR~k~kZe5?*X0)vOOMlKhM>&Du=J|^FNU(X4a#9%H zOO;b4A(knpT0*Q;P8Wn&qnt*mnAa(1azboW&eVk1teo|O*s7dGjhVMA=fniOOF0(_ zu~#`KEc}0#g6Ho^;(&4c7Tr3T7PPr)PqH;;l73Gqk z>mKED1msQSvbgY$az)U6<%*z(%2h#6l&gZCDc8h>Y05QGT(6buPKbAk_ynT)gK`5M z#{5~iAv}w3%1tX;pFz1PW>lkcD*z&$a;rUruX4KtL`LOy6Nt=;=Z=_{vMF~OK;%&F zq8VE9DtGHa{Hol`0g+$1R}G?oaz6t^VMUzmwiHtym>^0j4`3l$%*sOpgiU#f99kU8 zLosZ+lt&JTVC4~7lO;@fET)tQ<#9WRDCJ2ph;qskPgBTf<>_w_m6WF<7nPM~*&(VZ z;)t-Nn(|zvTT^*1(ygt$5b4%aUWjxXC@)Zmmc~k2Fho-&4P{|zp}d5ZWNEFu> zQF&h+LRH>(gczoL5CuF^`5+P;qkKgBw~SXlia~0k@=4rwvht}r#8l-oTD4`m@>$R< z<%`JLT;+=>k_F0F3;a3DBIT>d=~CreAjEP!KDs_rC8I6&r$1-foW;{bSfarNDgFgI zmfVyJE@;mqOG4okFNvK5ChXJ}vs7}?zaY`#* zgo7;9r5ebjwU2}*R0G}1IzU1js&NV+U60@{RI@NZx`)9%sb)`rDuWHAnlQ_(!zCzG z3%m?mL4)m{sjC1EYqLF2H_ zm9T~C)CDY*u#4&}0Z3ml_yE=U3|KBTN2qQUz$ytBsqQSmIsqX*RQDY~xQZUHkN~QO z_GgvvUJS8PJrQn)4CkVHF9EwHgi`%-0O>1+L{R-Cz#*wAPxT)Fj!CFUe_$oRDttw+ zkgD{@2*4Q$HRz9PfD01pQ3DI$sw~uyhSVSdbVI6|Q-d>rI}+Mb!=ixu5;{^tv_tD7 z3EipTA;5D9eW($-xAmoj0n`YigY}JsA=GFGK)Q+{DmBguzBg>Ae)3O)U-ArhlCx}bP+)Al!qLk zrXK-vr#$2oHLDCLAcI|^=FMh+3QM|2&Cm&K(pL=mNX`EMNMAA3hnimpSVXYUT+|{K zAl<{zQq*E8K)Q#aPHOQ55G8|^rj}I!74#3`8}PR=^+R}S_CTX`Z*taWCvztkX_wl_ zW5tsfmv*AoNT|8Vd1x=L}Pa@hTFAN9ISaj-C~rFjxOi}m3uqHRVp zUxkHHMYJdOKiqfm$V8Rle;vPY?*6s}tQ9x|PrWWtm1D>NdpPb_;HsjJVV6v#_YN7X zis9E9&z~%Ff5o(3bs~PKE(73G7IUfU1u(lf!G1Wk7M~Z)hi-y%_)|QFQR^QZJRdHP zp9O9tt&-q^u@o;%n5L{QYC1^Oihx@%mqPN@+JIYUzysXm6>Q5-)kF_&%a@0ksM-@C zJ2=7kbT?HugW16ej)4VO9Yd_O8%I|~@ES1heFWEpK~?=NxHm^vKbC4>2C?>IY~oKf zFoRhK%BwxWtzhrf5W^8WJi#4I8>z;3F#9{f-H_XwqT4H+TyNBNO%<#&5A;f+nxY$y zU>*wluok+Gbu{w`=xbF2v&R!W4)N6z6~W}B8oGem!ML#Xm72xW z4sFjSC)Lnp)b1!ij{l*nsQvE%IjM$j@=$vexlJy*hVG&Ey8&`m4Lw91VDi{T%jix~ z2N;jG@e;04hm(LL3HPWY8i8$!glE(dri^WdgxAz@KVY_m&(sM`%{EU!n1MRA2P^`3 zyuwV>X)9={RQXWnOn?;OLQEQ^FML{tj?Q!W{Y&Bed;-gcSNy6z~%XYpF+Z%!#%%Nt>vL zXeMtZ?4ll`(S4F|h&Ej;Q4!!xTLC8k30$IJ( z(Sp3y(Ssp;)iJ_~$f%AHlvy1sblKE#!V<`#jzhDs=2piePHSFuJW{a!u1-h?QBd_v zK;yI)Q70ldR)2M3C5V!05;}u5P)$O(($;d;rlJ zp(dj$tWoNeUm?n?Q<^|Tt5XX=#Hv%9LsU_x6^5v$iW5!Nn(A~^wzal8-O~=TzB&UV zjkSR~qYFf1b*8XZo2fGewNz&bYNO8T4AD-VZHDNe&KA1P>Ku%V)~@OtL4T@qtq{G` zxm_Xps`G@dzdEl2#2|IPpagZkh$T^7AVO;Dg6^U$hp7u45F=Ib1~ls!b&(5Vyt+u3 zze(z1^cL%6b#Y&asVY9FWw6drm-L62tuBp#n5U+Qa$caOh>~5bF7t*+QJ2L)tWcK= zTCFZeEnC;BD^SbU4eAO(o79!*;I~<~sw)L`eU@yg5^X(bgP`j++O2tOs~9 zi@;=De(og3+oWH1iN2rE>4H|^hu zTORfg`Tvg`nYn@lE^=aZuu|6GH}DnaKQL1_5RK$Ib7L&cHJAgwB`u4Pmiei{9pGJF zbPQ=5#Gc6m4&D)KrVZOd|CqTe426bE!B3fcpsX4`1;1eKgN3z5qTRe^9)JbhMxv3t zWA;S?kS!t9v=*4x z;X-zpsOcnNZVtW&Hd)h4VE%@0$g%v?44u%%`z0Y~U=lWK3NFaOFQL^mn+Gn!iQhx; z`#^ujwI#xSd zp`Y5~nd>62LV#jy;{Pi-*B+Sr{49buw2 zXMi0zl5r@Hw(w3UE1aFyji~3*Zomp3v3))LvwicV=`M$29>N{ZcuuW$5y#~|~^S^B>L}VYyD6bB5nD&mrf0GKy@@*B1J32xKBFo-6$dYn9A-dOcM^BHm>CaIQYUe%Da`oP zGIdG=FK5nxTI_5GuVVhi>#~PB3oDloX@%v3o7P#}Esj&7<%ShThwm(U37^smD}@BQ zgo4>k2@~C-3)~djb`Bo~m$%DK@GhDEuyPLSY6R|Otel^^VyLtoV617Pt{x1Pwj+FX zePple9q@7HW>_-lhRM}-iWzTvrEYD&XPLVpcionNFEIB-?z%kzUt$(d@N~ymX}iii z47uyx4Sa*sALpR%o^{~c%v18Sv3rkkj*0%nP-%O}S1&^D{u~YFkveQSM&3W~f_cj% zY%RElC>!1~3EL!iESR@Q!gdJ8@__9#$F~pMGtb7aTQe*$Itu^v;+jp&?6HSRo3lRk z!?YoJoH-lA>5%i?~BDRVW2Gal$n|$OUd>9Q7u}Kdxd>jq% zpyS$W$#7F>UKWISMkga$MO)n}K-Z6cb{P#0jT{F-k>8yc#E zX3B7#Y3OCZ90|QBF#sUX+e8ec#6f_?QjJZ=*)mX!Sf zv4-juF`3ldfGrYcka)1b?%6JB9;sVFyCf_nEfYYxdl9Qi!;oP=BsCjJ!)$LqDq$<> zUI6L(MeHFRH0<7pb^6Xat$@&4vn}9_$-6nr;%pB zHwlkOcm)oU@T3KeJPk@G={b!m0+5biL>i6i1@M!a*EH%NAd7@|G&(OpI)V`&Xmm$_ z^aCS4)9CGhd@|Tq8j}U^d+LYz+p^y>+AjX38)x0Gu7>4cJ*cimwya0gHDw@G=*(hz6WwF4l|tLrcbTQ95Y`axV%*Q2{wZ>sD2K-^Y0xFPPT8+t-KRPjPOgY}8J z5tGY5O~Lc`B=Jm5#jIvcQ&WXQ@k$lv3axL|O(+`cdv!A!vGtR>8IyqwW`ZE2|zrtFc9^2f9L3R1Z2JDys+4Dr{BNLnv-rb@fnhh+67lbZ1)~^{~*@ zSH%YuZ4J~Ts2E#g^(d;z)=WLx7ow$lEEJ-RdJGk6Yo{Lfptfxt)e~?kY+ck7iZPJj5{dY-Wg&>RF*1qn;Bq zRy`+Zf_gqH#3c264Txm*LNILxxj%}uTF+0Q@^`cPDS1)COSg2mA0PvwR%O+I`yi^+D7$iWr)q{HKE(4UaJnVL%lBI+^t@(3$ahVkq6?SdZRwX zVfE&(5XaP;P2k4aPO7&Ao>6ZJ<+;>v`u0qZjP}XNe%*HT@Yvb!R(EM`(xEfjJfQMgj;ZVUyLL608Y;-SfpoR4u#>?cQ4q^ zTpIc5hbCxu{!CvSChLdBXb>Zd78OG6IVexcfK85gu z7l6CV`z_trmj=HA_Ll0>Elhpzl67!D<~F8C@`%bEAlTzoT0Elf5w**^AEo=4dXeWN zcqm_xM7b#e6N6o4o{ZW`5SGF)nO+3GT9_azXM|KoR77nLK@Yd{=0`+T!RYw*ajdT; zcp_Lj75c{>J5oRP*!jynW1-#2S^Z`WO&(K!S0<06n3&_rc+=Q)nB(M%W*I*kTMzL6 z)&$CHexb2T5lo&UEt7}FegOnY&F?g>D!?tF5RID+2$N8f#-Yd@5fW@P-h+YCQBG1Y zjh_UFmJmVX9|B?}l&1-(Mn@G1u{2>Mpt^)=G~qg+wuCw~(F&+1p&?Bi0%#yXp^29O z((Wv?l9FJOI9f=JXALExj2zO|EVGW1V3RmHN!3Q06b$GpVKa%(bvb%S*h-VG0(wi> zL6gmZ{u1_*c+Sx=NI+yAntTT^L_&Q^b^=rhjVW0J43lPKWJ^jGxf>-_t!YXrz&HtQ zY05yrL<#L_$_YTSgibWIAYht=E;JS0!XdXABD>MleSmpV^CwNq1z0GdFHLI)kh=_# zqi7liPKQTsGeoYS>3*QqGF&Q6Zw-*!43WEOI-0Cwlhhog8JH*>a+@LYBF$(Bkfvtj zZJMzHuvZ3qOf!A}4oFC&nP`-b!xBEw%=rLmYDWH`neRktNn10@mu6K2$ZdwG>@;gC z;F7o~ssPP;3XryDR7sj00k|zScA7mFa8E)w&Bo~CcqpMFiKkf{PbD;@IVwP!no+H3 z&PBj$sp;H;=9UD#lhlvqVt8eFA3*p-XVaG zgcmel1j{Jl8_gHNGUJ5vuI!4@Q7YN*)~;S2JMT3{w8U6D6a{E!CqAMry!4?n!Lzvg zcE(!C(3@cP;v;&16AOX=&pb+rEy29m7%>>5Y2qUAQoeo&81#Rq=J^jMm-q+?uHeB+ zjU^=jxSDe_9Jx_CgFWl`htb%mR#xE$ey}Sd$wUf92Ky#fPccj+6&=RDm3ansGF6yp z_8rXgut=zG2JhkImN-bo%x~YvxH3O!Vq!kXp@oyJiHY(EZ^B1x#DQ{6%*@9*{5BKm zVqWGYgNWUz8$AZhO9l}KFqG=(|Mv47-yw|T`Z4e&X1ryShKZs0Do1xbl7`g;@`^#k zsr)o733vxu%tH}pd}$ckpq*C?BF=f!@O;3BeDMVn4Q~v5!f{{3%JA?xU|ukYxQwOR z;dj9=S$`F?(g=U>8|Le1M5W#vd077$xfy)`{44WUEToS~56;j0 z-D@(9f!XRPB)N>iw1LKq0vBaAqUy(pakDsc>gT(=vVEl&f9b>7uBhT|fVS)EomLRH z)Vsn{xvSoV)ndD^-ZMiyQtx$vc&6TmX<~b!-tPwST74kso%%q~NA;ngFX}@<-_=K= zbPehw3Wq zNW!JQ7RiUGuSN3V>KlZl)KnAq#9A4Pv`sDAQ+Xsmv!1kp_WjOobUQvHkuU~i*-$pFz_{Sph&QT-}9 zbZ7Oeh^3o~H_969J=AZ}5Pj6|B9{K@_p%TJRq^r%yGQ-u34t7<{t$PeXbIx(H7!Am zTEn#=r65LWL)t@((T0k08m|o%x+E>p0+FmGic*`VDWYU%Xo@JA*_tX!W}c>s;$Em} zqAZqZS|5mInvQN`U#aP$EY@hl!aR`cwP6BNHSuvp`(|yppl#X+K|8e(f_7^o!y)!+ zBhk<72eeUw4r`;tT^!R!i%3suqeT*@wJ~B0J*SNkw{%e(D>8FM8!NJQO&cdNdQ%%G zvVB(@Z#LvG*dJ))I{_bQ6U6;K)h4u`JY~`${m{MLsfX_EDc8{G*q7`VwYQ(g!5boF zviZ{3N$6fy?p`@57mXFxjDtBJy7RbS!7k?C(H+NObaRBr(V|Qt2aUtv<_KqXarE|a zz<(LJ+?6Zy---PWqA$a#nUUJ~qTndzAi-_G6*zOD`Dy%8U`19(;9kZ*16Sr& z70r%!NFuxW($rZ%wyh$&;gS5Qx4>*$MgB>@(6l1p#e8`milb@5CQe}<;p0i7X^Zg# zZ;?b!MSiC}2J;q4Zl-350U^h+)O1pY9^I;fw(?3Da50ylo zHPH;wGInrjUG=9KqGjx6#6wZ3-^=gyyJU0~NY0u*rBAY_%JeMB<4S)LZxl2*KI-j9y3EU`jdV`OfIQwCy&)E^OB5gR~vfgHt+g(NAbQ%x0(D zFo^z6+fhKyG9p-vfp(xR{8c*er~U&8m6fR)X@`PTrGpq_q8%3i(m{;zrky1Ka1hZi zVti;PhDm2Nc~N@Wc?=*u#Fz}UD?dPbh%tU7-X!g8D1!ylE)h(6h%r5Bw+JRZ#F+lH zTLfz>gC)@JJ%A1pG}@B`AP=R*jG{dlIh>yEQZ<40Yy!!xiAu9+|7L*PnW(go4rBz( z62U4hqXTG^PU)*vT1^L50mP9ouSy%~AOWPWR%sU1ZTC?gYff(9u}{>7B(^_s~&{8cw+r5ZjQBRRr9X zF|?#(Qvr`8bf#lZ0n$B-?L)^S057Cw2pvxXypk}Ij${0CzLzkOPJ{qHNtj6|#sa=d zSWG8G6aE2c?G?L*PNEIFOj5O#PL2Rb_bhfloxBFfAT>wn6sB!gCJAThR01HIgsXH4 z9mkbZ!fiT@j^oNB;W3>?$8r6pe<-jo^+SRE*#{bfS|Q{_nv(6&9tIYVQI?c`rzR4VbQEYZS@EvxmqJrQT&7OuI z?sI@}%o?+=gZXt8QROjX%|SzSJmK4`*`Ku`G`RIf}8JIuddr$LY!I>nNH5xC_{E^_SxEyX>*=#17k1iLK zo%Q)~{erCET+BAiVGEjq*|RBY$CI@S=7N7^y#w#=SOCW^h&`LKL8h^^uo$?I%wAb{ zXIj`6$c|0f5W_iI2L^XhQ< zTt+;Pil!Rml<}9(6HAMIfgv0`FJ3aVI1bEf!{zfC18Fg)?4Sq^{(DNbo>@Pdg7({>Rp3{gv4C_>iN7KxC5 zXp00j(iRI{Q_ZtjT-aO_O~uhlTT&gOy|z>Y?Wipk)J01{uW@wOQbfp}+A?urA8naP zqMx>0WM-hY94*t~(N>829jdKBw{$4lN)v>pt&D;guB{vlF;ZI<1~Eokg@M~KUR&+4 zKqhIcJ3>s>))a@Bs;v>#JVRS69F>{cT2Zlcv~?mE^R;zVAQoxs(KQ@PwDn~nmT4PA zN-MPuqVU#e8+{-q7Jren=(S|)HaDi*rRREiVo=5uWhagaY);O z*5Wv(Z4qtfq_#CT#A$7-NdBC*O|-I$+O{STSG4V-J>Jl^w}iN*?I;FuSKHAJ;(@j^ z0OGN>vopjqZI>0|g|@2;#4ByL(7n}m3*858k9bVZ@k!g$75GKlD}sL4zgZ99D*^{I zkE73jVNJS<$f@rKo#}+B&tE`gA3C!GAg#&DS?DZE%4L?C+;kQl$R(}G%D>au^#GUD z6r*z}7+1IiGo8a-x}qe6(z$nlzevaPA4w=grdpQHqrP0ypsiey&SPkDRg#)&bp9!z zsCa#te2GONR zKpP1vT^bLN)@0?;bP0pMtBcgkrOPe=zka6jQo5{zpq~u4ldhnGT>~W? zB=NAT%Ol|=T{#I*Bs`?6sA`ukA&ssM0gRCFfv%nfjFIq_uAyPMCJ2Z#(6v5jtgcCt zOmq!{vulck^mH9P#Wh1hHoA_+<(ezuR}vp$a4nEfjBcQlyB13@(+#v;*D?vgbYlZx zm4wnHz6s}ADxk4erQ68>Y0$=XrQ44I(x8p&LwDe9xh}|HgXqp^fUs^`d&TK==O##+ zw{fHCt{osZAL1s`-9&)ge2AM$ch3VJiWsV7rh9<^Y28+-N%w{U(xj#--Ma#KE#U~= z7j5BP>W94tvsW-WP56e#p}wA=HgZP8C_Rmq^}`I;o;fFG#AWBeotcYy<)Y=7e1f_& zJAEe6a`dYIZHcy~<%fXm!B?o@ot;)-5c^lrd;X{K^MXKy7<^E2MI6%O-OUP>@OjJ? zW55b$Hx?863N+Cmow+Kkv6a6V44xof6R1!f%NZ+cBfw~BFjS~rn^vO!g2r+9dMJ*S zD9oTF<_0iUSN#s=Gm{mXf&I}JpQ4F z_@O<1Sis3AW9P3YOdH`25P6DFA2d!m1+lAJA0`k^J z1swidi%BYIlT6rC;f~=wtsR9QwsP=$F|_ssa2IFn9x77IO+kB@-{K`F>qNaDVE*it zk=9KIACZ?wlZn>d03K)b@u&5LfV|ZaoeAZzz7?3aI--B^pdQvQ#t-K?d>#|6e*(ND zV~_rgB4~pd%sxSMF+7sCp)2?X>x-K@(S|kP+stOlOdH;U?{akZuCy^2_<+?8tdVW( z3x3QTWb{m?ja%>o`vlQ$%19g0?}OMUhz_YjspvI9uQ_~3I-*p#Zb9#u!{`B}?g4*f zE`v8sZb}FK!pTP!qfHfo-&q}HdO@3p!*?X+3iQN_HXWVZ>=0go;&%vtuqOSElyG%mwfznVUv0mrc0cWa6C#UtK{c0#n8(%K2psmf?4^Fx&NXeS#(R?tr6gQ%#T678^(c3KQeaoTBEIYIH- z89#^`+8I&qHMO&%HtJ|+F}?-W*UouCG|Do)cqCI+qw_$rU(%7@9w%=SM#DpMoPLqw+7)4hvWu z(!#PtjM1NVpuU|ISZ_2Pq8)3%6`Aqr4BGJ)%ukubcw?!2CpwvvpE8N@L3Vbc^*C#A z`1E*}?@kQR&RWbFFk9{X2Ij|1V*G5hD-tOEc8^z#A6`JaYY2W|$2}$(^XjhS;HG?i zd&36WodwLh7%_eE4zJzl2~Iwx6*JH)Gwq%TZZAtKW~e{yz5wjRmup^`Xipw6KT;Ai z%0zq6shxjHwI^mA=Bqt3@Iy}yKMA+9=O(z1 zfFGy>p3l4rmmf$5FJxYC7)}R7Kdj{VPsY;0g}^n8 z5B%wn3Am2&nJ*ne4LVafwwGvGhhRNAw=jRi`q`nYV196-(s!@l=rD#7=WeNwH5r5H za8vL;<_xJ{l8shf zh)NCkENOfhLlT-?G5*@rm<7h-1@S7=3{p3;im_lrgQ3WN`OP71d2DrD)|z2+>}+U` zG2D;!pqUlHK_GnZNHgYRjN;V(&zY)R)fV{Gfg zm$hq$f!T#U1;jpbZ--ro5a+=qbj*whZ4CQ6Vm2t|WH{0B27td7JMuE^MQ5Yf>wFVAj{7Cz7W6m*5pJ=}U#2*kF`I+{|c}4vEj?<`G%|(CU z?{JMONrw)iJjTk)M#0nkj8}`dXR2>ZmlYvTFby!Kq3smqk9&<RsZpRm@^Z`ZrQeHOxcf$nHEP|Pe8XhCP zqWqnz@g)qe-{6h&{3*fl0hE*%UkIDbAuY6bE*@ob!0|=djR#~2Hn=m-oIC)?VwC2&CX$R_YP?w%o7 z{6C5WoEMp+u)uw^C75mN*h-l7jspK>hpNWpc{d1=A)gIjYar9j$FXqSU zxcB4eDo!@sW5?p&Pl&Q$!#y?`_kKdm_+R+?*~s0AX<)Y8V;3WLC$6LG3dY$VDfZmKd^V7)`pf5+aAA18QpMm|DkAmfXP!{IXg1bS_21x8h!6+0J0+H~z;4`q* zT*V|;Ccz2d63jk=QQR&*`&~H`7@r_8xWw6S+<)cl;s=bWE*oEvS1|g&D~P#(Ur^>E0eoP51VAx>H71@xS!boVm5PozQynY430k&fm3n?I8+i?~6hd(cWXUaQbT> z#LQhn`+#ol4A4FbGHV~}LD;lUqHzXkpDIH*wa*zLg0;`0g@3ZB0wiPGAa zN=PY6`-%a~Szh}Z2@#`x9RN{D`zA)7%G$T?5LLDB!Un9aeMcW~*7RsU{2}XTKg94- zUr)#n(LhgV2+>#{k`tntKBO*0D}87#h&K9ALGAR!91tD##99ztbR|1PH(jX#(L+~7 zk@VJ8LH%?M)1`Buu2qFd&~;HHLvld zbn$%~=URO%x{`B)K6Ws~Mtz(|6x3#YJQ|C0n?7F9PJM!)J^BO@&3=8NphNmZp*x~a z90YM(PZE)y){{gu7xYPGAuj2Y3F3+_K5XN>rYDQ6-PDuO^_+L~Dd{2Z>r+H4d8khn zt>lS5wTj5%bA6g9p_lr!>JYE>>Ayg{)u)S+{h-efCHq;QQ3v9iJ~KDkxj~GWAmAkyoz3qoYnXE%e$tj`gYRi7g$yFOP?E`6?`y!yOC5Wnj4n!`_X<=5v6 zETqqG4pCJ9cJM^%w}U4$|3**#YSmw!d#;+E9-xK0Nvu~^%|H)k0(|}fX@Cq8{ODm> zKo$v^=^@I}{mVZ9)3Q64go5-4W#-N+!A6fToV)%a9nXIvA@}bx)fV(P3|S~7;VeDD znBXoc;W9nh0SK0>{#9?&Q*;Wq+^wkkh@QfwcSp)_FX^cWS582@fu3Oia?2wr@#*PV zQ$S^@$x6?*0^%g(q30r4O$mkRxd4q_nEYza3App5=5Z{o}&H}niO=Egl4A4_T6MESP&{sk;dU+Ti zw+-T3&?~ercY@T+q*p`GSlx+|Qs~tsfG%M-y)Ffi`v&nB=`}ie=DZRxwvjou=)I^fxl>T>0e#2{ zkUIs{bI^wtfCC~}^}O_9J>V#ywO4gBee?#MkSZsAgdyTSEukuXOaYviP>Vi(1&FNz zuj-9Se3Hz4O+s_}GzS0^8k&yu={4Z4gs$|t0^mXFhleL~nCMfo-?NiFJ)h(pSLFq~ z#rw#vOFXhfUX}52$xE=JU3hz&z#;y0sS&Upquzin%?5Vl334=6z%Si4{CBPv2E;!u zcXj3HW+N|`F+#g|f1$E?d-dhkVD_XdFB7~N+=s)9Gj5k3gZne@1k2s2LCpIFcZNQJ z`6w*DE33iSAP@#@<=c4r?JD61jc@O||&g_CYdt)wmC36_8uN!y4 zYb1N(BJjgabXV7U4iF_6qmC<;Ssb0YxfIMx0deBZd^ewhc_|<+7JSPL-pS#`oBD2b z1@kUJTs6UK!24NWQ}7$`A;Gx+I{4wX3qKs=0QCi9$Z(xtZizd%jehAm&Djq-gJq_B*-gn zk}usI1-#GUry6I{-IL%)%=1wL_p*VXNUl=Nu#Uv)s_VJDyh?2o-J1`5#piqJ(vMZ&*}_buO6z} z%AX#KfsJ37Q}wJrJ?RE4#lbJ4nxCu%hcMqXq|uYl;4oRvRUhQ1r=@`rtbT${`*bik ziutAC1wB0g=2Jsezhm?28Ehzb4D@If@!ojj|FbIiAy!@x@8^Y?bt*Vs#vhL%^w~9F zbynv?zk6N)%&U{}g|V9WToewkPR5t=r{`CJ{1TjaydeEWeqa;6+KD##qB*#^WKVn; zQhTugKeXZiQOMp4)TO&Eb0tKdCT7KsoSPawDGfuMo407=>mhg=hB$XO4&E^RK1vf) zK@aA}l!ac3y6DZ^#LGx8MP2lh?1^s%ulHptei*<3n&S}G%a7o}oZbWzi8Xom5LVB` znX6ah!76hKhDWldH!|Pe#am=2}Hx_0 z`fAuZuFm=zG5B}W*K~pCp|7<<^wQU&r@H#;>+BE%^mVYsT!Z!XK@daq^*tdH^$lXo zS9S5a3D+=vqZ?wRzEKSPqxDpnqpop!>Hvs|`X=;a*JORuK!~ZjXS2ZR`sP6pv-B;I z5cBk{Mu>&_)@X<&`Zn}rSBkzZ4q}D=zi4~Q=%|*h-@ALJ0|bJ*%Z3DZ(w6Q7Htr!n zf(8rLF+u`Kkj9q;cXxLW?(XjH?hu^&ubRBiC*E`KdCu7kGUoWLS*xm6$*ftjR`1LN zu~ymH6k>z2OW5ws%C2S*Ta?||A+{^Kn?vkU_T+@vtLza2@BwA7FujMAy>P;+9aZ+B z{Z>1v>~o9CJEQDJ_pNqb*^gdZ?V@r(l;suWfS~KjK|!~agM#iThl)VlR}P7aeWV-~ z)%;XB90u`1IU;nglp})PC`ZM8@0FvXMm{OW1btSHb%yw+9QTFzsT_B?(NIY_fjaO@ zqnto>`FSZ~!_?1PIf<pb*3eV)1T{(>>^vkK77Pc&pat5A?Uw-9GU5G-; zStCSYD)V~uWKUXvcT#V{T9;9HhgG1nQ^C4j0hZ8nIvg?)LgecT4a zdRTfpuOE-wW|)qO^Q|dEj?ZqGF$3h_`|cU0U&C5&Yu1?!Gg6^z$W-4jvj)iBl(CUv z<|trurpAVuS3sdmO$@V8WWH^fni|9h4}HV_hqi(`GX)uDn?PNeni*y{0rg}GHq4$5 ziezeTnEeXWi`UD?cQ?#Yf$^*aA>Y0On0grIqJ#MkV#1~)x{L2nCYNFEb`UStkH>N& zTA^<;>tI*IL1UR780Mp8`|`%gcn~VQ%qre(5QysZoy9>P8s;AZ&1ZUKSb&D^yXb!i zP1AQN(__Pe6`++&PmBWxg4QuTH!LiSW2SLTFANLoL${IXlVRZ`&=xNbH@14le=#h) z11!wS?}kNaufBgV{W2`-0opEU!Yad(w4fVImkdjugLX0PH7r>JdcgF=u=E`0F4Hx` zQYTshub9t@Gwg2--E-(tz8pTE^$eLCO!1z2p`Y8255F`S;AQ*>x6Y6K+$JM1V1GOT zp2LH{ehdnqa)49h7}DfM1>+}}f&cLC?d;1mxs%5DX)0o4C%MTzI4_^j9{kuzZt{rQ z8dEVE_#KpqH-&SN8U{Qf^VT$vVVyB`9{3n@0b>JW>OJsD<_gB_#?QsUXPB#D`27si z?T6F+;^W&*8x=BsUJAa%`eu06&nRX;K3mf?#Bj{`MGTO<{@*kV_kTf?^y3x!rk&wi zeOV9Y75b(wxYS=hg84<_rqO=JuNL5A&V6r-@vAT%Pep2OkEVU$!GGO}3okhNe(=S< z{sQw7ebd3Hif_UMy<@!w{kLJ@j~w5an#OO3fvJp>O~&tDz^@#71{V0g3*+{K)$`IA zzmLXVqk+}#+jyAoXK}%UFMOmf#vibWW>4lH*h&3?nl^jM`-42=jX&V$nZ2btC<9!x zA2-0fKp&JHnf(c?ZRQ2~p#1Q}e!@hXbMozf#TkDt2Il75OS$2S{d|fG?Dhs#f?xRy zWo<6V;jR6Rzfja>K5QRUJ=XYZ1F$&XUI%BaetiP_FgG#GBLfCUGkd{7Ee!L?&>vim z^=8N03Hch%SQbGLJDj$RiKG#yRss%8{8DYm_1L z;Xf?;`Mr`W6oZZ5Y^cY#5_ID?(OW-A7-#%;3-DROG~@S*fbSCK7{3nz{E~3n`27OX zCkd&>AMm}*X#@lrjXyB>{3#vxKar4GE>8w|8Gnf4l&|`r;rzsCY0fJ1Q_%PmBaS(T zT=WYnVf?uVlt-#67=L906p&yx{^|lKETOvb*Ji+966%owgR9wBLNFO%{mf+~v?9Yw zKm`dM$?y|USwc55iu$W2A)1Wy0dDzyl%RMregHY7Y5-AfKy?YjiD0?RH6u(O0u!KyN0WBr0ArpGGS$-ZRD1}Uy+@N++wU<0= z0y;`KN}iJe-6WhR&nEzvgqxHGt;8(X9fKZ{_&sWKtkk@wG-xH}cnPVL7OljbAfTC% z(xR1^2T1Uuw4#;Bb;oA8+>}l<6M0^_Sy@VlW@47>j?LOpI#GMc@}?f-C2CKuJ2pE- zUZVEox?{5oi((w7EIlkkAji>^6K!ZS*L3NTl~JIWwTh&Zks?$PWsWrzhW z5vpJVWq1r&At4QUi_X4ILMHM~25gj&gS>A7ZGBu}PShKpuC2%^8=(kqMrXIfQYQ z51G0e@b5pm-Q0?Y#h?udWLk#E_%|hjwwuU=VaXiES;G+)@~jW;#C#N4^@K}j=4HsB zvvK5k2H2C;mpw928e!OZ5*&0J_9smUIEM57xId+t4(u(^?Gf}kmePnahg9P|UGHmxx1{9V>&o_#3oVxU{b)-0om(h9RagfXuVr7H{M32-wX*yVK5V4eUs z!-z}iFoK$O4&DPxEa^mt;~8+X9;Q_C@&oe>xY;Q@i&r9ef_yeKvM%Jc1I#nvX4mj+ zUKm2nQ(1opnN2TDzzpU`D5~_cz;igg7YUU94sbr>yEr#xCNz+LvMt6Zr8(MY)h1M1gA zx$1-nQm(?__%&CqnIS@yYrP>_E8-mtzqZQto)BTmjnWXElpBKnRSNF^P7>kD%`!-- zyK>V7(NnnvYwH)G-0BDst=ujI5v$y80}-#>$pz6@xr4Ulm*iIN!ujwUpxmtk;a2Vu z#1Q4417etRZveyyecOo-4m3MBD{2k@JxbdFyUPSXy`2hRo_eA;74C1-+F&o57<)ffC$|s?F zuY76&@kvP)NqkXKMH1hX&**-BKb6nIMBsC(NWyGVz90p2TIDNZF{e|$Hi5{Xe9Hil zN%>Ys49r=S?;a4@mGAx#xs>k-5P6j!RUq;!KVl#XDL;kXE28}D22o5Aza?QVq5KMi zD5VZ608v&Qghw-%R|g|c=1S^d<6HFqgHET_dw!XwHN2$0Te@O#QJ9w439;O~^<0RYY`c{KN=oPGdzZb=0wrwYn1 zp%~@729VBc^HP)x?Zi?{YRXZrfq)VcDp4+s;+9eps!?u?OO|pHY?M0=fTNH|r~&0Z z1gI*(MR`Q3*ffV`5apSN)@pG`QYp`SfOKA)FQB~b0dk+bMQO@=4Ss8B+R42XpWZQfN+nHYE*bVNE50M2Nf{^#z~k-MbJzv6MsWdv=WPS zL_=m%QM3}v0;yR@MbSzuayL9=DHRi~MEap2%c+=XC9Cn{YFD$jCCdaXtz3nx!anDbse!g-MWwr~l2%A@*Df*q8N{46`ZMF>q-So!g^%wF;Em8(fz4 z7CcODm`qCr=D*=~<{k&G%-qn_igJrqTaB~h@}WFcfV_v+yg!wsJbl3a92{$8l;<3n zR{)x;#%z=qHErP)0C)3+#^RJW8W(s8phanZDf91OpT}fqG$28nY+boID7fG;X(%vzZmi7 z{|WBQyattBpgOoKa|-fTU?`ZU3@r|zd5ii*7(Xy*CClx$_3ke*+g#-#=u(I&J8Gci39TgJYZ6ND=8#Yj(vEae- zfm$Y-sE{b&VT>1Xe%uCBWayA;Y2&HzI`BwleAa>re*lk_!PA8NU5JX9C)d1&m&RsZ zO*uOATJ>LKY4t~M(%eWDKaFH=sw!}+%*|A#0YnQ`%>WUqs_5+IHmc?Y(O%Wi2F)E* z-3ZY|)g2IBRXq`+hnft-YmQKpdqG62Bg;edQb+cL_*W^o|2s*}B2BEptaoHh9s^fwn#;D_ka-2H85yV7w z0{W17iaJ3Q!gN)9?8-b-omdBAwmQiZVxB5afSVVnlLakSCyN4FrcUvISgB6IKxtm1 zPQf5#UawAdLTpl}VuUlNsM7>(Q{B@BK<-qh`$Ozer}u-{ugR zs56C=bxNIOfjFzqLVq-0P-ml?nlGud(b>#b)H&!==IiR5aEM##T#T6Jd+OZw5D(OO z7+}ng)p^1KKU3!m!~9a6?-qX5D|LaW?YHWJx)2}Kg%~2tsp>-X5c3yxp~&HPb&<%$ zFLjZy14eamWeAhHSakAq>XNb$>D46>5Z>xiL7CO1E{JUEGUUmUQ(cCZV9BE{F9VTJ zT`ocvR9BQo(O8P8D?~JZsVgf&_^2ymAWEvM1eH-&30--0H5!zqlDb;lSVdhUqVZGL zh;h=Yij7B$U0o}ZaH{JtYFKKh>(CM`HP!V+AnK^=Q3#g0>V^Uk4b=^;AR4P1^TC_6 z1gRU_0h_Cv3POaW{Frk+V***7SP!|8=(bFjeJW&jMJg&{oX1^mwy75tT?ppArD7NNrzD+m4$Z+G)t8^dlW6%*ycFZ6~i?@z@x0D0;ivOIkzDkdxdPrXA{ z!eal04r$@3cL;u~oBl#SwQS+1S?fW63H!)1?~t_~kLj=P;NARm8}T&71Hk)uEDhP> zL&XP!53+uXX*w0(2R_Wa!0#X7ncC=U{#_AT&n_Dk-Xpw;e)rnAT89(dgde zc8ukrc|EgH$#uB!iQ_9s->Bq!Fgr4#B`_M75{1o4!dXjv~Rg|fC9 z87o4IqPChivJebarMrXEF!%JRPo>v@y(POtU*cg)iyJcY{XYv)83&N}8CvBvY@{;i zMb?}$d@H=SPh}2+bIahZYMH1k?3OhjV@Dv0*2=pKt-1qIu+}1cb2JLFEDF}jXKPx; z`?#qb?1r@j>-!?J<ua7MyDDsd6; zhjiTkNJ8gjst3piW18iPghS*by6<%f$H?b6;Fg@;g{C225$>K)g=QgN5$=(MY~(A# z$q8a;b}A|Co7_?f&A~1FtqhirO74Z`qlDsAO7z4p5`3u?h7yb1QVA_XrBVRDq^1&; zhD%^|dq@hS(i$kOgqKwMAwW(LL%&fOGa!r5plz0c6|l+)qJTR9xw+A*J(b0fXO){9 zt-4X!Q2;qVY!yvqQAJicKWr6GHu6)lbxze14IGBJzD3YD$hZ^r0OrKDr|$CEw(O0RmT7lrKSp1 zy$k3s!A{kx00v2@PSu73^bezAMq z>URXzhMeA6%r7g7T5H0574yqVp5SJj-rfFGsV=Yuo}30^GN?a}5XMhlCmuw@+J4SAC_3wWjEwrPzI z$O<3dx`x@y_?@g1!Rweaz(ul(w!Tr8Xj@lpvgHM)Fedqstu=5Phn|cv-Zm4=$D7@4 zr(w=(6BWiTQ`;GEf$Z=#t^4_gS%N!(k1)?ccsm?M>v86J#@u9o1m@$+Z5J4;k$*|> z8P+c{&L#XphrxQDc?o7E)kp2G!{vkZW*zjTE2RBG|> z0GCXg4W}F6OW|dHU$|VI-p`9o!G*oQFBCByGMvQ#RPp)mNGp07&Ta+2|IJmw z=tHFr{Jz0v*lak5Iw^ISFU+lNm~Obx85fT79dqmY7%m{2?oy9_hiGifX}Ew^<>&kR zLesQO4Hv%RmZ!gOX>T%I#1yX7i{Aml(q%PV?1Kxhe_!aBHpFmoJubZceW7!jnTEwi zoM|ff`$9LvAj7J7T=4mQ!QI1mGQauwa%^>TVb|%NTXH$jo?kk9ed>>K^fc zQ`EibAZEDLy)__bseAiF%u)AMgP5=Gi-B0A?k^3oMBU#NVwrlNFvLprKpTiP>OtYI zty2#+f!L@X66vO>heRQ4Q^hxXEj!f1c8Fc-;l2=i)gx6Q_NzxQ8dwggM@7^}Rrk?u zkSA1exYTk=J=PxLoO(PD#6|Tu20qIr^+aZftLllm5I5A5qDXJ6Cq-4UN1m*n>IDR~rdKZrT?X|cGHlJHUUWfZRWAu$cJ&gXvF1`Q3(Bir7IrtkdIis7 zEu>yShqV?}uak z8k>5vBZNb}RRkhH75kLd8mic*wANC^H{z^y)jK&M>Z^B}LNrqEdP6i-@1nS@!RkG< zPHS`Z9*WBvqTWMsSzD`Oo6_1&z26I;y0?a@;`bb^ozw^7VZzmiB_`jvg_+Nl+tu5U zE%FaDAFDiR(k6+_BEyq}H`nGInR@}INw`Gj{eYPgusbI5H%G#KvWUVJCrv!sJSGb| zmQ@}!Y4e7xunX4ZLen-KSz+n_l#cr!Noci9H3M1KAl3B}{K#emY?cr~wqU?kIrC{d znQV(dJEUql*-`;=me+O`+3NugNzELx&j1{iFpumn0VgFaB>w=wSqV$Xe;nX~gk|J^ zA0Q`RZC8*3-O(y1Uv1ZrLjkyN%W&(+aTWAH!Ul4d0X&v)lAHqo&;CC+4R|f#CI$Ea z-br{u0mA@t^40b&1;A!o<>p-5R0=E!_#uP+qQE44hQcVIU0MnhA3m^oO3Fah(F$$p zB;=&(=xw$P5(-iEeSpjoicyUmfb0@{s0R9hEtiBcRAUPuuLKMI4HIH3B*8&{!)LV> zl@LaMZw8c*(9KOX(}CpfT)Q4r6TXtItW*Kur`Y7~Tss%l{0We|bL}Fj7P^K_?#>CA z53tF5qN&ygK!AjCRJ%5yhJg0hsWt{!TP+D+sP=1s*r^Nm2H&9J-n!!(4f6_3R zK>v1ZZod6b(K&@pfop>mv$iL(;^y1|e#v|ji%0>&0=@msX);hiEil{z5&kLWZUGa( zsqFo}t;{X)8>>HIwh)N1!20ud`rolZ6W9hN#J{-Sj(jLkRFj7c-!5(1ZWMS2%pbdG z=j}t)ivhh@ofFYj4+ncQ7ck7F>Px}=vx)7B_)+!ez^pPe?TUL*4Om874j?`t)y@~K zHyWIW?=FqavKsK0Z238OML(*6lD8FPwav7d{zl2$c#W%FC}viF4*~NUSG#Uz`uiY| zx6;~m!_sEWv|zcF=FzT46xFN`EXT3Adzg+;&177tz!zMo$eNgj*mx_goeL#jD>Jwn z>m#x9TB{kD7rEL+!q2LOlD6?8SG#D;lWL*U+ngLeI_)N^og2)v>UQHio>FZLFE*Z4 zyW3y)DoV9QQ?Da$X#W`#quTetytfus0GmN|ih~=<;t1~a!q&89b}vA~uDcHx!uY~M)MY&naA)SFNWPxv zzTKEtBE5PVn77u#R%4!6?*ur4_4s89sxQimzn2xZDVpkw;^LjPuq{Zx{zNd(gTvBn zNBs3KO-^?Yds|KK;_qI{(WdvSHz0eXKa7yJ7}=dusJ^IixuYJoo$6lz^p%?3)W8Rj zB;f!xNB|6waFiMx1IQiqu#?oV0ARS(oTY~FoouRvi_~xr;1B7z|B-}7%2cmXqijfZ ztc07?s0(0%gh$j!Sn0`fgbaI5jfIt-Ayui=SXk-V5`I!+VWsB_=umnp6iY!ZL2_K~5Jy3x zpYNBNJ{0s7a9BbDHEResE+L7U%?6y3(2ttE0m!2|IE5En18`Al22!wS8rKALT2IYI z1UDt@q2{witCd4>r{~lHE`&`E#hrdq3%C$AITUwZL@jm#-iw<$AEK5y0G}mXpq5<$ z-z40pmSTkbCE*8!L?*SGGyQnS30=r$pDQY_fAcx}c8`SnG z;BTpUMD6T=+7eQ!-6%l4l+-!*v!^55z$xBK@5j4sygn7SKbjf{*MXO(!VaMuHxN}i zh`~eZ)Mz7kD&PJLJ3x(6!MvCi_8vyCu@&s*)vT~D zST1Ue@z}=Og<(Hw5jDnmY+EQJ=uiT?HnPhtVfIG^O#-1`F7wl&W(8^@EEcb2b!dde zpC$*uyq49W38HK23Fdd0Iy5tJXWYcYbBE?Is!dhsw{Y|krgYTwKA2atI>eYFD5wN@ z7wcn-6+*vJdk{d1MG?1 z!9D4c)rUd`gVV{}c8N3*HaHD-tck$W>e9jJZ~`U2d8&@tc~ z%)gL$=vgpt=Y_+QY6T-@&nMO4;yigP_+fT-cEVG>uDGA23)vF?xW;1bsXi(SFD^oT zgy*wHtB;F8#H!+LeQTWhMCkgePr@LQ)Tf0Y2B=TlLAcdtFnQJ?>N5-=*5T^&yb!AT zJQPA#U*v`urM?J(_*W^o|2s*HRbOK4vrbT7!j4-ftKvYdb*lQRF~kh@bw-HU>gxs& z^VBz95cAbHwILR{)wf2-rRrN$mvx2ub^ydG^&L8{b*=g?9%6&~z7oVH^*yY*b&L7| z&D^?O6`$&|?ovONg4m~i42L+Ve)X+;gmsR(dAjoU#*Gv#M)vpa9?x^BhUDo^RH<86hsyJ6`eX4%14)H?$0gGsTrT(x% zyitE7LcCXhR)P4W{)~qBqW%*3{jUD%4e?7G1f7kvL46=h+F&z8T5WJ&i1gYJcQwe2 z+7LXbEsHj^DnvGIC~man)P_}t$gPPV3AE+YhF63rpbZyPSQ{a9#k3Kz5I&kBk|?Pu zQ4nP`wKPO|P3;O%Nz>4RY*jQ3Ey(7l=_mx7RnuER_-o18#DMJ3l2Hh@Ky73Oh`+Uw z!ndxijYuZ=_D**a+B zYC?3<#-n;|UA6JTkoM5Vi}YODgaC*rZGszXEVf?SM1gUd_=1+Lk2c9Mx&M85k~tpW z?_tW(9FMAJCwqx0-X$N!yX}R!lLcb3)EYg-UW~aB3`T3%BYR0^d=!CNF9DZfjx`OY z)=$CZqy-4?XQDP`fd6K6?*ChS6*;+aNWKkf!_M#Wh0jHDYk_{OUWwewvEItO4MSsF zv;@09^D#KoZP7yPJOK&6fTwLM+!J?o&i-u^wfzp}2}t;3B-ajwXs^TSPbO+N7|7F) z@SjMoov?QeW%OM~!+^CH_KxQtT_*|_2Co_GH((~&ejd05v)DCk{{Y;Y$FiU!Eln8X zEc6%}?UC|vx*2?^BSuDh43A~q(m~&`8!q&g_jk)8csaNaa~?cz$CuzlzQ2eMbwcm4 z_hT$;qE6^Hc7B*{_(ezRgptucls2mfWKlg?9$H=SbBy3f}~hbIBh2scTxm zVyQVnU19z0DWb0LzD{Z$b32f8$sTX0o3IC4q~;5Cdk>IHygf5h zck!sZg{Eg|>OR*E+9%16x{GI(3%)&_)I&V$F{ufo9@hb9By^{qk>ARt0VviY>?1aawP%oO4v=V6M%;j_PZ&f5a_9-!xRw%cp>2=MeGB-k#LqG za{%Nlv*%@s#9(Ppm741miHW6MF8KDmOHmjG?LVdFF-5gC82pVAo>SCnfJwqz!pLv% zPY($9==qVNn}OtMo1Wh&dLbZ-27qls3%*F-Dt~05M*hZibkoP45FSMVsLVF-@C+@yIq)n<+xh)x^Qq zf0=^&zmvoQZI;N)Vr`c2kd|uVY^`mDHd|z7wKhj&ZJjnp=r(9`g>JJpw->}#w>A$Z z-?l@WhnlhN*5;Rj*sINlpJ+RvEhqwUNL$bv;;6PT7sLr|Au7#wT3aOip0nB_7*X2= zZ84lb+huLBD9S6^V)5A5wIw2@TiOy4^<8agd5A~aQbA9(WrCh-?q%q+wwKy+fp4_s zy&*nmD@2}BHF37q_ElS18RCbw66I_+XsfIcq^&{{c9XUm$qZrEQs5EVZQ7P>5DslibBI7~E6T}UL)#h(QA^vF52CKN ztsO)IZF^ye#@hCd5KXlmV)M=(tnCokQWL*SW^bkK6!xi&CVrXB-d@`!x?D$X_g@g5 zwcV%~dsl6bFGLS*PY;L)ZLe_AqO`pc5WO_~GMT|1r-}VndtYro3eTRT?T>@#uN@HH z(;)4DD9Rz)K?@EM+lOlhMG2|eA+&Y7j@QwiWb97%y;Ho8Jn8GUZI6hZg4dz{po@c?4 zVo>5;jGh+}U!-V0JhSh46&Z@0g5w!>p4#`ki5y2>LxAu6K=(^fR6*b`#>Wv9)d3cd z7@x;clrX2BoSWAuov6p)bj6VQt|!>U9jrGZeS$FT@_35f^xh;_^l8u}#6{IrTD#J$3@P65n6RM6s8E zRT-;AP;U%${(NrNWsRfW@Rj^|M&WWG^4^QUJfm>^jm5U!Prv~jc@sApLYye68XO&( zT^zcZe{I%B;%r;o8gM=4L@dC>y#@0W!ZjRc+2UaZ{do%E8j1Cwc$i^-oGaVp@=?^ZXzudTVm7!QsgcarBSBYY;0IEo+ zM!hQl%o6O>8;#muu9o6id!Gk5rKSnRp||>1m(Y>o&~^N4O6W~-M*)9G$Ni5aR9~j* zruh6wRc?^FMpC?JoK2->6vgibz+1)vch^|zgI?q>-DTGV>I2j1-&Tg3Ono*1q`T~z zN`28;{X0v|4C>noAl+rxOzI2I&R@FAt~u2A2OvrYn@0&v0KFtEqy$(y|2`5HQG)0V z?nFu0HmU{cFJU<)qSyKllCX*rp8}*8>sm)i@GShL7wcM2NuvPCGT3HHx(ygDVJr2k z1Q;)2JM|k1z>WwWbr_9^ ztm`KY{Q~$TgZ-jm4FF#RL=X*|1^6MsM8jSi3=U&T>V#+6aoVEO6mQG(c=rdk13xW< zkZrivPsS#2pB>=#%u|hC)aNI-3-dH8OML^uU72UVmiHaxN;l9U8z$R8^M zSmbtXsz*uFfg`yzHXGhh(hcw!<}JuwKVh=@>%*??Uer$vZE{1y?cv&ir|UN#7p8Fd zT`28-_rcSdcVma4zc6OAIJtursed=%Tvi`8UZwuazzdjzL0Y{ehwnZ{+kZWuSqg7pJiLGEUPD z6n$U^^Hq=Tw($YEZ{orc4sZv*{4%I8n7-9eAQ7nq;m_{ZRq;LFTUk)Odm!Tj|Q*NZAN7=D*O zdrEHCE5l404A0B|Hiv(W_=Z#fv!~>Gi|h@F0Y75>J7jOjdhk;&j!!-`pt3Trq z!B9UiJ4&uEDDI&NV0M&T-%Mj@=r%BSJmstXdCFJ&i!2G`Kj)7VI{P^7a6dRk6SO0k zjMyh@M=+Mzr)o!qBRpL@imJ5F(vBg=_PLt)aX|Y5?Ra^JMcVOR5X-a^)gV@CCtyp_E=D$_E^y0+7m&w zv?qe_?R*VZt+S`E;1GIN& zvi@%Eo$#iIXzwi$!?pME5UTc}Duk|m5QFI`?PDc~vD!zG-wE0$G+_Tp+NVfN==`T@ zsltBE&{9P=o}Ka|35hwa0FXJE!h-J$${ z4@|>NxY77?+z zJtC4|!IXQr@E1ql4;D=Q3+%((9~MmQ0xrco02WML3@*#byM0J~0<6I5LD(|ZN`osi z55~i35n$fMh!}#W*H(iq;&!AztRiV|z;+Hl0Fis99vx@OUMfYxHmm1jyht{Kc?uG-2IE&UOqZh}>vy2=lHp7_n#kMT5yyNe8P17=ryvoh zO`~We8li)yAQ5MyXyg!JDBpe#FLR7M0A^n&;yR)mg@){4Unk-Vk{?wM+);*)By5t5 zN(Qs<6Pcdc)2LJ6uB>-w!Yiz!(P6=7?a%XUA#uWH6tqiY#iP(W0_r zW%eU0Q!5C8ko4|hdc)xk(Y))#0}ok3(Zd>MBQeVP>e=s0J$t0VW$z|xpGTQUlMzp4!JBE zkwD6DKw+s#BIPomn1p_$mIC}C9rr(yPzjmp08%j^J4#C!Na{&IISGSE!(ipWv=wu? zh{2@w0?B!6#8A@q1FSOKFw%1Z{3WQQ!<};kNJu7qD?py|i5N}E831*pW;rE`cF{n> zHcDOtXe{9vjl^K?keebAXJ}+Fz}-@+&eF(5pjHyD(8yFkTM1vdm4^w4OiQC?KqIG< zk=``wC7_$sWTnvofSwZa(dcmiIh~9wN~7-smvAa80y zV?=ZBFCmP^inb=FlaY}$b^t(5CnE>Y*wX-ck}oou#uWv~>15Piz^09H#)cbX`=&;|)TY2p`voK8kX(4+={tx^+BlV$;S0>V9_VrkNA z&|ayEqsi3)2PE{N$rAuaBqY#e(LIh!NTMmCdz_ZgpQecJabCh8nsOa*Nx~4CS`Khk zN&WQ_f3H$ff4$CLgq)Ts-bLQ@cRP4fF{)e%8owFgW=m5ORl$eGe+JHFtmH=%Y`_JK zRZtfFf%yNQ+`gE1OQWilpb5JW*D{XX>_rm|z?F<$O*F9vaE&xNQQcs4Ck_Milq0IU z=`&3{1m4K{o>Ye>r33RJDwKLgngo}`v7Pl%#(OkrBv{Tl+#XReIP^71Sm`|+K%A^MT6!kNYpSdn%Wu2A25s>?n6^WFTKNYkHEuEeGKLa zNR&#oX__!|k65qc;itL4Pnk#JMB21fU|yJOzmwi-(_H3CvTFsPS-> zri(81fg_j*e_^_4C+r zg+L!xN0_$I>_^~Itd6Zrb5PVyUYm_hK-TA=sGa3G_&_)Vb1)b>d2KctA61z17Fdnd zBaqnKs_9dV#ou_HAjjH2&gcB+YhQ|EJYA%HflueZRQs9_V!8Ge=Ei@O_AM*KTJ2jy zhz;6zG86^Lm+K+gMJ=)KT5c{>C$fo}x?N>>Nf0cs!zmvpK z?H2|u{}cKkABfZXpm2yY`e5;7=k>vzATH@cia=b|hqQyZt`E%*am%d_Z4G%>AC?#5 zp*{?C>;G6Eo(tlcJ{*pd|4V&@D1v@rQU~I*u6jaz(^XL<-*wFh z@k`g7Xpf|8!hm_|Its{XY)W_nn9cA=!B_PV{C=-S!gcYfB1d<9hS0h6 znZlAs>N5vG#OSj`EWP#F_@P~cqmMpYV4^<90MSpMBgn1K6*NSjD`>brPmrR|6A9}2 zynYa)^!cJ>$LjM%f)n%wBEd=e0#UM4^o61*r|Szv;my*;uL(Hj=!-;g&DR%;0$QXm z?tqhDjwSk%LcrzvlGYHb^rg8b7kP_=B_G~Y?@i9lfAS>n%3)8XX(Ar6R_YO@(=<_q zPb7?{X@>!F!#`>)O&4V@mrA3?({xdP?`5!wG<_%FlZ2%-BQrp5_(!d$8L$$LKcwUS zM-q}J3!?VWj9oAvMwy?zG!r9~(^JAVnu*5b#QAl2l2LbQ=0=cop`+f=EX-G&@?=5O z4>pcD#7)t#l*^%!E_8Grn)Mw}P-;5S?8bm161vfBRJHRj2~jlr9iXIyIGR%vAooV1 z`_r7s0Cxqc8ccJZfGSH+X)YRqQ*OdUkD|F4dYv|@nM8AM0UQ#Rkl2iO21?jP^M(Lw zNjOaNE&%FEctrDIM4ZyIjebS*697%5<~_|nh6d|wCg~$BC;(_7;R`K@0<@O!l@{y) zv=b1Mh8AW6bdZpP7NUzfyGZb%g&5|X-6c3^5jvmKCE;&cgtq96me7zEtpvnMXzr#( zKS6yYb*05k0f`b~Y4Lo(011h-_ya(CwlQv6QX4Q#YKGC0sQ^WSPD`Evk|m6xrTzfv z*~YA-rK17TvyE9rOYZ%ZXMAWr#sDXud5nJKS&inyemWcR?XOHDY5s6WFD3^!LEL)e+MPdlBNtt(?5o5!16WE$#~BGmkNGCR)4{I8Z)q48CQ!STsLgD~K6@ zQ-I=oaZX+xYnh2win`)iN3YGee`R~HoOO7_ z1{yQcDie6G3?ADooK^`lz+YmEotl$YsX*Sxh@EPBPOFZA&&bqcr{Qa1t22VnGb>*w zyu;u7l&=#$WU)}dyg!VK&TXO-$Cja$7jdK9#*7W5Rla~DQqzo9B?9E+GIlDhIu1A` zHPdKyLBKf)Gih}+Ku#`W=hEuEfNMh2do79Y`~4{$_dk-*O_}OuS~CEt-V+e-O>0F? zA4$keYeh~m(4&3EXQQa*FwoU*9OU)tL`VbHz;RJ0n1LTk!f8I^oG>{x~i{|C_Q^=wMReif zralE}yNFIMAoX$3b`f2a)Fjf58UVR~)MpdzSPPJ&Y@Y+PlK^s*?Q@xSwgSlYqrRDF zCkFZeG0KK}^sPs`a)abB+qWt0>H*M1+5q0yJ{TfM@6?$-VobcqopY{fEv-I>W^_d|x=A># z5tj6p?E1Yon`jN(6T`n5-pNz`-uOWzTC)kaKa|nOXY}ksYXfn5%K3yZWcD0MYX>91 zOXh5zqiO9vFi-L0b9j!Wbsk`z;>YLqTuSR|f&! z!P)l>7!w2dG**0BbSHS00a-ckVu^c$XxrI2Hhh75Bg|$%E>^e3hU!Mx&4B#8*%IFY zhH@heXFwqi&;^CF$s1gh*@aPTQxhY=e1Fdlv^fCG zbNu)ro-JuJiZ-AUN2g=N-FyIycRIv7ZSmu4Q%WVUne|h!fsoQ0>|hp$6;e=l0fEem z@w*`@sbKbp;_(Anqd2x_0*n0**xWwZ@x0r+;6gVUzE3d|Z5QT;w>$bck=^a^F#@7x^nDVLyB!vA zEb}^itZc^=a2(&h-G_FF%4h$j&ru)RnIFjhOP?#1X=f)i%p3CNzA0bRe#}ywoO}Mb zvf@~;FUyQc+$MclLx>c8IU2EJo4y>a$+1IUfuY&4TVL@v#6Eo`dZ*){zS0SCSYO!> z;+Vb)8Fif0#Rr`nXY|!oAkOKlVbUEJ^)(eC{#6R@|4tHDbnz)&$8~*eX^5Np+HMfH z^>xJ|?&<3~LOj&h7lwGOuWtwOOc$Thb-Z-z;#<0oH~K~xMaMgRBf6a9qrM3}(eYW| z)EMHMzBxU_4}G)nOALAnio{8JN_7a6o+4bYwE7l1M0#DcNoOW~E1Ie^tG*R2+?ie9 zRt_SUz72VD=GC_sgD9YH?*LIqcklQMvZ%g8R9JC+ryyT_Cz^`0w7v`3cb3z4396{? z_JydT@9qxar|-doI<5Mi2nc_DuSg<57l(hHHS~RgYU=w0)zK-AOs!!$b^>IXy$ zjr9Y9g7kxKp=_>;pY(Kw=!b;Q&`Lkl5~8hsI2%NJ{V)o_*-<}&R^sfeA888FRX>UX za`w=V)`y7DkEMf%(vQ`I=%pVwLB#3D>q7L^Ph@~d(oZym=&zp?vAFe<%^-&8r*dFG zb`IB1wSZ9d)2IWduAdHt7^R=d2QgMZ(*|OKepb+A{j3N%RX-=PHbXxrvNl^kkNR=W z)6ds~SfF1J$uH6`)PPu~Ulg=LzbNW%wSGxtbe(=lWP782*&93M&J_K!h-RyPB@4t( zNoJOwZ0@Dn|6kT_T#;9kP<&GezY=xG7` z##F+5;2Pj+sZGr3L;ErT*U7sR3!-fHp}hp~jznS!q`q$gn7>h&Sk6TIE&{jl-PL?( ze;(jYzT1Jj_oMOy_CSq^O=2zRg)_E~_2Mh*)>UAV|3D??_l;!5;x%`z{4=A0nhpNoygl^ zF*?0s-fwavzwVHq7ke+yt&CGm=hj_5#kRT6L4pd%-NKREP5 ziLlZu$=*gaqlR{KJIyaYHmb9qXn zujJGWCnzxC4->k8Mbi76PJ2abStel~?Og>}DIhU7?JEF~i#LfyXrHLo%~DgE_U!@4 zoyf$hv>#?AV29K=X}@SZa(b9pm-b^q{-<=@|42gnWfp?y0L)duVF_*MKx=?JO`8}_ z2UY@3$_ZU!G#wDG&RH35C><0%PR>dblj-1mz*VW4NC!UvZb+C#hhPx{?nqcfho%A^ zNLWROo&g?9*hGi@0na4tqQj#BFC`qN!=mrG-$^=6M=FCpO1Ml%h5_U>GVwMYxdf2Y z$i$~~v?MClAcK9Nqe%b{2|wxR2|!u_N$KcVAwYTwS?O2|AhU!5bZj3Wn}iZ{T#O%q zIVF{+<6QxA8kuCF<68g)q$ZF~V1NiLBB4H=5G_@%<0b{uiPZo(jZA7w=pu%|GBQ{= zi6JVmyo4w^xe!oELIR!q1dwyeq`_`Fg>Dxp7j=^~IyD{OFT;(eQ!fDWghJAEI*slf zSVL+SlK9wYpj^~VT1lrx*QhTwo9IkcKw}BJ=*$Q}kc2~Y<_e&>gfn!uG$0fZ?vZqb z&h`hjm8yGm_7p&#P)K@4=ZXM2NzHpY7Ypbr;X9o>0O%>8pC_Hq1&EZ8k@%e;}-8TKys1$bj_vLwHaD-ufd<9h?XBtMwrc z>(|mk9M!MYggBvJ6E1}Nw0>Q9U}yE~2@n_b8)%Ns%leHdh^zWd6qob5ezP;gZT(hZ zh`ai&4iFFY+bAOEWBs*5?@Kr#Ipo<5+2{!ElbN&UGfnbP`mLFM!pV$i9eyI=HytgOE@LsZjWq7VXX z`YWOI*Ix-;fc{$OYUr;MA!_Px{2}V-Zw5lt*WbdK4QQml^MGiozeC9eG}GUwfoP$> zM*|NC)jyyQ1+>vWG=ylce?)}_bksizThm$pYTf=25<1dY>w=7bop z|7-~{N&h9xK)_V}m%tgxgG7R}k_U;r%}pMh4q`#_-~fol$%92%EKMFFigZQt5K-K# zl85?2tVtf)9%4iCFf@;VP07RH5(jKa9xlA6?a9O8F9+;O9#IlvZ}Nzq5C@W#vJi)o z@hOR{hJd5VYDM6QWHlDzbjpw3Uosw`fDw3O;AC7cC&*GC#`Ow+Cz*f93kaQ6YA0d$UCGft%2Rb_&+@4dr5A_{jM~>|oVmpWG z5A4F~cRqAZ3^6>1Px=B}3hW_6_cI`}bI-vMGIYPRK6D=4I8aXM-5&iiA-40P=J^a` zzg#9dzaGeY68#Fm1w8)&+?Q`J9!MAdPjP1+9aWL<@q4SAKm@W3A%@YB?f@YQN){3V z$mCuGL~%g{R1S*5z(ho3ldU^_lLiFyW8WGz0%3{lf&?NEk|+p483ITavWJB1B;!m3 z1cdjks_Q%Q-hc1BbLNkn-}zM4ty^7HzhC`o$t@_XCXX;Wp1{+Wp}AVTd|No@-79wD&eBdP%H}5o9ewo2B+RFIkNLwMJ$Dtwe zM}SvLo`{CeUk+X;c?#Z_p9_}r4#!O894q(}Sk5~f_*ct<(Lg!xa2WgqWq}6)a^m4w ziu?Bk*qKUEYCtYQbSl_HBjzKIQF7AS3)9(ztdFLM`ij>lbbr;DM_`RFeM?- zdJu3!LVy)9eS?38K8m8dBy_?krs%Uo241YO13-+L@i$llR7ju7W8yEehB%bNA;!}% zcA4usGQhCgpXC`0Wj=d5rkV7GwntdKX) zJ^}mL6_`5hpnxOniVJX9Ko+}l0q~80v#cl>a9qG8Rx}$R9B4-|E8=}k__dB|RvZL4 zExcDp6XO@SYG=hmt*kf$0LK;;bdQx_l+iA5aC)&4jL_O;0Y0pRt4@J{AXbW&(250g zWu;>PWdcH3DOb>|0>W77ZNN1F{aG2tG0mtKG>nyP0yPPEiIx2m@J|8rSov^(*dB2% zVCA0yZi*NeD{lha7LdRy{tCD!z|AUFq4}ABMXUnuo!&;kQdZd)&`v-StNhpibriIW zRTcvL1gv6Lp8#|cu!dcA0v;Byj$J(u__KhGtjY#>Ou%+lH4}i2ipn~|s!joV2sq8E z(HHbk0q0pY`hwm^K;8sajn1G)2r6aQ&=>S*0aw{I^aZ`YfI4;!eL){2;2x_%U(m%~ zNqkpU1Dm1?S2q4}R+9%9&XdJQvRaI)`U~kdtL}6<$MjL24hQbeH*~rF8b3k}VYT^C zv(|_3sdXq_zzbH4$r|SV76!x+u4w5}_kVz?DuUu^$0T2bEh(A-&X!zgoym$0f#t%0 zqXM?E=muCW3^?k%S#f_L76$lrOpa#67sFm_a%I5L3gc0H0(@Q?i@PDLguh-MQ*>H@ zgMo63(fJT8Q^_K5zDVuF=h8}e-@YOW;|xOKO51_u>Z|iHOCc-eDl1k73=94#ErpdX zLqMf`ptt%8E9I87S~CB{PMJTrR`LK?$}(;m8YB;er7YV3mb)0v7ceH4q3>udGX7Q6 zM){w?|B^fpcN~>Z1pflYS~R-xr>uMj0&dA4;OfE3Yk_wpCPcCdIHQ`})^NJ}u?l!e zy8KYCbI~wXu@5MZEjmB`fK{{r+sbE`&S8}iz=!0klW^d(5=KCm>jchaim`=N9zlSw zOu#pfD`DL9KS^GN&-`8;0PZY#4YnOb@0b7N;asQOWLGm0k8KUKq;mrfWmci7bm=`g zldVfw71~O-$@o-ky;q^Fbh)eHJYo!IRcFBR-^`t-A7j;ABjrIx=Q%G{JqP%deEJfW z`l^qD!zJf?`LOES;J%VeaT4I#(_p!&;jBcR5E+ z+I8Unp<-O8P+9R~&~aE@(1z?g_+VJt-So3*zu{wH+9{7#9kA1LV4fD^InX-E%g}nz z#%bsPq4a4O#1r)CEQlxRGwz1=q0i<*L=ZpRsznh$H>brAKR2fhpkx>{Z4iCI)kULC zaQw7qXw$P0!)WtE5W{IRx4R=~3%Beq)0S5tUZoVMyEd9qxW{0;PFo*_97|i@gm{a# z@y`4XZR3vHMB4r+#3b529wLr**dgAh9lU=}qtpE50V0q# zbc5(Z8!)%f9wARph^}OKxZe^?8)0St6HQ_Kp)LMjnnIgJU-rUpnM3@Tj5eQk_JMHH z&gl>a?c!!Xk#^05SVX%cc^6tjyFY?RqCIFsZ8_~(0I`zx@?N%v_VOg_X`cqMk@lkv zwa;lk^ho=HzT$mu3w_1=+;%#^4e?Go!0qoII@keXKOKAt;sAZkZR{cX`Zb6nbci?M zQNoU^zoKPOT4!J;rM(XE?ULVq{4M>rAMar|pj(osL-+f$4PCB}yDs8Vo)+{^JH8CT zhq1)|lRKv2ViRl*@L?>mpX!5tV~>?LjwGD$4pxq!>)6bzQ4-Q&pjx&=XJ3~(%D-Sz zmkb^!`2*amT2~4lFL{PnSB4K;8j3za2F!ttsecCvCQDv`4C}XoIbk7-xNuA?n=V~q@W1GiFLCj_jQoCb^CjQZ6*lE1+Rz2DN!T zI7PlM_e0iP2>j9;t*0spZ9=+$es5bggta^d-et!6%y|ohxg(Cz2p1*Y+WvoeFt$w7 zg9)%UThXU&IGrFonn3j`ZX!}_H70je2Ps+LdXpcw6f3A|TZPHJlnbg2{H@6m%GQqE z!6(7u{m`~G1HpMFPh%S_E-(&n$oEfI+giRwK(YC}Cn6dK(1ybt!l6s?ZUZ;M)@0(Y zkT&BLMZv(&Z`Mh`WjBWr+iYgO&zjsO1(kcyRFq2PoN@@789LfbfX^U4gCEn)DLDPt zp7;GbF#B+dO0$)FvaXA)1$J8BEFhOPUj`%#IL?~g0C6nFaG~pAywq2Vs7>tp z6~GDsV`Q&Q5-^H2-9n6*=(&cnrc}T}5z~(~jRLp?L`lUF^F~)BYdnCM*LY0)>#XiC zfH49lG5)X1`kUq2eqg@*z)bqVIW~42_%k-;4C?;> z(0(?Y(c>9RVJWaew)2vvvVQ}9k}%Bzdt<|P2v7H+#RI^02#4vG7C@_w;K2zV4LXo?)K_QbaneK8P(}wG)v5< z5*`UcFR^3Nj~v`Rf+k^DQwA#@#nNi2vlLs-FY$}HPku3K7XH-R<5_TiR)>xF|8muM zU@&wdlLyDjZt#x9SlOS2c7)E@;rt37sQUT9{Mv_uVKNejT2;$e;1Op0^J*s_44rnk zzasreWvfpO81Ap&coyobW31|3W(F9_QeQNzeUvmwiHSbG`+;N4=tS#H-(SFQnY@s_ z;m0cqBdy49vD(3JF?f>s_-7&hJ%Dj$^jAuX|7({9X^0d8yCZGlAy z*dcA4yMvOVo;-*E?4@&y>Fyk$R$#3OV=2$T{fJllphpy?$8#nJD_es`B00|0isZdQ zg4O|#nb}3--0o|q5de3e*O&WUtJ+YH-#?(c84wquRu5B@;Hl;lvrvHgEqLRg-vhkr z)cSH<{`Qm^@R3@rHlXiCn^)zz=c)ec^)v+ZG7IHYQy+&GME5ldl@ihi{X3culKeNM zdLU%Gn(pS12n^!EKbg7Rutut=4&@cK zr=s>$!_@w2mT$7E-XW$R@Jui1F+Ix}%2Zu7^gUD#<=1yo8Fx^2;66l%ZB#lMVhfda zhWLU?IDJkfoHkN1rwvrx1!64~y$P|3ir{|g%jpWHM>^3J^ca096>?8yF%|NG%S{FE zK_pNCW)!-U_?JF(V?O2Mcjh#ES+0HTgEpm16vWx$$i&2$2#(0-;&7*?&7GKWCipe%GJ?N`bg z1aX(L=0n`4Z2ntR%AN&brEk$?buZ$lp>%Kh4u#R%(|400e25!3-Jgt;D7PL!ryhj} zq*J^uyU_Q65RcOLuR(OB)4YA`begwMcRIt{peLQ-DW9Mm{#m<#bXT^tDU4qbF@*tk9?E2lS82mhV*ypYvl9aEoWhCNf- z^YO~U$Dk!N`< zEED=%A|WH}I&wTAAu~J{ARPmDW;jY8{Jo6H4zB`8U(S6h0+kbtMPr^@eZ(^Kp5Tj; zevT*uVAU8=3)@CU0t!We7Wyljp97Uh3Q)HE0;-S+{}P!4sFhJY*eIw$&-pT{PhaFQ zpjt*nDLeKdVT+{U%9nniTkU#^xBV@u3m#h`3((9Z4L~!N1c9b6@dr&?;sKg! zS>k{orD!f_a?xrI=z6-pH@&8bZUVw=#&D=Yw}_`^P|NBKqoD>gdmu>n94FCpYCaVeq+#a z`IN!fMHbL8i>OvcFOoq=a=2Ze6kvk1^crIm`nA-|;SZ);P(1kNWhb-(2I(Px` zgBDO`2@6_-4qQ+Rbie|tr~Wx@DT97F8qmJk<3ammHv#RPO(*rriUIAJ+&gcQ!DWfH5#|*mT4jI&B?K426cIjh4+otyfjZdcrZIfOZ zv~}7%3Hi55n*`7@Z6Ih|S~O^jw2Gk3Qz?Vk)V`oGsbQeeDYHPErI36yO`&^jlHvo} zIGKtbmE0RNGPx~iqhxCDhRKvoL^4TCcoO9jmPEDHAc1r20uU0YZ`}p?XP< zpmnWOU3IJ^2eqwqPqnO#K!dGxXEm*qXbr0iXm#}?&}!;X&>)rSpsLy)w2B%C8mN+J z2TTDmQ!U|d^*XB9RfPd|2xQG`31`U1@^y~CSw7A}Q^^a|ja?DURyQ_9FiYK71HpK8 zqX|Kxx)Ftx2dW!&2nMJd(QNV|bpt9zE>t%_tmF)J14=Ec>INf%EOkQ#1RK>2$WET7 zu19UlTh;Z`5$sUcCnDIduJ4CnifUPpT9qfN>pLPCsjkOu%OlkFRS~RE*P}h;P3n5I zyL?bxPx+RpWt8tqwG8zt_gBkM`En1n3<4r|SIa^X%u~xywenoGtO|m$YMCCvaJ3A* zMov)6+z_OyWvFp^ky>U!Bgt#kQnb08rV#>Uz!MNxzq-9$)y&cyDwRy5$w4H zp_bQO3I|<&38EtRzC;Q2yabt(KfMH*leb)=0&TuX($e)JwPB}={XyGZq?RwZNQFzk z*c^1id20R9=c)1Y&rz1i=cqzgpQ9@HHf#9qLap~q&pb8j4s}{ zv^HpF5jF1ALQ1r>fa-Qg{shp$c}CEFxn|J5*@Hp*WYam@vgxYg>`I`Av+Y5PvZ$ce zER0EVmn^EY@=VIOZDs?|cnlMYe00}a7gtOP7kBC0pK28O!mjt8cQodPk&$7MjlvuF z$eJMkp<_oSj-Qe+Wa_A~!|I1bMn=|;*jd@xUC~tArK^G2L=#xJ8Um~dD5OzbV=j1r zQBz|o_^t~AW5Gp~T4N}WLpdNYXk{b71^aV|YTni=;jax42K;xQA3F#J219e&0 z2h@3CJy55G?x2nfCV@IEK)q`07oe|c>=vNuHQEJK7|jBRu|`>d=|UstKw>mf4$fEJ z=isK4cR4s;d7Bdn`X&bjRsPI@q$+>pkPs`cv(bf=SJ^19@-mw)_&vKJ=x^D9pf9qK zz4APZ&U}^yX;7YKbpU;mg?m+g&4m0bzhn*t{W%lDt~}0+2mOi0edSSR5a^FI?kf*z z+*f|cK%*-UGANh(8SxlRm3tW&xRkpz2`JyEPX@h{j#^Q^OQ(cxr#AupHXSvu+)9Vs zC^yqRKyRdDAXL6dqw}w)(fQZX`hi|e!cSz-X?VOYH)1HWe+Te3gn(Ncl1qolrTG>I`~1WiIHc6uRJKN`KH3DRll9DU{9U zDG{K@Q!t_^pQTWh9!sHGIhvw?9-&D_Ih;&&b11nh=%>k)%fV#yR^^jqG@N2Nkc^g7 z_9tsV_a)H<2~Wx&~4WCpj)kVKtHxZ zhLkN5}8Rg)F_j(Z+wiliV3p z5t1h-)3s7EE^jqW~R0y^FK%EMDUqw5~_f#s-U3DPn_iA_0J1Q0HI~7eJ-&WCE zMGh%{u;xod?g;_GAg2bMmLBgDg+|m zY?pJ0Mkg=b<<_LSMj4{ZUlmFuF$m=YX@K0~f`WxVk%N6h)&M ze_fKC?&j}^+av$6YedWYvV0@IL+hsU`24KaQy0Zd*ZvuynNZZFPj7wEVKubUuB|a$ zH9G2hism_}@zE|_8;xu+jJyQFHY8re7-$%L0fjUSynu!=^gAB{+WQ;^9z&0FxGh81 zvyeMOr?2Qp`>*J#_%BfnhE``#Nro0@AcKbJ(_x@ZPNDe?k*6q~h?8h|L+FVF(0V7( zDGas0XbxKQ^GTqVYM-O_3{{S|05yMxLCB#03|-dXdklTU;ByphVW@Z%J<3qw2MF0Fi56~wEaOe8R`*8#M zhx<@m{r!Dte*IlbIRsn(-QJO)H}^s=^w;;GN%UXuMlaG|+U)~+epejmmpf4u{pp<$ zd;J$XP+a|y?V~^sZTA8_u#JwCZ>4i~Z>6hteB1lU=Ve)AR=(Dj=!wCGDV^#fhA zNdvlSqXpHjU%r7#v}6M$Lce%DgjSzdHX3w6StZb{Qrx9Jtz;HxatXv(Z(WC}SZ`Se zDbvqcOGjp|!2qlOs2EbLpHN%_bnI&MUj3+5R1?Ejp|YEny0*zWsM;b1!3fdr_ zk`7sf?xU}>$P5~shsM%Z&!x<&+37#H~>`0js(?Z zVf4^D(JoGJpV=N%lYwmf-)A6W|F`ML*#D1oRGt6JG>Db|a~k0MpU~Ll|8olN+y4=b zIsQK+AshdDG;R6cp}mp+w~&#E>LQfHFyYhy3>%WYW<4!dOjGl9?dMqZY)aCL6x# zr*)hH?8i8Hw;bUx0=Tjb7rSn7K)=yjyln>x+-D_YM8}{>duv-f3$T%s(b-f*xcPeA+5ued^BvnB+yw7F7_8Epiy&d0>bxRHH zKEx}w_(^i8drQ>c3&!fl-ahg@#LG4uEl0>yz8Z|@Z}wd!B?C`Ga*8a?pXlX7(lUl? zSE(Fafl51CK-b&K!wTC0M>t!k@?8%)xK^N^IEJ(Zef%c7k;wODi1ncxRctQ-b-zmMT|Wct&hUtI*_A}RE5lierHyMt;7*JOxpdI| z4A|BQOMFGwT&G(B)S4r8@*y2bW~_iNdn-0^IRF^VaFq1U1>=#v2}7@n?#>SYBSlJ1 zPraQfr3ew}_}**0a~HrcXRQUd?-=Mk*4Yubo;cY7qD2V@GU}#tqI3-c*=wR4e?+T1 z6$#ljRPr3PfbT@4_F1$8F7*GCQJK2~`l$ayajIf<8=%GD?Ei}4IIX1h0JQwhxQpjY z%^Bcd#I;J}Kur?RPaKI6)A&8Nb8?<`jr}Izjv{L65Vt!6%F(wz6|m<-xo!tYp`o%! zCN9Ul8OiJnff`j0uG<)Bx;Rze&1HY(ZmuW;7KDc->9G8rVo(aD-bLmm+OWLN9{GJc zCu#DCCIUUFU^krS1OU9>=e9f zrPkoL5%1U3=Lhgxh_7t$Ne91?c%SP&Ch+Ts_io{h8_^ewMDq`O(>R?%5uW*8m%*<< z1or=ne)h@$S}IOkw80Bitj}lA#q%P7O4oRld(wcGDEN`?W#CX$3M#AwT_1FN_@dG`+ERh0$7rB z?Mr|>I4Z3YO>J%r=GuD!I5W`OtpacmAgR6`4I5JgupZF?F=E!Mz4L z>!uH~Gwh+W5{Gn~g|m(mAM>)wx8Oe`zV7vECOh=WrpgF=q3>$u?}cFFPegaCZ_ow(*QzJt5bU+CrF$tmMG1ba-l0Ukd|tzXVDqo; zl-Kr`R7qdI7!7(-U7Ltt*^<7Xn;wq>J##n+v~-&<=qG=oTK(6nU-v+;;R-s;o#CoeM`$dP9riABdDit!-_vvR=1%?$2L;8RYlN6$VaTBx)S0Wb4y)@62?ku z);yq>YNmxs6RBoKAZVy&(mi!n(~)6prFRghn8$ai&VTwA(h&3L7S&PEtqGtGx5nVe z_qT?E54nXQC${6QE}-$Zu)2?}d<)GNbN?GOd(55d=uELb*XZusU!#_2a}C$Vw!Q{o zim^PrN_BPjY8dEuS21wMx?e@Ni*0rlcNX*KTHHg-E84@vyjV^x=CnKtv{oTWRPDlw zpmi2f)7MRL2Mtbv5XU@EaRL1?#STZzDQL=AQ}Rck#^iB0{xq2crz$=Bin-}Rn}V1d z$vDz&*V_a*$eL(H^`GRW7iI>Z959W7! zA)4kt_xgic{@8;H%rEyK1M`dBD1`axZrqmn*IlhaAMetF{37S>h8#KKbJ=mPK+5*Wk zTUSGp&6ZUYK|XYIiy%?4!A1Hh*3Gq0nTnl#O%7g&w!X1qf6Mpe!@0nv@L z%P2s>8vdSse=uI>Jxt$=HV?m+p6ZGhdS9e{jhb$-Gpz*o9l9p<1#SQ9XQv*=&4tWN z&99EPdP7p~48&`_jw#z^yw_`z{0@~_WLMuffOMXxM2HR zYe>FsWR@kmjth2Rnz;iV)+rDBBIjlcu{@VGvp>@+*B|4S?~3?YarQ3Cmvw-f3p_ne zTS@u!4eFv)oc>X_EK=nF&AZwtrLEbVnzhIQRC1Qj~q}K{L zLJ0^!tDO+I+hS?H;&B1Ey1-ouq&12LeK;&%;7rrD zx$b9pHT;Hapj0dUF1-pD8orXqOs6e8r*%jxD5h2^z@hT-ye6g|wsTVjQSb zPvjY~$6}P*LXv{KM5e};N`L8;1}D^=AWn#!rIm-HcUB!D;(dcqs9i6RXUGK+AN`|I zUWs8qiayOxGSu*DuFX+|&0K_4v!(OWR~Q4T6pIAh6ltww?TOxzj_a|pvGkG4CS67c z;Z3#EX}N6RZ%8j@1gDpz(>935<&DMV?w6&DQf^C}&O3dFVrgv$RI@pich2eKY8?Se zVH9oj%TSa%Qjp?`I|^Q7i+(X{@Q6Zyr0#V!q5f4|_8%{)$%O zN`*>!;EQ-2ulSq*5X*J>jSt$$%zI09hesyB32}u}rE1`h5HCOXL5Ajof`8(@5j?ie zIR3MDC-7nqAV2cLeVS>vY*FMNyp{uO6(`;EiUq$(@aL$3&FcmKrDqQK62YJHYyf_> z;J>JN9sCNxe^!wO*8;&Gt{4P9hj$;#JZLx;8&0{v0~KsO&G%NR(lfdwlTS-=pAV44 zAjuulW}eSLt#A`y76VHKy6*&$&CCjZ;HPuM+zO2VCNt>Zwhv$;gE+Ur0Am<5cB_CQ zM4xriK-R35Q2A3-YO~jl7%k?uRem@FfDNFcnO#GDVp)A$`T9}QKbaNx+y977|gyPFeXU(C=4UlLjuDWDFf>RV9%&%5_TzVF#$BJ zfhWP~(pB z%%$j?q7aeo<)Ke8p!A}chJJF#)fg$T5mq#zXXHpscGdO~sC!ASG8+B&YXY^_%Wmjf zHK&OJ6>pHP_eFu~J5pTyYxyD?D)fPf%iAg~`2*clY*fcesXg0#gHC>(PBi9AU!B8k z1h)`pt~E-JAhh+LQe6Az^8LH$jn@cx-j+zQBv90?>krcWC+L?CNhVxEhEGCce@ncB zz8%Iz^DW|)fdNmfGg4>BduQNDr#!b@HwT@SS^QDbXZ-7Rd-m8hgEunevf2fWgk}jVI?3|2x8?RRup2T zAyyz_Wuhi2DC>Vk(Jew<=s#BW|7g*DPk_0?E+jrG`Ar;YX7Sl5m9-dG2Y_2F1Ij`ZY^KP%0#0v#*Uv0@!7 z*|EYME8npq-lWACI&2zxtgXkId#uIB8hxzY$C`eu^~V~3tPKdwKszKcQ2vr$$xGiB zh0M^`DN1}>{v-nyK|K@=&n^HDZ4H7E=$_8sy67}RakXN<@AnA+P4}++P~Tb^uX#s z&`(xlgq04irh6h#7K%K~ScAyK&wdtXOTx2k9 z8C7zdlnPlo|RF?&)3tK^=0rcI5F`(}^y5nx7jdXwV zMrsaaV>3|AMyet0MvNG;-A1G-+i#o>>adCK-ft(DaKWs^^6!?pH!Lm-Td6XLs|(@rY$D z-qS*_xA!F~O3zE^_R#kYM3=KE0<%&uD+uqt6piDoIm}wby)MnhkD2lfQgcdjr zz)-xV(b%S&eL^km0(4R>rEvf%>4a>$)ud_-bU-bkda>zW+qAK5y4f~OZJXXUl)0%> z+myLKXASRe7FPJSseeNmyfMxh#ya6&u%ft4Z5+DfG&IFnDy_@UiTqa4)w{SkboZiv93{+H^%%@Dw57GN_Hu$c_l z3Sl@6hRNZpOK5m_^lwG>%nk+m0DlaaL=S;JANJK9tq zSq+j^B3V6>RV7(%l2s^Kosv~6Sp2oUGHy`kkcg z`Iq9SPy%IzP=%F4Sy7agMp=QBl}TB#l$A_b;Z!J}T5OuAtd+_ds;sTbnyakE${MY# z-3nD#s6VpWE33e=IxMTkvYITb%(D6{tJ1PsEvwkFx-F~TT38R4b#hrhmvwbnZxYEK2?&EL@aDiw1z^E$RxIy9m>kvTzalxw2qUZO|N=Rg~;SSY0St^d>>c z%$o-7ycla0WpzH* zKZ<2l{xk$D^D)pXEAp}0QkLgaLd)_oJS$7{8-Om!H-Q%AV?Cu5=2QC=ETZgL$C~x6 zS@)XtuvsUY^|M)5oAtJv2pw)#=w{{aXp7MFX03150B3D*)(mGYan=}T?Qzy5XRUJ9 zFlTLZ);wn|bk;~`?R3^uXRUSCVDBmn8MpqeRD9= zDE)HizWe7;H4ezZ1g;FsX$G2*(*|^qFrFBkGZ6fc9BRa&Ihdi9VGGdyO5y^lt>Fs- zK}RgWlTl^lf=JL&3tE7VUeE`0%!1(fx1fR%|dej#N)VIf^SaUoqiX(3(w z(LxMF%H)Mu)+kdJ_5z){kXn5j*@Gz4bE!pVvJkOAv2d}VvCy#qvM{n>vXHXC zvhcDXvrw~uv#>8-G?n&h4p6L4bDZc>?l@8RCOS>q#-Uu>#u0B~9+d0ioc^!z4avs; zDdU?mXKdq@^_5WsVHYASMA%q_4M*6BgbhmAxP%Q&*yw}}P}mrS4O7@ig$-8Nc%?jP z5K?EO7B+BUV;8o3ffWqhKbyp`c?_G%u-Oco(6BiTo7S+I4V&Ds`3;-muvre9=&-pC zo9?g~51aIy73Mu`>Lcuagbfh-1{(yiaS$5{vC$A45V0{48y2yV5gQz_@evy$u~8Bm zD6z2;8!oXC6B{(KaT6Ol!Rm>oj~2=RdeWzsG}$k?Qe&C7(T8JnHiOwiaIjZM?oOpQ&} z*nEvm+1RX&P28L3H|Rw9u`3{0!xxTa6d@muttGaQaA-cJD+}k(1oCe926cQPEDQrs-6W;2oHD zbG8XTGkyW(H)jVXS!I3Npm%}zRl%<+enI7f-c>8fDA#6JM@s3{wW+z)Q*&#i=GIKj z4NlFim6}_7Z*CoJ)pG5=Iyhl>9c}8tI;jWimL05Dqp*KfZMpVgy*HY@_v&f2Y1+s2 z%C(O}rg_K%i(-=kJ&O+aui}$d@-_s&`uK(7*Py)QZP<`&@{J;gufs!R|Dr};hgX#c z6b<+~yi^`vq`4YiTMjM{xf*^^_80HBw9TcYOOoW_La&mvu~O0xlH^`mZnz#kRF>U~ z)NjJq%ZWubZy=QkMICR1C(DbAE)n`8?bVHN{37sch+iZ8BJqpDuQ7g2@N0@+GyI~< zU)_j#q_?|SUo%uw!1mOD?a_ekwPEGj4FpR7x3t&B6lq^&9V`FhN&6^G+2i)w)EDjdzUZ(k z*4d+uW`NRq!j!?|ixN)yS4sOWyd!>{@atUuU3izOO1ZX47o^jui#9c;OKMEl)R=Cm zG2K&RdZfnmOpWQ48q<4kOrLf=?Is#E(Mr+aWhUS9QH6b$?j_)>cMc5zHNox*D9iWJv0gp{YxT?Ol?nHJ57-CL(HIqIU0*#Pu(i%I_)G~1-d2_3Z!S+0@(<7OGL_<(E9-Tj?qbX1?wmO z?-@PRDJsx?Mo-<9(?EY|-MbvnR0G8S-?Fj%H|6tp)&GCk1$YpH>BbH#zvb zIl|v<75;9>@DC%0zZ*cnJOVFl|79KVm+^$fWIR9z;|0A zu%4V#5j#YEl7JOu-F(jyzzipt?8u5|xx3e6z#}GmtQdpJ0OJ^b z=3otikQunU*1hdrUJY-B3~f2~H_xPMc>ZqaU=bPBtLj<}qHbWrq4tI^8?lfrWHHY!M*$-uhjX-qn8uEg7Bf)a64Pm=A4Z{@3YSgbmDpDuWM=a(<*6(0=2Pj~n>VTIfStqYo^K z^mQ$Cfz3K8oOqkn(ud|uADTIRXczUNDb$CCR392xeQ0>qgVNWrLDpZET9)@1O8QZR zE1PhAXxb$-@LbvC>qAqp4^6~AG#&fUr0hexGcri~%O34RLp4>{Y1u5`KD40w(6a4A zle-U1@jf)s`_OdnLzBM0O#N=+CWTU?zO-^Gco=UfieXaadw2q+7aEa9gKrih&QOG@ zpWhPj7l}8I@(%2MGJY~>iLbJaRKW}m#8(Y6?Fa87_~qtk@NUEh zRWiQ@?qn6gdo-Zt=dfR?I?kI(bcf^F?1IE{^}v%yAdA}RXr1YPvYzM zsZQfaAHm7jhJgrSb zvz3lF)Yqo@P6%_usM<7DWQgO#>fp(%AzSc^>d>^jQ1D0Uyac~U^n^zB>vjPU(%+~@ z-Ob>a(n*bG)+1pPTCIW$ z8HDm>zEv0*dtj|8995M9>rv*33(dp(FJ`dhY@C}*-gRhkKYVx%5SCgk#Y_AxDi*!e( zCs~ZGk#m8VHXF4JteXO~gwar4-E%<8#U$EjuJpE^FW@SUT;^VH2+$fv2Xyte0Ig+o z&As05KxAzJN$_#hhENS`!0-Q=k407*{a0rsN)Z|78d88PH*+ZlDSw9C2HeVUj-$4I z4Zt0|9*bPd~rIY*|uH=sUof+M$hO=xfe&zKFTIrgGU7@E<*3;w8IBEx8S;st+W zB{FO~&?RwZV?WohH$Y!AYV0503W&G!jR!is4yWFKgCmpO!>jpL;w(P?i+vM z5`iJwVz?{H(`15A;?K2<{*tVa@iq?2j4?$a{`vM-ro`1G$LE=&@cON8v_{yyu8M z9NieBpwXQp?|NA#G|mS06gbAg;dSH7fZigTm^$uFj6jt+GSTHglM#S^9Jx@}1dU?k zElF&Ic3YFTfM#)Si_1zz)4_mM#kp}^6PscvHCAUd+P&!uT8{RXj^O>z@#*D`$7d+L z6+@G9oqi_l{rs=@z~b#26??--#=waT(qxA+>|BN&&9KuM zc0j|jrV!yaqcdkuTCVXrpq;fB54u;&~0g2Nth*gFn;%3-fLtH=(?=1zxQ z>#&<0cDcjuci0sVx#huvkbU&9zaIA8!+w0&rw{x0VP8M&_lJD|u|FX84a9ze*k{mP z;XjC-39(}#b~3~chuHZLJ0cQJi7f1)h`klrJQuMSBlc*--i_GP5qmvi4@m3{i9I8+ zmn8O>#NLzGl~QftR*79KvAZR9y#zN*SYJqEWON#hWW=))^UN0(<6U)aZ43$HFGqn+ zzTyQs{R++Lv%YQvI`=9>#W4TsEYPHD-9Xc>)A^a-IDqDSGYB;IMkMIsn-xKeZjJ_B zc8f-`mEWRkBV=b#>==rjM6tstIFF*Si9JcNS1I-|#onga^AvlbVvkhpor*nGjk!cw z|7VMh_0YdjZbR|6nCA^8w>yEZ|Bhy|jdx_wEqD5YZu`Cg=+3)3(DJ*(Ko8ubh03A( zl|YZ)p9FgRK|9csKLmlE`C%^TxrgYZhKoOjfL?jzLUY6QM>GrF{3#mr_T$Q+-#?~# z^8U|lL4W*(2KvXpQ1$=%D^>rqCsh5vJ+%jY{gmq9%`>Y0_s>!NM&&uC0;By48i1XC z^9R-ahSZD|en)D?iZ82x`n<$)%;@_HMKN0RuhDOera!cxfq(P{4f>Og)O>^XHP(59 z42&Uf!$2Fna{`TcHxM-PJ?`GvM4j)9AX=T@A3?0D(uFNmE85)HMzwZD&`wRFEIX-5 zQxJ4jlUpL_p{AgcjJ?&Ak!XKoKQ$FMU>vBD9lvpqnue{9ahRHpdEGcd&7jmqs~H$w zjN{bIKm-%jOjNINvYLfTGfq>pG141nsM&PgY?T}i80V=uGZCmN`42Xxs0)1%q^k>u zBgj&7>3(w5JbUU0xoK~!%q)LfWtOYEVtiTTesc2MqKvRvz80g{X2p+)*^INrHaxx{ z7G22Liq?DaM`IJf#|!?~*!|$!6W`XqxhHtBlWNlV38*gC{KqtlZXj+blYJ#8E%maKa!T;Wj+Pg0CEu)%!4!%C|t+dg> z;KPV-vpqTwyx2>|Yonin7kkP0s2GU$zkQQp*YjU|mtw!z&Z7mbGsJU;c8%l0!Hee( z?YhQcU1b!{9oo%}vtV0g97A`|zEew*=Lv$p-*P2*v4!g}tQCo**ur&mZZ#CV*ur&e z*Xjs(@ercprq+Jo=hL;F8nq@VOd`H>#nxxRrwYDa8+31Drr;;Hf&3a5h^<=Z)opG9 zR*elhqZf?pc{3;5-N|FJE`OyerzyWVTN7W`V`yP3(N)mSF@k?ned z7Y{kQ9cxGH8?kNcUa38GrX6%n_lE6%X~k8F;a68{p7|`s8Fd7=~6F=~5m)+nW5})v( zD+%di;s;IaN<#XC_@URkegXbP`KyMrD(V&Er$xH%vs8-_Zq%^MZ0W2mXqJL_vIPc= zQ~EbM0~pHiozKo_17Ns-tunRSqcQLr8w%Lwp(SAW<94u$Fh6J0gdQE8E3+jgRXC$PcgE^#oi4IEv4m8x{8mXe^_3 z?k#Cf;P+1L2g{i)X#nQ;P91diU0PB@P7&#Lxa8Za70K~*0XsTcv<0mO0ncLGNa@~c zBj6l{3EDBONGdH1kJwFaO=E^tz)pRo9X52Sakj{~NO*v@|1HEpO{Wr#v{ z?&01BvTMv{G}pDwa-fC$3aj%i_cl~(i^P>({FUzU^#BVP#w)$!rvmZ|t}fHH1@RVY9ZeR#`TORYO1!Q znT2=N-Sz^O0`ji9+aJ!|+x-lc_YSv#J`-2>+T!(t7?8eT_=We(j*}64Qo!CO zhejP~?mWZr6Q_Nh6u`59@b1-Tgl>K(8iaV4-Y3tsQ$EmTadMw4O7~9R0DdiCUxSp~ z*$l}0^u8UG?wx76zae7#e^u#PXBupIqdq|6-uV^K9T7Po*kym0=79GYj;VC5OBVHt z9|TTl=exYi1w{YE@VV2Fu13IL1RT^|^S&#MLr(=9dd&Gu*Uf;uJ0CVCvFkIS-$Z0$ z4d)TvA_4jRdE!Lp`Q50&{^Zz0&Odki9Ppihmc%!98@i*O-csTW(kF}}ES6n-0k>-8RlZf?axJilZ!`%Cv2Z|Jt6OtTW_eG~Nu^zyLqf&C;A%M~1+=-E{eK!Eb zG8);aFPg{{C$5}0ORn1wO=N1t_I;hk|+A3*& z*QB|um{abuV($AfivOD;H{JfhMdKXjg;`8uYM<*n66*+)nDXb`9ziWHmI8Cmj#vj? zJlUPIU<5jkNj&kLqaThz)FhS^v%epXDc>X>5zj6g-WWWl{J$J2nl8~yJ#XJAx=rz9 zckY}~Bf*QM!(7XRQD}Xmc&eIsw%cX!Z|Q>J`Q7W{B=Jyn_=E06;O*%6h`=7`(IzM2 zM+Nqn0^XJQ(XKttg0Cp}X+1GRnY@V~Q?q9__{xG`-tz%?z2Fb^qQ*Cgb?KO^LddF! zh!?$nLxgyVGInO~PT+&-_}KHkse9BReq4_}8t@^+j}PxN0K8ZnkH6KY9DGAMK0)p4 z0bVSQCp_po2E5n-OpNPGHPM{TnK-?l#RSk&0BgUQ;Kd7$i8=i)ffp}RCZ6kG2Ye?w zXR5CMLh#~c%2dz(KY|x8Q>OY4z?#-1wg6N64#4bf5{u)hQwF>QFJ7ihT|5x8m1(F* zXVbvV;ITNyusC%`f*pWZ98W!*kN|$XIO%%Ae(+*(JiYxO>S)vG_>6gjs7hxEK6MaP zsaPD(SUi}jR29dU45n_IEcg|JUw}^|ex}C|Dnz#6+YQ-_VbPQ;K+aGqM84op4W-^C z-f7Ho9(oY`GCDr1`7ms}Oe+OHe%NI2#e&~E>?`nMK|JeOBK9gKu^^sZH!%&oSP;)1 zok#=6he9fZH_;}&A4>Iy(*9vD_^@}Jq>Sfw8MzyY{?&tef&bC1m)QXs;aq0B#734wG*fRv2G0$`Gpgh3Y43jIY z9ds3tb$@2$R@gn*063px3o980lZ!4>662*6&J3ommCA61d-cIIwq!6|Q>pThaKJ19 zXV!NfF$4=v(*lP5WcMLhpqcU*<~ffTiUpc!F~h^oD~8he$;+&nKOuH0;8MQ-S!Oqf zp|^ooh_h$4vfDB&2$0uVv&K4)7&aboEyu3#{$Ut3Hoqj9b=mppFsgW7X3f?*k4U77 z-z-j^9qOE)NPT20!$Dqgi5CHPFwC`&unfnF)x-;}*@v7L4<87~3$59Yoqrl$0=QqC zJ;%@P=5VZxO$QmabzU(d1n>~Uoen>bpkd=E!z*k~=!GrS{*j&uOz)zX}wwjB<{d0x(FYnB4yvpQUJRgOR6LbPeeeb-KTq^19 z7+WWKk99{c*F&L)yjfc})X;7P{D*UB{nXC3^Z)W5{ZH@G|MWinmv`#nK9B9N-1?XI zYSk^yr6RCr`HP?C`REnXwW64NUK@&bJ6f7qObgY|tK!~r)Z;Y6Wva)W5TvM|(L7~U zkBviMQIF9ud_}!PW5-4HVt)kZ)QfHizEUrcpr2OHe}v$KdY%&dOg%@59Z}Cw?S87B zB?&#Cel-F?x%!naf<5Y&?GRXYs%PdR+@_wXhhU3(dN6_w>S<2|CF-e`2-c`4rz2RU zo~(&rxq54XQQHwBE8n;cv@Xq2f^SeG%|;VB-VT zvX(CVQC-&_!Ov=m5y7wGLqx`BYH1YE?<$#Y7+;I;CmG)eGg0Gvbv?>tQq&Ex2(&8s z4>UQd8>wboRI-paxv87z6c3e*;7wlY7F3C;lDY-OHuXw^sMkb#2vhx~_v-J_$i*bsrkd)K%S2&Rh4nDocNuamruL>9HuM-k`V@!4H<)qe*}<;3&!eh0cY1kSYKx_-qK zA&Vw>>Z743uc6<@9%wS7&2fBQqMv(3E8;8I;;qgjDsIE|IX1k^*RkRg;DvZBZw&m% zhTm9xd+AYA`k(pyHGxt36M(;R{F?FF0s{sM{WBZB;OAqQ4gAuEU%MPI90q>Pc&)SV z8zTf%|Av!W8{lf}1o+Mt9qBdRn2LL`Is7lV?l&~dJP`*x_=HkB?Qt{i&QOW*`mTOm z0&V~YFy6G^Z%Y+Op#dHyso^)B@!M7f-NO*VcykZG16B3{!v!S8ZyxD9qRQ>U_ET<_ z-)yATZLJu>i$abU#+Ub)ulB~L2E)?{@mt|{DuzWiOuPIZiolQ+rQkw*Xe#6Jzf^x=&2fi z5nxSKJ#~#244xU*0KmZ;%CqL60aJngl>loU3@-ux?8qpI5tRFo~E=`>WIdsLSAd6)eRJ_0wJ^srXs(ODcbvlG&M`*Fe?z)RWR$ zhgiU1hG%4NhYTe2Z{=e7FU`hWn+tf>$pN)(=Ev;thneB%2gr}vrHe|A;}F1bIu{Ln zWs>7&z(#!OH(JM6K&;IzeLK|&(q?9DZs~hzzEe724By)WxvA56Kz^_yJ<_;1W2`i{ z=7fH&<~#@}p3$>8&KrREsfzTQT;}`)uoFKTmtJWqx->Y@Vm;B4MYYg|qN z@>3OA(YWY50Q-t_WqT)GAD{t@oTuv60u2&Z%C54n?hznA?v@wIW$x&3=0uL&Ded)G z0tq$q-bCJ~1bci3xSXR8OXU?INoLm8l0R!uaU#$fj{IEqskjet9cO#e9$j2AmvQ82 zso1j(;6{dDY7cnk0&d}R&o%J82DFWl<*vP#8IU(3@_l8d*GRzK9Q)AdwH>IO6M7`A z@_Ge$fZ=0htal9Hr+n_OPTuK2hdJ`8G~fF?;4y~JwV(J@0%ScZ`As#SEFe6irERC; zB<-z)wW*nRw2DsqNu>dRtSqHejHrZh+sw*RN~pH-b)auVrb<|-FFL&W2SyPwzN3I1 zi9D4?(pujgfSA81lPIOXFZGir44Y^V`o$`eGu&%R*{HmLO+~sHDf2Akkk-t)PD-4U zABizPOjTM*zx(?FVgjS|T1%?`V8HjBcbgFZ^+-kG$Ts#5{eK2z^(G}=yH$@hS^z&v zRoW^w^ccYc_))6TPIlLS4(N(&sXp3Edkt9W2e4Iz(qWPT3(5db&ZncdVL6Zw=hIR9 z((pZ?FT+mSNk+_p0eXg=<=aN=Q36a1yU04@0l+|pU8QuB0?5M%UJ9Dx@lZB^HJp?l zZYBs#01qQd5A83etAKU+(w+LM70?SM866O^`r{Q$8Rq}V}n z#lUrd9T^UmE(QJs$gi-J5p$|E2jW*)%BWUV(6Rx&xE9B{Re^K{^yLf3YJaK<=?oZP zlgDvNVbuiS1dbjr`&Xs59l~&e>{RtvK<>uML}_hML%@-o@}$Hd>~8}2t(EeTQxG0T z1dQh^KhmD8<^nj0;bf(-S~oy`f}%{3zpu6&<3Yf5j-D#JRl5twZ>^MR(uL}^0OxQ% z(-W&x)6Qo!!>Kxkg8+VhqRh~qto|O5pPwi*mBJdW0r~lfGE2T)VoHIY#CoO38;|KeD9j4fbhJ8>VLkyXRtdUk64PT-5T5*a23Z|mEOU{ zfNL2h$z{P00ZSPs%PzG-05>p9k@nV_0ijRR%t|Vtpvmt=`N=hT1>a;$(pp= z?lI;u@m5&cZ;#c0>8?0_*dCdf0|kHF9?M~Kkl;@_bO&Ek@LxJ?1uve|O6MFM!3#A8 z=^{-;{~zgF{?i$l#iL;9isM0KEFJ|*H=X>!HxY%q<1`g~wBR2&odYi(1xr6U*96~6 zJdc*1IHMKJZN*71oNs~eAoy1K}yXbA!kTH`YNT3#(8*avfo;Mq6xdcm{L=1qcUU(QD582D#`kM_f2&itF;Tlzi3^Jw#H0b2QE zI5CTN5lS2X6_8y?9FO<^9lTcXZS|eOI|{y?eiL{X!M7*-zJLmX?_d}RUc8r3IvS|k zRuacM87qSK6MSdmSnxt=$D(vGegP05BD$K);Dv-M-A%K=3kg?xm@b2_Ezap_t_xng zi%|NQF)0TK0aW^$zXu;F&gmD>1iX0vpbQ8oR3u4?!$J+7juiwqrH9fB8Eyt)=27H- zEj7s!h^T;8;?jh`w&3FhKPa#SybwiYaNs-eoyGAHwDtPmK3|BgGOEf^Tq{IZ8Cw=5a;C7p_%ei z!7r$@2mDdNFRWVuyy#0xZrxGfPl@AsPLN6|Fisp#Y=Vh2P&BvJGNTFFGH|$v$ZU$X3>+i)RZX{n7jLJu8=6%IKUo~# z(G2YuI9>32qA}tJ&Jp~XX!M6b(XQH?(dZ9>sp9xAF~h)T3I5NRo8a?^x6{Rv&AIvqRs49#zA5Ochkl8cCFD&f#T(ny?bjkQ{YW; z+`o+;{2k&QbZr-czn}K%Mk-u4q!i` zw5G>N?LmJ@CjMzM@h?))DyEkyv7oO~P=lsFQ&C-}cd3&xddc)TOX=U0vgJRLv{{n| zi89-#*8+8-Z*G}&`fL-ZIh)qgRdT9=R?D%V ze$6%L+u!C|3&w%gU5MUe4k2$*<_6>s${dkv0gcRS0V+PAV*8j1e^`Y-vcex+;g7H2 zODs5(zska2XyLE5@RwWoD=z#+7ku4?lIBmn3=^My;ZMNu=V18LF#MSq{$vb)K88Of z!=IJmPt5S=X86-HX{(1KP5vegf1ifGRm0z{;cwXR_iX6fHkfz#LpS`<8~y+ee+-8| zjKd$v;Sc8U$8-2YI{Z-`{=g1@Y==L*b4YxIhrh_fU+3X3_3&4F_zOP#H6QV1AB*iX zKm5rb{`?Pr3Wz@o#GeS_&jsq$qe>RCfp~Rn4;!i8_XO{Sr zOZ@pI{uEQ_k&YO>{#_|s#21WU1aj?!e=^q4bf%rOjf0WCgr0gd~tHE8SO z7?}dv9&ZEM{&V6xeclqZ>lZY%_V|MMo+m7{cIa~gJvyM@Nt%ZTo{R<^d=kAoVAv@P znE}I3%?2HHx-saO)3huYcLu#aVB#6{mVn7$Mu1NJati2-uV~dh`>P3{^Ue})IXfQo zf3Wrz&`~Vi-uJG_5FrT`Tml)NNwDA!2X{TVI~+K;%h26GumA}Xf`m*60TNsi+}+(> zgS*@Juc|)xd)K=6KKEVkeb$2P`t8y#-Bm54p23g}nRKQv{zja|s0*2Pwm1Gpp0i>k zgv>gJ`7tE=ycK`vo<~0piMarqAY|c%uK2s;VhH{&y@;_K;< z>T)amU411N{;s{!5`Q;b#Xt<%%%ArUNxjC+o~_rK;qQ*?+`aocCcBWmH!|bzfg6o6 z5<(8&=-sctm{DBw#zIsp*~tjYHYBA7C!qry#r>ErM>^HU^b%JvM2nsPj=j=y=Hd*g4u=g7=d-~}4dROm%z z{58Em!4zFL^qySIEp1_6iMTD*Bqc!(R_Vm!aA$fLNMe)hUK7EKf{8lPMAI z5cwqw!_-V+NIpb<$-*$zJ4o0B|I$<5T{cWJE()86sPb6HFrB>&y9JbYmkl#mKw;j> z^6s)Bl2Qxz2kwy@z?tsGg*OBDOPu9b_#5bu+{Dc?zAVCf=@E&u4Wo)o1s<0e#j+K_ z`WbRs;!?W6-$o2Mr=!nL{!VJh1&Q(OPEjnFA@bQf!xl;?#yie6d0^)i>a(n5R#cNw z6vL~stzd2^FsZdx><7 zlZ}-Mg8ImqbIDT)tDC96#HGfal}-T%N?b*$m3eoQ-(@gv7+e`vplOu!+~}sWmG6S( zw-}6vY*lK3#!KHblu#uRI8o+$%U0z%C_;Ls8%9;-VP{DELY}HSfU`x|494JUg+TIg z)C`%tu2&laTp+?`$V;AT{Aw1<1Ck7-X#O8}}o|D7tnNpgcA7&Rn-Pw;bj1w0ADQ+D}1uMNIEtN~puf(F2jJPnJ6MaBXfg z6TUt<4T1IEV83FNnAb3|J_e>)9tbJZJ!Bk=v8$O?pO=iMf zNrd^`BpvQ*-0kbbA51mZ6z<@rNpRN@?lR5(gu8)oS8BEn?mvZlP;+z>v)G;aj^jV- zYL>e*UzIU2Id)v7m+>eF+YiB+to^y9buP(e@UkP{SE|GAH%a>5gD&{`wq9i(7))84rLJ| zBIB3t=uj4M1me$n_`xkkgnzak-QX4@!asWtOud!@XW8~=ne>vB;cIE z=dvNNu^lMyU->*VjA}dp7%mqdpXc618zWb9Ng4Vjc^dPXu1JdPomk|x0%};+)#2w zfh|PJzJrQ2<1>pq-}W7BYhE5C&$oTY`z&j&06WT<6M#p7@|_Id$-`P?0m++G zz7gyPe+b;%Q-+;t2xt)plq;9pH;T5lcnt0*z30%rKMMop6N0{r=q!H#*F0GIE}`>( z?f?#xMLbu$WigQarjYMzqZr(B%!P6vKSyC%LQX(amFfMM*rw6&P`BgKvo(_r@3H!A-w!Q$8%bZ_#@ANi#fpVGi z>*w9MO&1`RIexe}7`O_!RN@Hl{_Zw!!16%bZ=83(wtU5r2ikrbkWWPNK-+H;OKQtU zms|z?rhE5mmkB8CGryUB?fCS%UKVP0=5~D4%bRX~^NsBufYPMj0$Y0*Xon!T-@@YU z2La{k=@;L)J=R^bd@sT;VL%5y77obbV%_b4QE5IbaWxC*FdKMG;#!ue1F!EB5@BC; zEC@U!Gu>+K*b8)C()P?9*YG8<7}Xh; zfccp$-Y>VU^9$fh;hWLRxU&nNbLD-$jF}Ciy6|aNzAllm)UYm>LGlJ)#+n1V@^1E3 zB$TmLt*#S5KO~JXc6|=|EmKYPS=Ox%<|AovW;|r;hRU=^W5b>C0wr|23YH&9%Xr_| zy%0!#uOQ8`nDlYFFx587gz4zC%kuYketu_M+NsD392alRAcWwAbIo7f2y%h1}r}odH2pg z$~(OeOd3l~nXJdqzRx_MoX`9>P+Ff`KslfJr?Q-VvCpu`n|J=($wC{(t&C*5UQ;O?%5ZG6s+y4V4^v?;F(~$pHitEoq4-wu0lwo`S<-p+* zy=h&4UKb-JW;BfY8|JoUthm5{Y_`8))L7(96OfDL_JPL7XUn+dS<*nPDV8}h>L12|SWzr;S_-I5 z2?Kut<+K#gfR!Fp3n-_hfMzV`ph!L*tJ7k_FMK{t3z!)B%gbP{6_e?vnkS}U{!zA= z`cooTx=o$F-LysmcU&TvMbnp(?~oWx_a!XhkUxF;`JWLBuF(S%_Fg9S*Tb(RkuZ6^++k zf-bcEegStyd!QLTphYT0o>KQ-UD2DpMLm z>{Ory z5SNt5t`Jw1nZXd(m6<%-+sdqb{L=3zv)V%3S7xIpm>w##TR}WlqB27~Q=)1^yi}sS zAYLocRUqCe(PJP!Ds!;5nZ77<&`V9Jb*`Zs4$7H|ww7IcTmuEe5h%(;|Uo=k3KAR+zs3Ai1?UAGtd!GG^#2@|2(^xBIs;uf5LpWditOIe{;dVAm& zyDJ$qlXuePvL&s?+4udh5Kv&mT-%EUIB*w>){^8^#Q(rXM}sSaA)}&4Py~Itbkm9@8*-a z*kT0)55VBJOca4^1JLO$Vuuw_c)&@x#f_4Hk^|BIEMkWhP;uY{xT8h*>I1LCEp}J| z^#-B7EMkWh&}CknT_@8HFNRRRVL{si}G;T|=l3Eb<2 zTN{!Lx46j?5INKccbd@89g6N^5jR`{;)ZU8d#}*19F`641H!$27++=%3-|V6hv7ah z+((8Ng8Q_%-5zjm_-Md6;c;#FMYt~s_mdGAf0nDl{d&X{xNiyfw-NW@zQ^5xY-B~a z9}2hMNZw3Og*)rWH*miY?%bp5!Tnme&7%_GelOgWM!A0hJ`1Qbx;5P2g}d44jc|+Y zR$#j^0r>q}vD*slI;JPwVz(98cMLk6)lY;UJQf|_8YtXj#}0)%i*QGdJq@?mZ3QkI zhe2)4BlH`_sc_@wTqj(?_Hj1>A;M$l_|kA&gnRe+*>Ky1d++#XaEA)_Np}snMQ;y0 z?T&-HgwS7fe}%iW7*E$}LIXhgh|gZL(ms354yt3YzK98EFngFg6aS@a_%)J0m>uBr zgVb1X3oMn)D^uhAx2@;{M&4_VqiM&chKvG$g@IulLAXtw?(3$xmIlqS9f|1Ej5A&}gYFhZ<@ zbcOE^nlubNRB|f&O_R`(t^A=NRK^DzP5KQSExa=`N}$OYJXZYNIVvM_8A_$e6TuTC zS7J#t`3RWrq?lLcx<-n?;J4!Gc^7Dn+38TlvF5 zh(6p#Q*r^Lr1w-*?Uc4){sbeuXL-|=vgc_n%)AqS$Y@qNu=qRHmzxR&j6*%Qrag2O}_@k4jKLr8_mcE z+%3a5Mt1xIFxGvN+atRfOTmXE_d#|u_{AQT(MK35(hTJLE9RADqKzW4JX%l5&{2pT zxf09|zuoX(SehdFwO*9L<88!$Z^wF722VuI%p3&1A$fC#Y=mFYChIN92Mxz)=0`9; zI7MnljKqIy$NE6x=>{|l)0y?L41F38NzK{;ekwWQTjSYj-x^0{nQgE(hzYnGHQ8-h zD$UYP#=)oqi`?)Abf<*Dd<9y8WU-9&rLBWgfXNaE!S@|-jl|)s{*dy(brQ$3977b~ zMv0Rtb;wcR7Kt+{VQ6OHR*CLt!>FMx!Mh|cqST@2SC%~zmm%~+;C_k8lrSt5cu3*~ zmSY%fB@1r|lyV!T4%-ft8{L3|lrWqDPf6dCt(vg zH!zcJBnGKPewr^Z$ZZ@2yU_AVhRw}#jzZ71ypYa5N##wx$a7ueZ2hEH4>X zE&%-{4yG+)g~=pw3?+>10^h6>Cm3^$<=tD}a|xV7sbjJBS#wF>c;6f2$^i4qQf#n| z8xJZVJ+TiQ=jJn3A)UP!k4I%#%@TLgwec{Otn%D0aCfcoi$PB5x!X7Ac+`beK7}55 z+}rH}6_K7NSvL1zV7ScnYzy}`P$@A60xw~noPd5|EsMa+?G<>v3Qed1<`1nhufRJt znlKqyN#bK5y05jWjQy12N%03)m)mbx*8ayK3nGwWu97BQzL1m%la|DlJs+gw*!hKbEwUs;As z%#FmaN1B@|iM2q@mE}GVEfxNAx8^oV5-!TzPD!c&(LqTX3DHSeQ3|4~vI6Ujxd(nY z5u&%U5|v}_tE?&t@vm}l|9i6NudEt?oCYe%VGx6q!Ls;uKQU0KKdW-9A>#3*Gw-!IHnHiSUTS2m!B znq!rXu=C7|l#QJsmMEJrOPf8)CX8frqO#czu|nD07h;vN1tZD4TG_&jwoXaKBxByF zxKn#WZc);BNw+F#oVF`lIqg)o@^bG{wpk$dE8BR62bJx-Fh`W_Jj3J44o)YP9qk}a zD?53y&nY{xikdGfyRf*LFDtv+KwMXL=YhDX?B;Ywyz*zhuk7KwO7kOSFOQh6?B!Sg zRM`iE$oxXt$Fq2??B`ebR@u+%;)8O4SKDXh08i$Naxgc^(SfrKwl6W626`sO z%-$RA|9|Y_{=mTdk1ZXCYQi3UN6K74Z2_N1c?_s8;3x5ozqJX6OaY`$12h+qTUv@O z1%#8vm*_SEN|QDL&{055(%g?gT?93wiF~r_C15p8Oa=56u$dBTaK$BMh6ah79@>jq_0q(98fu^xW2g3n%>dIOLs;D)r& zR`}xmEw9YaX&U;96|a}Uyvjn;c&_UN6r$-o*G&S7(R7|`s(`XIo#(n;Ky8}AbKN7L zHO=6;;srOP)|+PVT#pDCMUjO8#|2EKNS>+tjGzS+iFwv~UO*hp#8|gp5|BtUG5cAs z3rMAzM*z149Hv`zYFlAs7-)h0{kgD1Cfmh$U)Jq02u`2q3BfrUjY`H!+r2_AyTu` zoQ(jyga#-^Ft^E;M?kr*G`A%vzo2n6-vKBjAdO<507?nCLkkZ8L`Th*ffk`d+eF{Y zc8ff?R-5RXLG6gYkZs$`;{?s3Bo**3;`4!M{GXHd`M}&Di@`n(pRt%b%5CLV3vAHK zR+=4w%X=%S8Tg}!p4G1bMe*_ZLwINP zr&x-*0{)*;cmJ=Fw~@>)5G!&tCQ6%^P z+)QM(;Ogd#hayM@aN>o&Ik*ZYv;p1oE4o1&mj$Q9D^T zVZRWIwcA!w@-=J-7NT}-?IqtqehW{7d&}$3=7ml2A}m5SexHtcW!q$gpJm7U+rWnS5Dm!_0` zKz=h9_|ZtK{eUF|=D;h=+`6!rmRK{GRwn_=iNtdJHkPEdHNX`G=d6VN@Vac^W+Hgb zcEPl+8&Doa3FehMvo~$Tx@DgvymJ>Y{HBOs&*!H7 zdOk0py}{b_zce2$^%efio~5C3u^>bfHh3KYS83fTwxr*7s(p$O8*Xh5MYnX#Ae=FB8UH;1)-2a{|1}WD=k<(D+ zdOwH}%8f9HQOXTom@&%Dq7dVio4lZkaw{A{Q*I4`nCw<=qqkV5D*W@imKn;OG7vMB zJE&GmlybK`#9Za>7>N1Gy-Exz=3?g26Pz_?4@{m)K@{rflO63ve za7&8vs3yc(<*^UM2IX-bh)v3q0Ekq@{e)Xr+m!U|5IdE0Ow*R#%G10M`;@1!<}3%5 zXC{av%Cqhe$CT$7n3j{ubAF*`louFRmh;Msp%539m;73;C@)7q+)!Te_PMRRLhrZS zRbF#?puFbvSb4+giSowH@tN|L<4ff&r#H$wPVba=oIWb=d6j%o-t$DiDIYlfR6fAZ zO3Fusv>KF;s4lBf`GjF=^-(@?%Bb+4L$C%YpE+e#zHrK>eBqQ+`HG6Q=2pIr<11r6 zQH_RgK#N7(RkLv%1>OqwW#t7jczTj{OSu) zQu%F#D5L!D0Z~q!z^S4-0kvkWqAC`MKU9TNO;tsWTI;B4SD261daA~;p{jL(XpCRH zoF6#S;FuPZWpA_+6M1lcX8!s==B*sG@-v24W&sswRVRR0ZL@cxRT}{Rm%(z21bR?1 z>^M7K@<-B3DH#=N7i)6%O_Y2VU=cn?D5VH0$?gzzoKj#|+VRFeBE6-Q+W@@rZ}7^I zi&mrB>?H+Mr`555vK(?|rnOib>=gu5qjj*L?ZSY~Ie^y91GE!9n5y_R+DX83IWKkP zkgK|zHrU|RU(g`hkPZ-*X|6@I5w&d>_Ghlov~eFmWH$$N^nVimR@zU+j&0s-GU-$M?C0}fE3z^9gH1c6F}M;m~B07jB za?}*Cl)4=J1^QFaAv*j5&{n`PI#L-hP{4aSk_Z^WAvhNuC4kta1Q(#A%>ZMCPc=FU zJJ&HmKutR455QLtj?52AH#n4-fb}th-HtC}2`jjYq4MP6bDmI~7jG^No-6$FJi+3* z#R!g-{2W%qmRewbJju|R{AkNUpgb?mo1+qKxdluXq4QRPNtKGSL-U{07e~PVrVQ+B zWQNrY{U~(;vfCiJ7w$`>egSWm#hquPG)!vtZScl+DQ_%IptN;R?2+M5z-HW<2fSbM z85jdwu~gd+3(n``<3-!}0=DUE# z*!dg$S(fI8AML6J{3S3yt3enm%W zfTgWifM387bbrTC>CZ3V$W8D_!NL3jj>3{~jFHSQ;3$?+$9QnW=S?yAzaZ`Nrr4mx z2G=#bYOrva+c8mw?Srt#HY4m5$^F5{2ZLuxo(DdKDb+DwaxA+3=`gVD$^~nqbH!Dk-Q4M=K`;*-;%eWJ6?D|L}W`{~B7bsqfH^zAJ8gp7aKYc72c0-u)r7~Sx@29{H{%^ThDJNk+1lJK{seQUljOMZi` ztxoC&(N5)$9a-C}lR0%#Bg_z8)d)^K)hU=stbNoeT_O6ZQ}aRe zSEsgu7^qIm1~EjPhD)~&Q>SBjwvJS%*M#_2Ik^8lS&UII|&pR<|1I4WX)$ zwIL>{GtrvXDJuVw4eK;@mM=u4Ity)To$Xd<^ODX{XV-z4uSQ`uw=PhlxaT4@IwM4! z8qLY0&M`tPQ|DBNNK*O1jCGYdw=BeJbuM~@b&Wc&FvNOwUN4A^>iiIhE$VzMx7Mv{ zOb&=`YD_bTUFw2Bh`p+N0WZ`6H5L|&^{^U?ZfrfK#twrxsV*!Eaavv22jZ-{h&ROr zbrDuq>lJk|Z>DSNV%{(})g`=P?x;(8K-^d3utHcLsd2p_o~TQCyl3jtju0=^_?!^0 z)%d0m?^KUF0P=(C;qCNAP2i>YrY2N@_^Bpfz*>K+%W#D@gSu=egi%dInQT64;vk5O z>hcm0f$H*s5Si5^Y~^j))Fjv=wjAmTJ49}E1?s|>yY+y$il!HhR~${*vDmZJLU+hGnP)HMIB<7fYs>y0>CcrQ!th;I065c z!QP8FE9k-)#1Tij1vk-!3z*Br!7UaJEF0t$2e*Zy=&HLeC?k&)(vmN`t&zozJS;CISCNU!5Tzg8v~LADwuQTkQU%;ZS(TWcqVSaJKAvz{2byEnkTD7&69GhTfe^ z*VmzQv;yn0ZI7-P2}>aboz|v*>r#uLt)&iO$m5_@xLQw=5gPmP*F z^aQ3;Dr4u70G`U=pWf7F8cTOaZDF?}!bh{XHz83`VgD zyAu(984D_dVQ9i;L{vd?UBl|3U~E>yY@63%5i?=) zBEqj9L~FOiv~9U0YYBT}z0CV?(d&_^y^S8|hLP4|A}DoN=O;$ma3B6XV&YmZ$?rDd z;Rl}BM1#?A2XnO@HX1MN#N6z6xl72bw$6DewLF#yQ(alOiTVA@w*fZLz4IFvmR}5R ztb3ch%ijhz*U@FHfC<{vQkHUJI7+!08$*-3wI07brB+x5MLRu210Lck6kT)$9$TW+ z4JhDJT`?9 zXfErT$PE6peK1kcZn`3hHqs8PTK3zzVy;&w+JV*E5uWzG)skH4hOh%4fZQyH74S)bndKM(_$t7~avTGQd#hnpSWYx%m{_92ImWcJ*M$vXxv?08i9_A6VJtTmgRp!e*cg_3JD{Kd z1s{I|;FF~&w#M?njtLV-cVSal9xS6_PT{ke<;@Fl7ZtRf+YAtEeBq)jzc)Y}u!V_2lECyjMxmOV{R^S~#9R3xl&w}d#Itib@STIIym^d&l63>EP z0eT9b1Xi#rppSq=RxldSUqBKo_z*Biz!p}hG+?NJw62VQs47gXIz_gzLe~M}_^`-! z7J~IL%q_z0U?JlHs(@WAXy+E{nga+5ChfJQ4oXFG>m-PP?dk$+csR?S{P!q zx)m*F8?W+be{2)fZJ1ANnz}tZ#3Xe)2GPIF!Ts;aVv4#0&2O8o?x+MYQ{6EfVz#;y zqscZ$-PsdjzPhU*M69|CeZ#g$-JKa?iCf)W2hyYNMo+LMsC!@u*^<;f{0dj8d-;wa zMcs=|XIrE0!>ndoukOQKX4|Ch=Zkf!y1zEWcJ%;O5Zi9`0A>T*KJ}m%!~ykSMTo=d z!C??b)k8%fj;n`wVNR)sLmgW4JW-GHrg)|v9{}+}J>i0QrJm>k@kTw#o8rBCvOB~_^^_Unn|g|0#1Hj! zFvM^5G@8k7P|vvYLuOFVpcHm*^(?FbyPtZtF+_lRE)zr+^;`ppAoYAkh#cy9RF^%M zdI8mK&#PXj0Z~A`$RG-;7de^KOGXH*dI>AH-L78dQ?H8`}R+uZKg_RImRHQA@qyfT*M1=m=3?y@~nF z-blU4OW#Djg^9x6LcN7Lw6|1m`#`i-Z}U>LSMQ)j>>brRoVuuYd6~MYcPnB$W$&rp z0omh+xHIT0**OYY{k^VO1tVZr z3JPJAX-?c|qurDFCFlY6P=cWfMzBfKlT4-cRnQH}71Sf7+i+Lxf4IoJh(AY$pOnQ& z$I2l?-O8L)08faYMsu6uWR8>g6X)lrAOyb%)Z(OGe#Czh*Kt4((9775F2m|Dt&&A@ zPZ~^~>X??CzIuppc#8cNMyk`ID;61B(?isy3C~%JnqO_JfkriL)A6va#!&k~-cn;$)Ldc+6VFyj*$veHaANUFwS3fzh7QI`5!ZeVVGC9!rnciK3;x9cw zZnqbG%mu~YdWhUOZ~NE+is>@R$)P@pbvl7(>Rf_6btWMTJT}j}<>a>D+u)}*LJgn?q~WrG5bV)ILkFdFMM2Y!_WiU_7D%zGOY zKXgSwpJmJk6JuyAshEPd6j;$OQ1sLl0bY4nv1>$yi_0XZgczHmh(wtsPj6`K)d(s6 z21b0z6`%GcmnU0mLy0Oe0Rue|?h-rvy!bDzgo`a+wPGw0jUQf-uY1+PS>i!Jbpa*V z@|*y%_per(E$+4Ajxh9cM_mV}8gycvh;*t%h|2^W??^_(mTtrspVf$F*0 zirj#XVo|Q1m#ydv5~e`){A>j}Nw_ctst2=`nE=8Rs2;*r_5kz~X|G|cx&eeKP<uaSTg3qaIxN3|G|<^0;Y&yzgS8zPUypDaH>HpWgxs}3CPV- zZUDr&LX9xC8q0HdtnjJKR<8yu7Ept&{sl-BP>Zc;0$3@a4qKA|SS_FdTk`=RjPM#w z*xFivO~R)IbFW2@4Nn!+nyq~X*e;+QTUQaVTR<1K4g(>4pMc(M-Cck%wQCGu>%##@ zh0k!dej?zcfQfAVWxyE$)7S7%N*P_V%P>=C)b771-3C5AZ+a#_Zhww zMB#UZ&qKBmQ&;#S0dLqQo}4(%sPTnu;>ii?y2fv|iKiy4>zdwdGf(Xkk5e-f+srFi zSl2bPv(5VfKYj68POqAI*%mC?CB&&mO)J~d6JQjP>}<RLcH0i{_QdVh%=0?M+qW`Mi`%CodYKmh@jS=uM82qi)URb^Z2z{?__2HUz2 zAWlkZ)?`~>0K$Y%9k#79ps0X)Y};&rFtuwoVB78kN(-O9Y(>{_Q|-`Y69l6 z?VoWGRE_x%^I_T20)~2iX$D(pOu!e1k3wQ^PVf@h{8|;ZhM*(XSNEorXDWpspz6SIW@*Ay&3}y29#L zf?nP;AT4)RIZs6uVo7!OK@p$_Dd1a_qCX(We-v;;u%YEiD9pHx!>78al76LI5)_4- z%1fAC*hX`fz-;~|d1kE9P$(V#C1sk^yRiUvaU~S`ayGrE!HpZ#sH>8E!bY0YcX&1F zJ09FepUMvO=!@QEX`y=`@Lv2EI*x^(H}K$x4D*8enr6@ zb-t0|T)&av?s|dV8rv;o2Oi-fm+P8$>^?haf?|!Xc#k$a*cl4knB(m<{YwvaFaf+t z=dVU~@HW!yqeuAaXJ&`Y2#^}n`kSYKxx{wqF{5Gz95NZvFXqdbGfLRl@$*ELePR}T z^W<;oD5nSPMIQD?RiyApDrP5<$I$W!LSB=KnbXkZJF?(ZD!)`jWi|xRn*Fek4#~8m zGH0SSm_SU=V={jC6#Ua(TMuZ)vBd)gF~{o)J37mo&uG=gh{;>k0(BP57|@knK9$PdxBe)*d+tbXyNmNrL0HNX?n~mMup}cLZ33}imn-g zXkKP0th%CyUog#U0EI(W^z)la^QNLSV7nEGuTgqQlgEQGiEeHetF`lAFyM)k))h|KCwEXIzk>d(Fq+0Tk5RBcJ*kZSM%yCZM$)g|rD>A0kK$Orlw6>$9 zHZdD> zwMbsx{#s-QhymJ6^gzd8Z6>;hW0*Dz)1G65HVd`w7_H6DjNO-GoHiTP?Qm;RsBVXf z-{M*p*ur3X5);sQ*%Y^>oUrqzVdG)Jw*c}>`=klYFdjrw&R9|7mjb&RpvcwmH;iPbPN`w0TRH+|Otua9dK{3nk#678$Ss zy{-bPCaD))jzmD%$2{P$>P_HvM#9AEsB@=D!m=8OnZwnZ7#ya3 z67JQ+#9%rk>wnVYnp;6ImU+0RwK4)=EOU5St35!^=v}Q%02s^M=W}h|xR*ugCI`~H zji777YjRL+m=~_T(knN8#MpEVkX}XV^b17gSH>^H@NYhPr3H3h_H&lOb~+~D!!p=; zcx?j1X@*?rqMKr;8kY@gbA9cye$-YbfgLiWG{c1q+~PBo*$c zHO9rX;3_n_wHSpPN3lE&Zrj1Iio!l5wka6jY!JNJk76f-^_w(NTkw5|K0VOgM(BB@ z;a5uf#KGYBpewdvbHA_{qIA|3J7Dc>azoM0EfbCQve7N{MEh(RB02~b$RZ2`TQOZx z1l6+$QUa+ld(TlkJ9vST5<_7{$2eA&t=j7-(eQ&x%AFKOELCrx;-A<8x^<|BRZ_O z9-t3ylRiKI-8_!&W4J=`AyBN;ueC2-!>_JEv09JuS0Ke>(y^`81N?;psrc*Ib=x-R zniW2?$m2wi&AK8P9ngdI(1yoaMSVT=q6Cao+fF^eLqlsysEwg^;U5KlyDFg%LLAZ+ zkFdE)SOLY6e-u8CC;_vw?SxcBeEqx({}a=`eqNq6yTP#`CLkbbklSgO-ti;6(+&9A z7&&z-H-2_FUB&X@43}k!p9yBzb>TTHUdDJ#Qh&*~alLVm_}B=1?)B0?3g6pwEEfBm z=2Ed_rEh&YbO_zfM=It#MHg9lgV)MAsfm7qLAMMOrtRWC3g2(Ej1Rgiy5bog2~6~c z;<~PQf!>{1o~T%?u6T)ODibk1+ivNKPw32vym#N#6<>UMQzB3Ffw&AeMSt%?%X2~T zSP${jdois(4Mn=H_>CybVY}G$gVZ@|XfmOy$)+Ei&e`pok5eITvh(U88Wd zRb&!#E*mD&if5P``Jp5#Z_d@>#1DBW=38J=7PnCkc7s>SpNN-H23s<;G#$VUYY)_U zijfZNK(&_Bd2Sgx@EQ$KSLawiI>;@@hBCn=3-JW-!I4n>(+&wUE&!)OOb4?RkX z??yU|3UKt6inw}chhsrdRM$hS_T5H@Q;{foG(QlbxIVshX!QqNtXYrK7m8z;MI8Fk zW!zuBjcLsv2vJQB(a$%Mj`Jr zLgEHt+_+CvhGwCj#$fcr6B-mtbj1+g=5zuUlEb4bh9Z}fSn3>!x?-4b8l7wg#R^?9 z0+)L-28v`|F%rKIbMht>`m7K)%6B=P3gPQzMSLJKOH!J5mKDaT2Irjra;N8r(4wm% z_Ear;97LoxhqvKOZ4SDnBTAba4l!4o3k$|EPn#DC5v%d1@*In``DTbXZGK0Hcr6B# zuw$7PgHGm1(iU*TewDVM0mQ$`!Ts;aVztJ9HrcUGi>(f^QH#aGEgS~1 zO*!>{>D^-ojX? zFs7U~ZH*iDjMJ&DMKe1?wYB{rifHQ`5XCkADy6fewjRCSSz24)2BN&SAv;7xZ9`*- zD%wVvNX|dBjWr-@Xt49hSzFsw5u%>93A2N7I9*E9bS__D-+SbevJ+!U$AbM%rd?5O2+t9ww{@Qi}!~ktOdaiS@ z#(%EaIaJ$$-sv2n?ZA@h9Ifr-1s$vL-->X$wOyQ4Z5J>0ByG1p#8ho}ZS3@%)3rT3 zwV4`!!ND1o_Otnlz}$vVJUG>F#Yng7p?vtaCw}nrCrl<6exDalFWWBBI{JmycH!m) z=NrZv^s5#a&nSWIW${qUuW0Z)$*YZj(C;Z=`38plBx;=g=fq)K{XeGu`vG=}OlE!` zr(SqlkV0|Gj~DIAlkUl4asGy;gEE%f~W3 z18B+D`#d_Zz4?PXa`gAdF6w)(TI7-yl!r1w^1MwS&zcIZjT+ub;#%{mu~ zGcx?nVl3-r;5q5P)3}~x%LRt%#pBbS3YITDg*OuoT|s z(^((60>{7bqnqe<&Tcy245yo=u}RlAHSvpJf?u5g#b~*9#xKT;C|8#uMg?6F#}#Nz zr@n!UUyeMk6@)^6x-Nbte*NVdpOSQQBYqWLrn!dB96ni3BN>*_wFHEyr7Je_H10xy zSBZpuyLl{It%B?8oLYjeS4Ij6dW5ZraJ>rxG|~g?38(AmbI#^qyc(HoGztp57%z%+&c-%j_ARqdFT!n@ zyEl0Xc=baBc;-zvkAkgkJ;W2-D7hJj8}biz#Y>|%-O7d_y6qnS+P5v+fwikdO+Car z7}dAhBZO{t#eYO^-MJEqA$o|4{Chav`h@@^^hi=JYHzi z+h|j#e(3J;g(-h~FBFQ5;>l5$?(oH9q8=lc;XB=_4Ml{m$de&E-N6LmoTkSx8R^a{ zaHJlU!*GvAd0+n5#!A%@s4C(5*pFS=fGe*Fm8h zgP!t+9(4C264keRo{CW1gKgo|O-@e@*zET(7@bS?MC)*cn|B1gr+E0omG@pi@z0oO z&XU+6%ooliJx*i%V$6MRIp{ZCJlzf5={|;V3Cs|@Wj(!ZbpI;?Y|>-&LpdIxJ36=M z0S4Pxxw2qPL5Ps{^~B1o{S3~^F#%;)O>{f@hyxqEB5`aewh!{yZpkV-%okjT@QWW! zSEj+Pc_*nJT}(sBN`jWe(m^z*qmiUVtb_y9Qc@Bf`U})dQXCyb7k6}(w2}^C8gsZi zN?c)hxdA5sK1s=Rm?zp((i+3-?w~&;t)|0~2zgOZTpwDU4lBQ^q`q|Q4np>m^cSsx zJ!P*hsXwiG3%}`-{-)zIL9^ucyEbS?z+ zKvF(7m^ag7NrmVfMs?BGl8Vu}ZP<(lODaj{J|W~w5S~GauSFLp1D{FHigdmjo|0}T zD830@{DO{{Nm56;R11_%QfImp1Uhv_NpW=bEJBu*w4AP)0txqVam3*j;a71L{wJsTRZL;o4Xy;dDVox^xAUGp znBN&A=t*HjKCCNtv7ClNFx8y;mfMqnDdZuBne(_F;s}n69$|iTS)?Ly20r%lek&9~ zQjt&;QJ$j+j;}Gdo1_#n6kagDX-YwNIsMema~%GU`Xb3QdXncb`8`U8LO+r5T*OWC zN6(;u-5@5RJHyzBGT^IvjJr4meOwa?{;Uw6rS6B*<0e7=3NKV3@s=<75q^T;N$CVOX)u_-1IdNufi{;r&ST;iykGDk)EPy zo!@m13Z|!PQMdX5aRQ#}p{Fm=ye>aI(So=T{^=2yw;mfLx4IH<;+23;7;7%w*h>sc zK(&0t!seQy$GPiercam+T#IxC|4`CrK65Qid*3`IS4D$s`G0vv;+(JTMQNN1w0(Rf zU8L>9tm%x?_FEx5+Wu}3iP`~-U*`(#KnI9b+QB>!tF?nIA=YV!U?V#>Xopa1&du84 z0Ejg0Fsj|TT|3OX!@tVG{qMfECp83M6iJBk{29@36tvUVQPj=3O?X~%j) zoYaonA-z(wsuYlth_$!rkUwUdn@ZfU155jgK^r|Lo6*G~IDJkn0{ zp_{Iq@q&1+ov8}(N;@+S;*EB;9K?I=>~M%r+Bsf|ui80YzdyC}yncUc=TW~dFYN** zT~`LpeW53$uXfP{;jdln2$5O4loukab_o^i%C238x#7yCU2YDMTf4%$YhLY2BZvap zRaCdDkaiU%beXhkD51-$T|)_7PVIUIh*0f1O6V%AT}M;6ifT71LxgKLu%^07Yd75$ zAdz9A=*-v}a1fsw8 zI3vU$?QtE5A=(pPh~e6k>JX!}CnF)oYU!v_ms?BcWm2`Lyc83)r#$TljXw$Dnx;K# z2ob40=h0_t&uc@>(fCgyy5?#xcw#ZyOSGM9q4u&W&PZH~wU@kV;sFS?chK zl6KQ|T(Gl=r2TY*8DE&UrEe$kG(r3E02WE6_ zX#&Q7r`vp7j1rzWPPvT=caD?fL$_CgCP>OoxB1m;l5)`15-u+Ed5Ise8yNnT^2CPo|G+<5k-iA3)G8{y_oPrX{(ewQvcSeaOdbR|ZNs^VG@hg*qBf&|lFq=5^YYC$Zs2~@UAxp6I zZLlHgOR7N6k-baW-3et_`Yrgmq+b2gYN!RGpOwd$G59u?X(3i+TmH3rD zW5l^U!gINizSIM)l9YkIECj8Ua&C6XeFNAiG(atbm^rrHDf>N5WjJe?_XVa7x zEMsqwY`>J&EaO&?Z1~uTyt(%0GD8@? zi8W!}cy}n?=EWyfLSGwMc67?n7ZSe)v+USDh2o_J9ASoe(Oi~&Eg0`4Aaqz}c)tPS z9R}{5$IzDLz-kxzS+W^#Cg&K2od3tv#W%73Uy~30CQIWCW;xQ38UOw~3Rfna<@kz6 z60jHGh5Hl7aXHcB!n`DRzyV&)NHD)mfTtY7df+xdPV|E?+(qUQ`r@QM7v}vi+(qX6 z7yHd}wE$xyz!|?mz;eZagCyg98J6oRIH%+xh6XHmUNAN%Jp3>^$8vW7%P)_HjR|16 z5U2q{3@} zty!InTrK<#+>X`rdr*-eOg03F&%2R_87m+~`a#{9gD>=4Nkz7RyD=}On^fcrxF_=l z6l&23a38)rEe91H0_@M~G+bU3okYL@=FM0_Cy| zT*6%{RU5EM!hI^W2t8@QT1ii-R60Pud18J}zOevopu_#VAm5pQ?Gj#+?{ffl(?Ro^ zN{d{PZ=RUnQt1hR!&38}O5X)wGaZ7ZQyCZFqyUS7%IJWz5^zjh8Bs^%X&DwTDl3Zk zs#F060d7dhN@Zd41l*S3Lw*>V0_2+~mVD&b3-DNKij$wH?$0DR$v;2fm4u4q-vRJe z!X)xX2Op3QaC=y$QviII0QA$iyJQOJ4)`u1g#u8jCZmMqWP%xQ$}C|WncyIqvP#%Y zCiKH53?7JJ7n#uun{r7wM&`x<`R0k`1ewvPn&eB~mb(P=#bEkXzV2jsOqN=pBJ!da zWLXH1Z+TmT$tuEOgRIfRT9K?Gn4g4dWc>^dGSey77ZO?B*;+wn>X^C%YKx|2Tn*(Bd z;@heayAt1_Vf*b%d{-0VVB-7S5JwWy{C`yv(HZ*}RumgVaYg9@Q9@AzAWA7J zO2og6q6u@&Ptn>ym=tjqnZH%hn?g91;YA>Vl;O=FLX{DM$|)mSKtw1bQH}g7DkB9& zDWilgMj3^2^^aFZyHR=kt1F{1;QQB9#vl{?>nLO5AsQ%SJs}z@WAUK=jg+x?Q2%Di zIPst@lyRtz{%w@;R)`MD_|6cWlnG@ax+)XI9rjQrq6Yc*QYJQs=&ejD1o4M5>34|! z%H(_y1C`0O+>nEnDadaBp~@84E&hr!)eAybrdEU)p-dG)Mk`5S5Mz}j)K&lS$}}g$ zBxPC;h^fkS6T}Q6V}D@k$*jf5ExZu_1wVou^U6k92H}UMKYlob zYotT-V_#zGe#%RkZzH!-Ih92?OeO=h)^9mqu_zOEyJW1x537It;APll$asezeypM{ z^2$8e@DY#dC-R~u?tF0(Ps5jnC=fXUdH7sz?(Ua#YM3W{bd^bzvn-G*iJr% z7h`aVFSe?Pr(Jy3(=g*LT(*5lPoJzV;`aNoZy_bYFv94rXfTBR*hrdECu?cLcl1)_ zTk@5uW4$Po$k-p75>m%|Kcq~T@I&m604rn3OSCL;4iJ(`T^B@N3E%|2nbh@$GvqZI z_ac_!@iH5nM|VEqKl+KK4DrKO!@R2K=KO2@xMHWs#eN3-EAy4#AzaqMw^bg5sQV+t z9#pZ+7_a^F^AF1!XDvb5e#H-?_%4>gG|l!IjFV1fad$=q+5sEEV5flwJa6SS4p7*T+-__@#g4%q(V2H+pHFdQNx%p;Eca%o;^?shMzeaa=W;4O z5j>9h2<4{Y=fRUW`01`xq5yCzt50KNP6^cQ|CXBjKbsu3mkX2rEWT@5VkVNG&3xXF zMkUaa0+RV#1m?@xs3f}UfcdPy49}?~dhr0>rewZKIjJOaK7h9=nQwNXk`IC6<8d(Y zZ+TLwGQgF5^|gRQfykgkZNYnIBLYw1xoQrDQQ6xiTvFoV>td@OngL4uCH(8?isFY!>ikW-oXw zWigHgT$AZp;OUh`%@4TA>a50jRQ3e;F0&8zNcrUkV_{LGZ}}NcyJkvMuPs~SA7tN92pT+&AeXPGO_cmj|fxk2v{kJG{!_c0$D|68^`0rGb z9T0nz+CMP*@4h%3sX zY!KI!MO7gFSqkpICyASic-h$hjp^5uR*2pvo3f%lL=I)8 z(B)KCqDv0=Nm=CskylxTBm(j)t34nJDyzdGep6PX=mLr=YXTvPD{Hz#lv2d&dI4n= z_gds^fS8ZYP&s9zpa^ASONa_e8tkKh%8EEB zJ)nxRNkkf_Y=X@eP)!jBEd?Yfn_ENFQnr+UsH<#g1<^p+Ds+vMt>VSffF{be;tpuDu>Mw%ap^SUtFOa5s$Q5 zInoqjopKa)HDH5sR6N8c{5$am`{0TPX}?uW6}- zeH4alF|CkrjKWR}S|jNml|wO_Pit+&DlTeYO>H-SNX^p_j6qN!hELG7I^$}1^ zLQ9H9b1=)(H3HjE^lU&Gsp&w`7>3OO5_(XT3IL0QfmCG@z#(A>RYA=%%hNRi6^cR4 zGRxC70(FWRfoTnMxC}R%Vo+zy6(o$OSkxJFl!U1ii&|oikuZy5PXnq-SV(cGGG@HX zi@aY(aeV-FB&?#igMj)H(kT8HfSeZz+)eSF0CHX=@F=y2hgE5o$JYd&r>Z#tZRABa zsVX`wa|a2Jsp>j_oEQmwP1Q01dPvQCs#YH$PqGXAM%9)A)MxVR`OJ7SZgAv-*wU7+H>iOG&ok zJJ)2zGnvXTV_A@_=x9v-%+;{%9DS4Nzb7X4xLf`csl7*XJlAdA?@KmxkS6}FmMy0@ z+4cbCj(N9-EpI=vi$|{@?-lc2dCA@gT$wo@6UcT{GgAz6eOLtcTi`h6#duZ+dR&t@ zrXEvZn>|XCV;i_0^HvyCj_=@xGAnJ{y~!B~#O`BZvDmi5;BzM8hvpo9C+tfn3^Y?q z4u3E^xiSOWu==3qOmbm7HMM6xj2-^2G2l)d{AgZsodkAe^-;_)2a3kso%u9nr$F?~ zrryl%%P?pHr{V|xsJHE|Ck0*t;`lW2G!G5$DCk%4Addc-F9kIL^1BhX_xMt6P%?O! z3~nc58w$DuR%LK|=H3*H;m6xZ5A4BGcj0N*&y&V&~P>2XVQ>q=45Ij@_Pi9_(vJ347UckHpmxrPUGA&|W zPwu=F`W8Pd;UDl~FNL|l%jEq#_Ew}Y(YLN-JmN`WHO_=HTX6f18%Qn;jmpGtEI1w* zR#Ulh;Ehu6Iu3ogC17?1P%ddy?g@B1>rYcY4+;;MdTsq-{qU|0_}iFvcvo75Mn-dk zB%eZ?`npYfIplfIZ4@qU`~dSsG`Mha--qRmxvqFo`9RtaN>;=A!rJ5OvcVs0mFfb9ge`vo?EHHdGLD<@15C$Dq@r~WgvR&cMmL6m@2}|F@2WF z1^QvITmghvk9!NmA+Qx+p<6XD;tZQg4j_9Qfi^@|30;pF+ZV;nIZw!_ojVQQp=Ax``=DC5QVP=|3FtC zqA!BEV-M_t8eRqNo7v2I?Bb>>==jYx=7G5XDs#a&)=y;D5KpRd8yLixSchWJJ(xon zH6V;+b2-M5b=(w#=3|cF$i^VC7_@eCCFb#9IWF_MV&G)KXzk`$*3S@(Mq`d=P8N(t zV@_ZeJ$D=$ySWzgGQnu==DN%)4QXGBY|3FZnlAjMKNoONIf38-M-=gy;DF=GNnv=O zQcjhHIHR0u1#wPyavHf3a78(j7vj2drWV94<*X0H9px-aEa0AU4h0$TP&pR? z@kBW%Ec}0#g8T1D;<<7jH7?+#a()oR8|6Y6#Ct`2W-%Z`xmXV3vvN_;H{}w@M9L+Q z$B1kRm>UXezAS zK!Y(AQ*OYsFqKqpS|Ca*H^qorR=MR55un`a0AW>bmw~V=x0^z^6!#r5E(I%h8bX9A zcTo*Zk;>ir5EYesKSM+*_YxqYmHSyCVimEo+f-F~;0aMfc>oL1R7ZJefT*uL6nFT$ z@=$b}jg?1sh^ERT)Fx94<*^u2S}Tv+L$p(#l!EA>JaIRJ?5sR31kp`-D(<3(@+=QT zFGXw-HvOSI7wPs_o{Mw`C@)01gOwK|-J!}0k%@}(G6+IbUW&XJp}Z1S(rD#X7l?7n zYw`3Gl-J_vCn;}yA*L#CP_0Zel()s)kh7GxunJ9cm3Ja9<}2?SK`d0>7lBAo-ix5A z%7@Yr%asqEAXX{qB7@f|=_0`mN(Sn`X_JzHPRg`J`6wQHhw`xp#4hC%YPD&P@=4Hs z<+Hf8L&|57B}bJnX83caB&!0T5^L`sk+YF-A-GAO767^*P@;c!cF!GrvNr<5u!vWJI)Swz-JEa*X z;ouhPQBB;Wd5(mpR1?k1JYPavs(A_^-NT@+RI4~Zx`#o%sa9{mav5wO)q+`OUM)eP z+VIuQ>m-b(+880s(mM>AOtrTGa;0oozMEu=cl0lOtEr#fo^`z5TUx~LrH zLlU-7-Fkpy5_VDDB>?Fw1|6Wfp8#j2<_OiR3b-KQBGsD%xFR4pGu3+!5U!%zBiNtn zqyCxY;fui*sxQLbm*HGg{}teogb->_5g>iV;Baa%8SqkSDp7+6fVUE=(C?TDFbiMN zBe)vjS6GbFp9^k84Tpd-NmUDKcn07tp&d0U3Gk87i5j6E zT5?M0L5&UpewNUe8l!nz@=F*%jnO+;3Q8DCjduW~s~D_OlUx8C?1P&dMNL}5pR<&b zG>w{UfvSvzWNPXIFiBWUO`8I25|&fb)c~i2_0-G=2$rygn$-b>N!UTn76T$A9H3?y z0J%~ge2SXK0ivbm618YP3luBq9yLcJut;AqID=aJ4v@ZLNM>qr9Z**U3&}?f$V<-?)A7z@hBWKyB~o^JuXwcurONjAb1w@!&ODyY|DHV z7DiQ3pVU_=R)#wbiae4JQ z;6!Pa1Qm^?>cWK4SY6WdASILlk6`vg@(FFhqqE`#Zt@7S7N!Kzg2(dZ!Jd@x1jr6f z5I){bHB4Z3aDt*?0oFhlYfj?mstR5M=CzNYS}>?;yaUhX=o-XQO^hJsWX7hxR1+hZ zd4asz9n>23UQN**vBMM8(Q_l!{03%!C#XB_ww7r2%Q(3|P};Rr@Ji-^9+RmSnxT0O z^Dx+lwb69U>zPMFUpoQJ9#7DC#8+GN=bOd-xIKcV*{Sw5X~)~`lc)wu~i$j`UYi|Q5w9%1#C%wMQ3208O_=3ThGx@ef@ zQ_`>N#N%vf5hmkws%B)BXL z_WGgV+pIUCcSIzw$vsJ*9?}>nc9p243p5E+8_rlnuI^7tqo(M zWrU<5)HV??TEa+bi;}X)Q8i>TwL>;pCQHpMYA1pvNl2!4BA6UhLl#nd7#EhgQjP;Rhg+vcEC9axu{Dsz$FOg4SFx3J#|NyW66-vow~08$bDs@e^U4F*v~Id zG!9j$2YNQEhYU8BdMp5VNti}G-UGZPBvViH(AMk{QmH30*qU3yTIy9AW1=;mq)pTd z)x`R%gk97NmCjmJ!XfG%50KNWp{J<#Y=9g(LoZP8mjHhm><0Cz0FcwHp%17JdRePO zYF<#E`+z_R8T3aGKu)uU8L7n|7%!~lg(}R4{1Yhba zav?#2mHLTXs4XFw`iWeqCn1dbiCp+SExpq=F%~}lOTW!@N%@8z+H_43hkTlDD&Jc| z+);<1Qkm|lLs0&vhw4z&Skn`AXk&=y>M)oerkCol1`u!5#C#C%)I=BrrgT;L3F4!w z)PVS+syQM4SqkpICyDQ>S`8hIQPo6$?5S#1Au_9aHi)dM4kN^zT^*hkBBwe$7UCy$ zL}rM*ZgoTyWPWv|7}kGLM^=I;q>h5AV=khOLZ4(Vu8wj;lu}1WK$KQT3-VLP2r{W- z218iYvBHY5t78SZ)Nw)=td19!K$ton)xum}ogm_jR40fODytJSK~z!Q6Hz(Maq1*- zH`UZhF%UJ>$!HAb+UjI+-}Tff;+cL|rzAi$R;Ol%XsS*{sheA<;uDDG)@l-p!rV@s z_A5k3by`!1&g%3c5Z%@3Eg*WSGm1m}p^6<%=KktT6t;PQI@8@A(yh)yPh%dc&gu%G zsIy_Mnss%yppohvL1WZ8T_DD(b4?Hv)VV@8SxrX2Xr8Jj3!1LZvp~#J=XHaatIij? zdFuR*5DV1>f>P84B9>Hjp$NG`UD!k9QLfb#A2Jlfc*{Bdk#I<+OhzZn@b%`2Rg} z+{_gu@Qo9zi9h0u)>^Y0j`8={g}JeiwdYOY~2IJ2}Yf?E})hIfEjdD1bsor67- z>>Ruk=1d#4gFY8?Hy8?ymV$p`?uEQ+^c0+zxi2Qx8l&D=eqkPf3Eal0B$h(VZp;KW zUIQ+|$*W$}7-pTN7+a0OBYdd|%H6`7`-3OPQWKQFg}=)iJOkRjK!1*G4xX(k8kdD< z;er>UV49*?S*)y2g_YG51!-|`Vyiu=>2Y8ntJlLQYnBVl({S$KEy2{RHkjw(f_HdQ zvnjyx9DEOKvSydS6*#tIg{e6jp@r8=g3rJtY~Bo9g@a#0t!X|V9LK@$A^3e@HO66JbdRFj@wX$*qv*~o<)}oGPve~f+?G)s%do=K7M-kx-HDKqp49dXZ~{j% z9{JG@zKmrO^E7y8?a&!ort;Nu3RAm|6fGU>pda=!}w=$atjMndr=zf ztc0WhMQJQxZ7Og%a1rAQ;0qvc(sGBa_N5M1Ft5ynZ1JWJJ%Q|~gzSQ2(_sUc9hHy+ zXpbGyH(7XTCPXY8b*u|cLRRM-lP>;5N=u*@wC`ZiJBW)91XsUXeBq9{g6P{5HPzb zp`uxIg_~lz$NEsXyj^#KAIkd=t!SriM&J|1xWd#;bd}E;Yk5*Px9BQg^VJP-d)@AU z-!V7GltFh4u9kFWoc2oH+k!tacgNjzUjqKh+z)rx{R#Lxvv`B22l`5@k$E`ou19xp zCQg66oqD*}fwM4AE6m1jHpXO6>WQw>nuD)ijJxYO2FyKm=yLSDJ@0~f$s}|wxEJ!q z%1b7pn*@&o^Abtu4#AinuomO^_JMmB*w}n$)`dn}><=%l{kEL$=F;SBK>g9}TU$$L zPJg1oTjc~zSV#JEGT`3~)=5UtoBkBBb(Juf28h_?8xLW_X@H1LI*4K8X}|+F=ua7L z8Vw8v43aR1295*_m9UrwUIVBSR?;9dV7P?!G-wE5l!Prb2z`ikoP>Qe7-ealB;f=N zo(h;Mpj;Cg{1z}vLMu19VMAD@>sPKlxsw13rRooIKL(^om`p>$0I3qD(hyNHD+PpC zrXkM(@+G41coMt)tsAALHVvHs*eszT4Hcy)ciV(Fr(rI@E@<2y;caP{4%#Qfb)jLG z0dl)dcppmi2gvO<;R7jg5a5IiHiQyS0?tZMNGS%mAYlY4Xku2m+a`P*Df^iZmnl9nAHy}j_&q@l;KzLc7cq+zhPzLBt%bPs^^ z{KEH;j&8{+r%=NWlAZ#PQ>fvGN&kp5=ne9k<1{=T;3?r04WEr+%9dHu85)kh!{#I5 zJdLOT$R*(-jhF<;BjE~-z_4q}C*c~6M3b>e&oBH2jT`}xQ>fv0XyjEuF&XSWjWPjB zN_a%VDX>X5F#HL%7UuO6iX4>l&+W9K&a@G*$NrY@zNK z3AR!9i+gLY9zdc(dKk^wGEhA%bZ%9A zK+!T(Jt7K5QICpZ($%B=AV#XkLLkPd$55b_aq4k5O4~9~JprG>GDSU631XUh(hFjS zdNKxLj(W-)VxD>`9%7+-8coxZqMlBGNL9}WTCSd{4zWr-n*(C4dRFK*sOJQ2RL=?8 zte(#Wu}wW+6Jm#YAveTs^@4Z-$Ffhom>8^t4`!#tV73PA z$9zHXTd;}w8n_P%!OEUP=pDf*4J$r`gvspCCwA(y1<0=nhrTIHeZCm*@4v^-24m>o zlYljtlmFyNf1up0<>cLl8DMDqA%?LC<}5HY{@4Sqz=x;eV;b|RuP1(B-y{ql>7~A? zYF745!V1G`?3)H=XC%xQlTPS!t?Z11nQ?hP^dr_<9KTbrNUtt)2<*0gd%+Eu%i(_d zqY7FZ{YYQzCF_sMXl=>?sv?5^Bf%}0af&DPKMv-_maxXKeE!S}X7?qmwVnP%*;?6s z3F};#{+t2qEOR5QwD6xU|tFdTM3(c5L&5q z1nv)8XTsKd(I7NeE5BtDwh2b|AawKAu^j&{tbGg?m1hF;A;WkYEG(WW%%>23@Ivr3 z`Ml+tc+=q5z}Zq=uBB&RoUsm0W^U^lL2gmF^98#-%857h-J*1P@uOT{&pzbN059bW zCR2VIf`P%hoOvorYlyHER>}0j@zug1qHxwob$At&_E5BNE3bWoR}+keZ%t!;ZNZbk z(x=cfF78gtxVY!1d-zsaQm*dvh9!;Nwl}-mR#l8~<-KTJCX8`%LbJRNjjIp%|JT%( z*Zf4|mLizkL|VQ8jr$B}C^f&)_-cSA5{l9IxqucD%F=jbnXR=1D@|~tqqKF96hsrI z06I$urwI=M-6d3_i6}-}FA1?UaTK7hgan#+9WX#bU7BP843^M{CJhA)m7vh1O8{wi zmS0Jeg-J3(YTRpRGV;hKZO!uQXtJgFU8M-vQ4hyrkKvl(ttA(rNYrfHXBDzSHdYBDbWi8R<=PssQ9FLu4MBGac|< zTohS^<~+qqX42M-EK7640a>KRMsvpjvPlS|x#)fDIV4me@iL43Ckc%xSp`T_GqMdO zUj+OjHC%Rp#%7ncX5=WEUj$HEYNpWq9soZH=V<;RfLX!| zS|EbiC48j?BA5#socHEYjJBFdK9%?Naa(tC^K6B_b{I0y%07H}J2>gXW`p;$z6+*G zhTR0S6Cd6SoLCI}f5%ZuYz5}E#_+-DO%oS`Px19b!Jz+dYVQAFa*2U*7?no)f(tTVM?D%T+Bwf3gx|s_IC2@dsMNc|@8V`hVwkm+;0x~K;YQiOY?FmQ z#08^zfy=V~F|vNt1~AX=hCfAuqtd}H|TA)CYo!s1F4dS04&0r9MLLT1%^s z&>j5K6x@GL5@prLBEbOlu`rg*>J#*YR-5_+WomV*PXz_3PlaU?rap5)gsacQtwp-k z=K+wF)aT*}qtzGUDPq(Y-5{!}FD($&)t7>5s;>mqQD2ECtgpVdLo`%hizFJWZ$$FV z)Hfpemg-xPL>u*OKZy3~J7M5-RNwsx(M5ePsJr@p5JWH4{UHLfkD6|T=%=Pvh8U=3 zz%a48)eO-dhp8VkLn!LU7zkbcgyG0KQvHMqU>&1=&I&PJ{TvH1QT-ws^knsmh$TtI zfwBhc4E1Xi#2odTh-IGotpdaXRh<1`U95h02SYAVzl*0>rVSBKzd{=#dac#kP(O%u z+RzRV8?<2}pEha3gl?;rXolFKC5l|zttldB_G*gAnFE?Ca^|q6itIk7X(BI9YFb~2 zGn$TOV?D3wA}=m!!$aMWSGC~+Z)oD1&2tjwXk%As*BLzLuMukB<(MF-2S)Xg8 z1-;Tni>G+2jS-Q4(8h=)GPJRx5B;o-6_4~y8;6^*kv0yuX7kX-<3?>>+IZZyEvq)c zWcb-&%dSo649uxb#PiyI(k6D8`eoW7{ZP#Qv_mlmDmF6Oo+kM$JkZ~5^ z{!DpckWWONxBbE4tKw*av=>+7pm-1WeG-U;l*G9^33#KWrfO9$S9k}1yijq!(*_VlEgRB;x zEwXLo%iH0t@o9CyavzsRLA2q+kHNe`60rtVVJ7+r8!wPVY)0A56uEVcBiM~ig3_+O#e5jX-OP{B^E)LGXFX|_ zs2TS;x32oqEKxHaG2)%5v~MW~d@dQC(Mh>FujrfPUOgvg()i>r;y^)zL+)OQT0vXS z0HhZewT8B#qC4dF&Zu>?4W^VsZtslROWSS(8VH*;>L6{$@ZgY+ThtTU4zt-I7Yw4l z(RO5zqm2j_ZJ-^f3xAak{HgyyLhWU$M%tkuRp}r`d(w^z0O=q`d(qA^062(f7txt% zC%Q?;AM&CswDTB1dWg|kX;)!@^bn(cNE{^X7$$=S(Jm28dWg}zX}1U_J;dlgX}1VA zRt6hFyY~PlNYH4{&j7h6EqXNVLC@iEPm`*Nv}Y4YE=|PDrTv=$bA={m5go_|*e_uj z9YCdYNM9{xH62(55Ie#=Vm8u20!UviW)~f-4Un!{%t<T$eYKcxbhrpW`f9OWbQr_EL;7m5Iq2|SzzdOTYylFlPCMkXKx|Pu z(i`wWYW(QP0f2PXV$0Fd2yy1U)I`(KIRNRM#ny1sQS=&4xe^fDh>leOWR>As(Xr`( zoD#awv8Mp(p2ha1af#asVRsz?XiqPa8hgAWG?!=N$u zI+zcuh^&MGD;f3Bo|_-9uAP!Ge%aZxi4;d%CS&}v=Vx`hSW12bWXC453u2q+4`#hu zvlT+Mj-y4Tz%lanDg<_+MeTs>*Hj2L zoTEiBPVCjiwwyAlpFr6m}#?H&30 zVtCDXiKrZ1m`mU!$|dLk?A`hL^}e(e4Z+@v)xw-udKf&2`7EkZDq4bl2=gcCQ_&CG zd9Jt8XG*5jwP0Zk89g#r5`JOoyQzl`9Zoy!dAPzTqrJ>ux_Gt%+9Y(;wqLZ#z7U19 z$zohDqD>LQeQ|Ayu%}9CQ;R{A)~2GoY<^l2MgyBkOKJ>Z)usu%!mf$W0NY&J^n4J( z+Vr{*VcLv55dSO%_urF5xHh9EQi{}O=7gxE&8!9yt<4e^R~2m*oFiMDHrorLnl`&K zL=A0@2ShEmHb-DxZEhxr2HITFIW^LfMbM^NautZ?+B{)pw$g9{xxv;}n=d+~j@o?D z5p~fPhy=T73lboDY74VN^wAcIe0LR%RLv07U>7-FrqDimUawhH~WZIiazZHC;ct?mS|U0YKcVwbi? z6!Ttft?*O!X=_En9@N%}yEvk)s|s;kTaTt;JE^U&0C7g!AW}N7Z4jAvN!yqi;)=FW zWZn%eO=RV5ElrfcJ#AAqhzHsxkqM8r&AHG3ZO^pLwIE(;tWY%_+g2<}vXb+KH+vyLHOWWB6B9FGq0+CnS z)fJ+Ewp-|a)piSAVQr6iNzPtW+tUqLT-z&xmeRjYI*e}!9LX`Biv5K#>5P*j-*-CG z86#gc0dbk>%npDwCgXC_S>%+nj@0C*vuHq0X-vlbMrYRp8cR(nI){vLwv=F^b9hQ; zI|(6l?mgfy(sBPs5^5$>tw85dUQTJx##N#7!Zzt9H3@Y7DWJDBCgbYU1!0`@ld2|k zVH{wfgywYNHoz^RHC=Q75+!t?iz+~q(2Xu$1dNn0h%Q9{#z;`<(gc7sCgaA?C3ODI zDN-|!F1rAH_)OeVx~zlbazorIx_lXsEW_=jD=1*+0tp96yz1&)Ea40@g@Kr>kcH8zg+8Yp7Vx%>v>LbgeHct8<$qPr8QA*|}3f7P^j>;@m4C zH(f{NavqZKD~ZoAIFCvwMK{pMohKxi=mzSp^NfTby0HOpK|(naUxagBmJmrde+OKX zP{~a z*UkVC{!H3!`b07&DuYAw1q9FR|Hn$f*0fL|mWq5G%{u0m<)rH|&RY;^eJ z3m!-Nx$R@*h=yKz1}*E47CN3eFGj>==fIPhOL^p@}+E+4H zgEhADCxgLl=P7~8H87j8vJL{Qmj*-SI(29z%Fmw0;p-ziRw6U)TbUceSY7oSn0HK8 zZd#aDH3RNqbxSzdt58eqdzsrK`BnG82bjAey49t@?!){;FY!YM{BV?$A5xfBF9)7r z)POI5rx{0i(;6%A9OF1&TGI=7k#PzT72bZC@oZOG^AX4^AC;F*eyBGrz+TC+kGHKWF~rk&V{P1izM-N0BG3y8(R1 znAw-s7X$K2M^tv?!}``>Ug?PX$&GSYpMoF0aQFhAwEhY3yNo@m5QWnQ6Ffoo38G5j zg|rRbz?oQI+OsolSOd<&Y@!^r;TL}R96mS`Q5rlqMuI(6Ss9DvgODE^M69qxX-LNB8u>@TfJyT7)t2Xe=x z?YBc%wf&;l9ohjWM4)y+=z_I_=r!zN+CkVL_VU^xJik3s6B~}~m9@hnWVCj;Cq%4v zM1-uW9YK!T6SSkq=zp4m`|nAjhIUlc^xE1nUx@nJu{IFDYsX7KG|`R=7p=K=Le!d8 z+6mF9+Gr;WL$r5mC!0Wa(oPkG=&GF(^{|_ET69Z2wbQV2?0vK|J`nx1Gst&)f9)(v z!#+?ui~h~-*3NlA4Ast6gitkfp$7X1O`QK=AEk-&AM9hb3*{gtXcq=ROwulj%uCWP zip-m#xi5*_o2^|E`IW3)Mz?EUpj{RZlcHS_%{f&QA8)m<(5@DNSfyQs4R2qoUBduo z-=JM<2(d{M!g=uiX@xa8SD?vgC+%OSG8d+HJSM zQ`&8TXSF*b!SmXkb`Y1eyJaA*YGUcgenY!g3gWhQPegN9yZ;-+BkexMI{Q=YK^}-_ z+Jl-9ue67vZNJqXR)=`6J<1M|p*^Y!@mYH;vhthu7~~-BNp?4+hxVizgqQX-8-%y^ zG#bK36DJNka%j(xmyX=pGttKKXwQ*dj(pnl?hpmE7k&`GYA@PC6xLoAhbXGOL?$>& zXs>>O@YP<`fheoJ#$7o4wAZl^Chd)AP*&|td03MUyY>bp<8WzjgCT;`zCJvjJ(tn( zJjo~H_#bWuuMtMUO5B_PJ^Rv8aX1BP^8_&a(oy(|9c?}b=2gO|j__T#pb|MkxLtOF zehccBqa1T*_`0Bf8M#~~_#ef-Wd`Dp)1CrgexW3Kv?uLBqjpS}YIpQ_j8}VR;fI+V zehMCE&rR?g$uY?uKhgeE;C(Xsn8gF>0GgxYAgjgu-v{b~k1(&oMIE8vN!{*qz(7a{1AL~XnD~^3Y_}YA#eC!me}HL1LBquL z_)wIWeA)@cV$zY7`Q>F3eltu&weV}oRMPMm>6PHGR81&jc=HzL&GUx@C-~FkU+{6Y z?Htlfd*_Ka1mO50ZztGlUBT04XK>ZaiYOa!z))kWeMmbqQwzgnJf3e!rZ$EtQJ~UH zT?|vkgZS|#^fXMl4>GfEm|-eLeP27%aKltQk8f}^<7h(?#>NtVGEFliiK-IDlx#>k z04mS4&>+?zeJe37HB7_U=v!6)@cBgAhtDT}ZeVokIOhD+5Vzx%Y`?Ju3)2xNO5iPH zaZfq|L&d?ic5E5!Pd%~;{F$3)Fed+xA_2!Y=15F%A8m!|$hLKC3`V`9Q2)!w#W3?f zi+wFpW6K@wc0L_N3w5&P9{W4Gx?`x?&TM>lC<{8~250Ao>tLs2dw{ukF+8?MVLFbk z%$bMP{XOY8+&^bNzS@mvKaRHR!jjP~zr$XrA)Isj)A=293-&cU2?HfQ$7#tiX>fwL8Jkl@E)wkzVo z!RP(KY&XPJ65IvcgUy_{Sn&f!5GT**$0Z1U4gP~8tP8&20QYBZB)AWF0CQ8pY2d-k zEd+lA4`pr*z6dkQ$=e~~+F@|KI0yrnp}d}{!Kt)N4Nhk|Wpq~mOFzv~UVGOWO{s$R z9?#&YtiA655v_eF2@$7#KyTrwrlpIKJ3&iFGk4U|G6dDpGU`Lr*FK7d+EDu#2hm9T zlntV(_6arI(L(#|4e`%XaQ{6?w9-DuAft=tq&;-F;pMY2tv_^=7rGpq4gj}>BI6tjM0Y) z8mA}z3^7qptPL?mSMoq4=}JwA8M-R6WVWsfO4c61mI8Tw=q%~yR&1&HtZR6->rU3}KY z>7gg#)|_5?l4yF~`m`(%KKeA&5@!y5I%mbA4#aPOtmFF4n?Xl63)^S^a-vS5-!t|9e}2C*1y_qdWuHjlB*Tf9??^{ z^scrt+$(x2!gUZ(-9XRK0lDOklKN?({m9_t|3(S zqvsC+at)!nonC|p&}Fz_dNB$l*A1#i(hCfBF1c<{J%+?qOc#uK+(K=7ITRq*4XQVy zmuCUfq^1eIDg~G+p((xU3z#dRIlVdzkjny)HA^d5th>#T(S^u7l`t{Wr_qxX9Oa;YF;AAKkY zxG95OqYt87?@72rA4GXQl<sx%18x#kx-j5zQCOlO9dV^nvnP)S)hl67W6S0023OTPW15&AghFK^r1-!-kxLv1sV~ITC@M+0Qu%ewfyG`IwU%J#7IF39}QrQ^0dL zyx8J)`7wAN^G>i_om$AeUvL-bQ<#s!;=8gMj0FN=z{cJ7+(=gmKdj)#d+bG5(cwE+ zb9k|p=Bfr>$1E(UtB1fFIrwL+QDp^gV*D0M*Dw@0x607*gw(E$1Mgt=g!ORkGFM?m62IhHy_}{RcaQzDSI7e3sTVZY#0-s_Q zJ4`yWmTb-SOf0;U=1^^C}036pUWSd4pMOow>Ob%u@mJ z;#|I)Pr*DD5FZP^Wdc9o@Zy}lTiw9CN)VqQcn$a&>uU*q3w|jW&tDfm+;-uIw;Z5> zV00PI_sp&E1h>&Hof*tMF!yl#EBG^WU%aq!CldUPc>u1zGYn0In1`Zv-q{cKU><=S zy_*^A1xEdgA7}KVyBIfJ;w=gCh@aw3cSi$#IQ(?uY`S|AoRfJ0O5k2@aBj&}6AbG} z%&xkAmX}wp<4N}x01L3X8SSKd_rbri`ba6d9||li)%wTUXYqF~?PKlpxf>W==aYPv zpZ~*MLMp3e_RLQYFe1BrnX_Oo>w|`1-Z)e(tCb$i0$Ny|)nhz8xB<2^`(Q>*bn^et zvB}xXYJVABF~sNk5j`f@9*PGKX3l|qMGqH&!+vn)`}FVuSbkr?qgv56^vDUU!0Mt| zf2K!$z?GN-v5)r=nw6^xA3Rj8wJ$wJ$L8YWa;ly6r6=8iH97c26!Vj{;AYG>4KL}* zCvXdy&($6jrl;kAty%p9jrQqaa69H#h8Oho0GM|TRr`j;t7ovGTwS0?t*GvW^ZuVz z#Sh)(1=W2#FtSbu^9O9JqYHg@4cM2}1<~%F7XkC^WcA{hO?)mghi50N`}xxIt3W;i zr#g;De^D5y^3_h%$rmla!zH_`ha$BX3-QA!4iJgkdm+klEOQK^eMx&XvT$8)ex?FEJFjW-vFQob*bR#cbxL9!7d4$|6~^yLxkYy|2>n z!+Z|V0-La2Wq=oPdJ{cK%*nf!uzEK3T)mzEUe26~E>n)+tC)A;djM|=Lcf+TzvxYG z8Uxp}`Xavk@MaD;joE#feCW+>{IG??UyY`>7!h3CS${PX(c3oQUCh_9`iZ<01vbJIlAC?=%QRlncw2!-l25f|}|nxIbqLeTgqbYki66QQPZFOF?whm*Ux- zUG&u7AiC?RO(A;f%TRlqee`9`A^uqk?!PCAzWQ>sW#^yza@11iAbkb$*EvLAAs%9w zzEa$%s;_JdF zTz#DlV!pl(wwQB~zTN?`L|@+#Eopm7Y>$~zoJkocG0r;uD8(pvSxxO1lnDdps z2ldzaPT%7eg_o}HMe}uj()Xg(I=|@qL|%T^_kmnSeLu*RN#8Fhvwol`L{|L(3f7ff zKPWnhT>8Og5P9@NLYG%RBq+arSX@_7KP>u<-}EDbis(mLK@``ImWC*$AMNCZEUO!&L~#OkL-)K&E};SdS>8Dzezwtg0c=BlTkMcKO==;unprE@jZ&!Nt`n&{^X zK{QYMI^aU~<;F7cX+C8x_9VA2&q+_%WSD_+@U6|Wm}?Ac`hglUZ8V6Ng?)LheZo4! zTJ-dxJbyf4i(v)|&bN~cIWdo6=1fp)zV41;#ud!<4rN`CVP-mXy_n(*vnqnzeHp76 zW{m+3V5)AIbxA0h5)8AEWxm6hY8b?EhrZf>(Pq#{rkaL1o}e*IwG49-Kogj18|KUa zO=hZNnDYWO?MD*r4RePBXR#85`}SSP)WML92IiZ>ghfX*7vEH-PKM;IAfBwBi0MYu zLf^HlL%-Svl*V+=FdsGBmlsYZf>7Xo=XkY2Ad1s>9|yf}n12LxnCXFG0V=xhvHv1e zP2ZDD4-E^JfzC2LqQQefmzbU!78b$lTr-)T85YJtca`abVc`_eO>Yl3mUJ=Sw2%H5bDuH>_Du5ma&ds$#hYIe26@waVb-yoo1kI9et!&n zfCquS7!*GI489@9kc4X%`hafWe|hyCwq+7-dC`Yyi0uICV&_{IRfjs|TqZzLMh$PGAbko3=3wjda!QI^Wr>DEBT6VG%7qUq4@rcE~{sPMqeUl-072jZjymN_OhyL4ea9+u8Yz?<>hk^M8 zC!5{AdjJbc>KRzz{|@8kEnkRiGB2gu_c6d?qITZK!+bxB3nk^kN9yMG12)mSwD1q? zr2fF0_Abxa2c?L1`++#myArE|(jqkbaRV$1^g&rr*`Ki5-m*X+ln0U6Pnc+Lf4RMI zl-tk6K)c*t!iiAq=Tlseus5gz;>usDt!qen-vqZ`s;T9$eNfd%w_h88_2u^3IAite z6S$#pW79k`!8v+M6dcsTG@ncf;O3%lhd%*q+6`_cxpz*$`-f}vZY$iQKe@q0dUp`) z6G?6^pzQDm_46P%mc0$5ef?N??%bp{`|U)=z;7^=Gc z+6<`2P?t<_t=^3onvn^%&$}r@8#1i~v}EW^rk{W|3_ZwAy}kAf;p8?S;N*{^1VxkE z2T(Uw4J4`s=*cjG2zJZc!H`Jw3=qLE5g)<>;HPhB;c4Wq#$11fdE^e;?;Xdmgv=EI z0~ywkS&eJ{HcC(unJ+m(!&$YLQq%xw3`Z%&WWX4P^OWKVU;@KUN~wAYuQ~=jB=xtb zy{EC}HKkO&WF|v0rBc0Qj)JCcluGrIg$y2)O7#+6b!?j5NvTyg;nT`ZOH*pqO?cI@ zX$MNJ-rjn?sTX;ux5ulFO;3@BdV9R;*z^K+B>{UF9#9%JFb^<1qco=g zhZ){cT4g}gVdW6_rk^QoBdGDc^Z29x`2q=^xO zFfWGO?*iimmqd{nagw+EL5QGLUS#fW!hiqK?cRgMDF$tbA@ee1CVx*NXuFxra7x}I zrD{09LMiHjb>X9^Y6=8&-m(Z8bT*1ooB@s(^=0?8lv0^>nF0shhV@C=3Oq#~<#7U~ zoDQ6>(CHrZI+9YVHk27~&_@cPRO+qB3^?cq3~ef9*5#Y5O;hxzREvR&Ik%?i%#=zQ z^<{#&yeM^PpiF<8dci8E4hPHhw<-KBrB?oPoh0vt1(wumz{&i#X)kj!d3b|m{@e5v zp2Z^;yp5j?ova&q>;TLBx9K%Jn}>3syG4Hol})2ez&_zeXsR@`z=x!~7crFP4)Cbp zyC^56Ed)Lx{Mn1rBCPa2E%?(+X^#WXa%Rma3|sTx&>=nWuog=XNbwJ+xa1;xo)?E znCZHc9b&fY4*FJ=c}~||gdSBEy6)D7SnRq-5X)TmY!EA4_Xa|&a^1(6sj}8}KMG=l z>p>NW&8`QEwzwXu$KLLGsLI{tdQ<{puj>&;bd`Os#~A2U4!Rzq$$9 zQ?93&ZdEz!az1ShdBOE8C&XpfGo`%ddajf=UC&!V+;zQB6~6C!p>z*jFS9~CalLE~ z@!a)F(JR+0MQ>cMvq8Lfy;dndx!$N;zPR3~T)w&9suF&>-nKzEb#uK#WxUOr#J5!n=X%S1?2qZE#+QX4+u&Xf3oM zs!px7A*zP$w4rDq?~d9~Rr^lbFf7V=ch!b9fe6utXT&OlcQ0*tBVedDLRp_M*N^?z zl78&Jp6&|yT>VoR`IdcNAf-Qw8i0e><|QaQ z`iW0n)|8>_g8&T}Do}QKai1m(RVWAik`Mo+uep_SL;-LR5(U+#9ESky7#x&Sm5MEM zXa-ZxdFZV^-Iz4W`5wT*Yx4z^t0RDS$y=19T=xJRyteS8+`a&gS6g(U+=&2=S6hsv z+&2LnueKORd0<9-I9_crmGU68@)@qmX)&MjoCk2Y+G2&1^1?;=jAGR$$~yosmf;}f zMR)Y!;I*X}Mg`1(EetcMfa)gOe?vjlOE?g1HJb{mUUG~z3#p*$ zCA=5jYAF>`y@cb?R?Df7>LnNPht+OmX-_`eu&{C?+L=qd+{`G-jIrz^=jocwhl|Y& zv*RP@>6$Hvhs`_#EI)*4wmyL}-v`QUz1g-%$^xH&3;%9*^6i@cOMX5w`){@f9y?1f z<{iL$+1F4InRM*{@+}4%S^e{o0j>% zv-v`|B9to}7i0mTMM;#OOTF8^oIwjKGRR#LJU}>DaVS_0;E5!t zTQA za5blHRV7t4{k0CfK^PykpuawVlQ?8a`0o@kLmOQTCC%1G!ytOk)5iP-F<%?g9%7L;HYdbV zZLEqemTTiOL9EioHHBE~)W&Cl+@OsQg4nE0P|7XZgoY5?wTT!(-n+DksuA{T>N{87 z`?X26Ar5MPr+_%3sng%y$F#|cPHK}?1D(;PxI>)Rroc^lU(%+)1$kf9rrIHHXj9?e zyl-jK6y4RF(*{C5(5CxCJl3YiLp;-F_(8nXX2e0f(PqMIc)!F>S(}B? z=>1KbjX~=DLz|7E=KV{Xqec{Ib3!0eXmgdn^w8#Zgh;E+Q|=|bHc#2#Oxk=kShH&L zortP@vS|zOwtaGH3+h1R(H1HPkzZS=#zz5dA!_JTSX-owmzTCkS%Ko(;))QZw8d(O zm(!M%hNz$|35BSnEmh>LEph4Q)kPG>uPfZH3CF zp0=_)L_=+51Vm$Pm7=EFDy3_otwx9PX|1hRH@4N*sBAiDYm}Glq^V6upDx;3RYZuk z4nD)Dm$nW)!N;MkF8~pytyhf@u5HK*(NEjZ1|nM9m>aRAPmH#)12A6Oln-K1(hv9B z>0-zdg4K}Qu}+_zJfd37E>8tjjz?s`%{KR?f(ya2Z`^93=@S)v0hWE^R*Nv)3zY%? zhqpUrrVXdtnhL3QkstH6TAo0KHUMSf-D-K7^i)WVf0=lF`ZTiOd5G%82bE_ z+8y*FujVF`1*de>A*@!1@bF%faltIb9W$LFudCox%&k+Rv5OS}%kOzxXUsswx&u8$ zozXmliY*26rh|Ly%x<1k>^WF|70^0sQ7VpCWsxn1)>+MKs5m;EB{yz|CvTlAMP@3# z4j1xCe);Gd6@L$wFs5};_~H_1Y>R|3t&8WU5*VwNqWmX1o`bT>+FJ zqs`x@%~bXkxQis82cJ#l%15zez#qzS{v!o_R7|By$B>zdsdPL&Y@LOQ!@gN~ zL#1_A>ESs!S#B!67n-~bMW_TuqNM;sF)9I9V&M&y)}^RK62OZ!6{sXa0*kXGQ!tg( zL1h_UQptw^o*uUTMx}fJK1zeWSqfIb!qY$XlXPWRMCH_=@4>K%%Bd#l&9Ixwt0oC!I85bLlSDF{by9iNCech+se)>g7>4^) zp&fwdifvv}g;jvTtocY4egTFt{Gy6M0GEQcsi@)tfWeS~Dt-j;RIzO)s#F^=fi+pE z(lo%|fDre#*{RZV&{S3xrpn4T@LaKNDXKgcFqbuzsPbLF0tP>-QVFnxp(<4w0pNX( zwson>WdQGUv~5h@B>?NOWOOGfImg{B7pQ2{DV{Ix_H$Z(@EC2s4EmljTsOctl-me) z2ye&mE%zBLQ-C)6VI0baFz`zKYpp zg%sdeDerCqRj30@5PTX*6%v6mp=k3Ov%w0dz(eKkWSGK=nZP3?`LF&|Q5jB+(b+bY znJP{JCvx((>F}@>uYqMg(KbWsom5F#&T*p8lzJOgY7dtAMB6N>lc>@x@D$N!OTCdQ z-2zV+E(lXuxgeP56Ygz`VZKwj8+gv|^2;Ppx=nPl~UlP zl3xHCqe>Wfxo}N9UX^v=Rl@c0a;kg)uN4l$^u=3^T$y0B9bs-w-f9?aX1%lRXmeZg z-i`}f_(HoFb;D2acHwoVv*c41yi53;X$$$lq+0e0Uo!0>pM&57!dJkSRN%wH_Z8O# z9}|9v`z@ouClx#0+r5Ohu&5VyMiRUsPx8$OKF{1fmD>aIMT~E`B<$h#oqQ*OuL!3_ zh~%sK`gLy6_KsR)%>}$A80SUSw!piRdNO>xbtYJjHapu-!;IIeUYLYT?Pnka@b(l<8C(ld1R?PctlvY4(=X=S`<( z;{sj_C)k$Xbm|89@b3iMQ$ICrJckQtXHI}GzjQ|MRQ%-c1eeU4O{eSQZ|5}qec`HU zo=JUSqg<=s7p|L~NHPxhocn#{mU)NiG|X}F3%@gcXEuF-wJ(18cY+TTYdVAORdWCD z1fRW3XM&O7+Hd{}#_%YP_*KVgv~GP^A{ovnu} z4}T}gi{Iu&CgH-9-xms)51G!w0hQ1EJJa$Wrn6hYxqowIFvd{v=f7{Tnl_ux;hhwJ zB^Tz_GEFyK=!y$(6f%XkE`dYlT*#P; zrYop;i7KuS#qK42D0V-4MzW0g<2r=TFl}=SggYa(Bn)C7T}x6Eh(v9R2gDd{OAUx| z8s3)4XQH+>5aMrb8}jyH4W^K2s%vNoWdZZoN9`%5`w7sbz_Bpk^)gTXO zdlMiIY5S@`9M$$kK%CI_mxMT_?e7k8Mmz8q#Ch#NJBUl#LBu9LSG0qTA+BqORJpgb zL#h$(YU*>nKKHf5eh?3}!vi3mXh$kTJkyTA8~D7`j;gHRXwIWOAm3~1V5!eX?N~>M zFWT{(5Z|@qaC|;Lv=bR%eoWelIuP#KN#$BoYA5k3EUC4V@epY>^)Xya2JKWVL?-QY zMTo50X*`4_hjyk6L@wR=rigwWfVbLxromIP}vaxBG6$NUSmEEnTUBRqK$R~4Qy$r-9RH)I%+pnHl4Mbogunuw+cXX z*VHbhrI)65DJ`Lz`aYZ`T)UGEqOW$R2}G23*ArrZc2_l5oOTbr(-N=UQ_VFHk!Y^B*bbBA1$$eAl4Vs|?;`bpzaF2qkMXz#X3Xw4Y4YMWFkvnoiba0MGK; z&mzCNfS0V9Lw+*=Zy4s0-%G#;hK1xG0Qk(Xg#5bE7ze&KhEh~*3m}leLe*hHd}}h;s5)X+-#QGzRDCm`0YeWb)kqEEy}1s( zs0Lys-)5`=AWre+y}1q!s__%RdvhK7P)!UCU*4NjFdxvF?+K@x9{}AM##60YfL;nZ zUZ+}cu)d)TU#Qk=fZC@EaS!fHwX1@nlqz^2)t(C&z%ZR^zXR~(D|j~5fi?6U%$fyM zXEJ~{>Vg4J0K8Ebyp-x%0i!tCN~()k(|4>ZdB**ustnXyM9qDB?lYU>SY=fcSV+hhpfx;otzdyCk;Csv%zTttQFK zds8*l@-;+lHE*Wss^w*kt3zwdtf~(M%NkdQ9zIn4AW$~aI`qKOW{p%}-bi!r&?}5; z)B`q`+?>74N2o?3F0_;j4!p=3n1=YuMp_34TE1omaC_1B!OCmRrr_Y;j5w>NYH3;I z>JW~3QcX4JLqs2*Y7^DU0hU>Hhw<)DsTSOeugt2Q9j|*7q*|)0hw%*^KVxE4>mFEk z)`Ih5E2wr6a6fL2;Qn=~b`(%%#lcfBZK|CFmScRuGm28}FTg>ZTktGQgX{Q%hX~^b zw^T>jnqk7u1?bpy_Tj=vxv&uLvaUPW5MGMn>#E^9MtCL4tE+=$V=Z_!=81JrfG3I` zf0TmisrHg@Wd&~vr+TWnWM3_K3(BuI2`uyA;FQ~uf4xhSLm%Q}h#Mc^-@~M%8y{7z zPu8eEcu3zVJe-rLo_gcFpB}uO>RkZLX3cJ@?**90aDeK^02VSFrTWJJyq_L?k{aX% ztYpnuYJk|ucMZcuYOn|JhjN_%NI@I8)T`7mGfGWjxJeDW0k$zbqK3*!?_>`d{G1vo zE4`0Z$<#<$>4OYEsgbhMM-_A`N{y72KEYt8#>z^cVW>ro=L0SH_t2_W+`RtaCTJ-^x zQJQWJYPB0so}r(UT4w@PVLC^xdjfnJzEkUM0DlD`X{n7TfW2=>F>2ElP@OfEsLduo zZ3a8Fg;lcFV`xBaVG^v37&=kg6#!la4T+|Z5737pnL3OHL?k7y+iJ z;4>(%0p6eQS}E^5%4@I;yk7V+oIryY;7wBg^*+?FEKnA+f^R|HPHB$aqwhM!Q(jsc4{PA(lpi@!o+VGG& zCG3w38V5pumh020MmcJ%ES9Wgb!v#kpT-BkvX<4UF|uot0xZ8@>eSRMgYkyA=T6OG zRGVnf-DQczLwL(xaNWuPGRPhUA^+bJr6g3zaY<(O@!{vH&h z8cUY4I`zkXn)=F}?_0TBE&VlBt@T0v1&3^8M~41+&`|dSjKCQ3Sw3>QG0~vvrN_=7lN3ksbA__W@t~8ZnpL$ z7-F9GG(W^b?P&*y#o9BNJj*ie861dZrS?1*#2W2+YlwB)iyROewHK`*{#6Oi|4tD} z+DrI8%Qo#L?6_s8rjFBEc5AO1LG06Br-L}Cy{->&M0?`_aa4O#3*v-Rd+P>yT6>Gv zWjUw49SCtjdxwE*xvag5hPbA^uK;mFdk?E_xvhObH@Do=)MvUZ54DdaAf9R;Lm*yg zp9({~(mr*9c&n+O=vm%t$#@x-PugeIKwmWVnJ&wB?MoJjpPKVa5IVh^_BB0(S^L@m zB9*4T)8(5+`-Up|rqk4^THlP?_o@(Cv>(bMX48IHA#!LxVj*&CKPy4x(|(3S6wrR5 zzP^REU;QAw^ubD3LLb~8qLepuGEG!WtXXuLJwNPUba zM6^BzZ`3zNAFC)qAFGd zUn?nYJc@6Jw_%mv@rBGqachAcM7KkWW@`T(EEABB$0)7?8qpdi>Q83sFa#*mkC2}zu7k37{W$yXV_?8KDtjmM zkM5HcD}xs+`VE*#cAN)J5LUZ}9Up*)h%f7oAKy}NFXR+)cvzl6pP zz6zF~0(HNI$LXY;$0*6~32yJy5jyaAz&OLBpI^W+TqvGY@S;9H-yq#Zy=gR#Byr>ICk9EFaX)|@fxUnvl)c9kL)CHc= zx?DH_xphefOWe|%f~T#Jo2EfFMtxZ^t|Fu-7t);TRFF$ zS8yn)8-~7hhwwGGJk)I~cn@>0G?aGK^>jPKU>#9nA94v>Fdfma1#~v`x zR&4Spz3NFlRD60<_?26I>M8O{wc-0M8|Ryr6E`0h^Vk=N0M(&u87vaE-d{2JBMM z%acMf0eCIBS0M`N3UD4`)iw&*1md}5ul>|L72qUmPEdDPf9pAhi`0DufY*3?-J`29CEnf{sHb|=M@rMXB=wx@1U+T)rk?6qdBL~0 zoqDNfeao6)>UABE%+Qm1mjQfbh@#%Z0Y4d5QSVD8lOHi`B1drm4%ncet>lOUq-NMn zjuQY+hW$_D{H(c7eK4`~;|1T| zcPR{R(61XUzW|1`6oIkgSB)VPMPRJ>@q(`-8}-Fl@vFm{Jk)m_pn)qn{#jCT{B!(0 z80*GA29~}H^vV8ksEhT<@eoV(DSi+u^eJdK-_`n5E5tf|YAnP?eVPSgvpy{bVyiyg z2V#dly+6b*eTFy09(@MBOR|jAJWeLvzP7%lSS*kK8^;s%HI<2d-wZ7-{ z*{U)Z^*O3)SM)hbcTJzGbT{?6eIf2R^?5M)zW4QcDkgiR&o2S-M4yj1(f7H&pa8^6 zeL-7@H~PZt5byPccxk?$^hGM}`K&L35%vA1FIK_dPkpg!%3u0oJhqkeB`C?7LSKTc zt*P~;Wg*h(OBH3*mnq7uJC~_3n^j+~Fo(XpA4DE~1?ptYudB1Q)`I%ViV#Kgm1t*c zF@2RUL@s0vYE--xHMHqql`Lx^^IQU-{QdJ-Z+ zYiE5+W{7V3mgW#W^sQ(oYcG9kYlu*NTW*MOeOm{JNPYWX5YhVf&JY9i9cuH=8mI42 zI8axAmCQO=SAUhvI#gGGmCQOq--RJ()%4wkAq;&tUW|3LzNZ+(IDJnqh>7}M6{7vE z?+t~Rs^hPcnXEH(wf}0Jt?yTjH&5Rm1+hRsprWTG`T^CH%k+a593r-^)DNl_TB9FA zU$?HqkI|l|>q*upQ#?z*7~r(tV-MH6AUs^pU?h0R97hMzq1$hdIkI<>Yi}Oy;ZOIE*y#Ez0v!vza%-% z#Pr?@i{K_|{Ap+E{S9mu-jt#)Ih6TGCA^i!l4A(inMN+`q%q_;fD7q__tIzzMgQ=V znSAeqG@L>a*!#&YM(?AFM}uW1-}|KE(_q=f=zSL42TsXPb}@QiM1FnHef(r*-}@>m z)MpBgXZXp~zV}VkxX(2tC@c?jzbJ*}19}NQ4yCY8z!HMbqbN+7)6!Dg*Jz!v$KZ0p z@7;1ycuBC#?0YAp^l%JBKiS3T{liS*Yk|J=El#?xj8(L zdqf;qX7-MB$UR~wSZ4N)OvoL^#^hI=(xuERHq`XN^G zlNp7>hRpjd0?UlTQ5}nI{holkOXiK8=m=4&rFuzr=yp*UW`2D{-v?*gqSk;Tgk!M) z6ZIA>QwYZhoMnrK74(xSgku!egQ8)E{bUN^7)^&MdNWu~_BqBipyYrpe= z5Y{xND2!IWo(!ES3PZ=w!O)MQjspHrj`JTWs4ti5r06^-l{ZKoqbOQ+&H=0$P0_mn zh?a4{-7${(t5L+^vST9khw1bi#_1+g|4jf6mmO1SfEug@Yi7`ZHUJKn9W!YFB0E10 zmmPCxzz@LRoNOM&GzLs%SV%Fjc7C%M7Ez2E4bHht*fy#OTEMWJVlis{mN2ZM*rx!F zVjb%!4v~c)N3o9e6gL{Mo|A2+xZ8kD3|lF_0$?k{c8VVcz>WwWbr;271ngnhO$o)| zJN!7NbsVIGSilih9iarwT>OqR9HW8CN}OUiNdve+$&Xqb%W?iRb^o3ba(8))5Xung51@7w7zo& z_=RGWf7|T=Id9^^Ye{eie*$LkUtsxmh~s`m8r&K9LDUaT(`m4Z1wIQuLUji}0)G>J zg5w`Uii3X&KSh0p^hVE@Z-+QuRH7k>yZj|maynj_X3`KuUj8X1{cGemv>aF>CC6J- zZ)gNKo#@}8dPCQPGfH!O@}i+1fLTQS8HWgld4nZTa(qE^4~qdypyc>w9!tZvfo0&i zzRr1-^mWebj4|YQ=#LXR>lXcRJOaOM`VmYo@&^7sL-;oj~+6=@-jDxa$}3*nTPXOK1c?5B(C}mS0-^a(RgK`ej9#^ec+8>Q@wH z*RLwdrC(K)SHGs{Fa4ULLi%+@MfB^6is{Z93QOuY6qeC%Dk`tvR8&d7rN~>qrO2Y+ zR^+GOR%FxfC<@f?D5|dCRa8sAtEjGiPf>mSo}xzjeMLd~eMQan2Z~zi4-~c0A37Db z*B>ej)*q=S?5sah&)ZFZtf+_nSW$2NiK0;biK1}*siH{zsiJ88nW7l|nFAtDe_jk? zp#EHqsv-J|JP^ZmwP)lvQh$jT3=lKxuR-pTqK zHOi;yZ)!ly(BCQtGFyME{K7o_?I4JS`a5)4zs34H6-_VG-&-J7>hGf=*61H9L#)$3 zC}+A+|5yPcN&l$oyG{Ru4(zu>|I`N)I=|g|va(TniDo{#4%iu?-PXPu1F(2fmI}Zp410uBpTG;B3uLu?IXv5{Um#<7md6M3=nmH zm<(55u*^R~2f%{4I)dewf}ydM$Tb&OTP1h8hsMEzx$faYJ-I&~7ECJ)ZYZ1p3#N4g zHxV8P3#KgwHUZgz%wiNYXY#Hk%!EJWfCWGZvRiA=p8lztJG_HQ1a#?9C2B7~8N$rcL9oqpsTezm; zIpBH1L5lB!7YcW9;=(xfvX{z*UW!A&61?<@R;+@>RiYoF_$hd;@M!S(Qs523(-gy} z_)GK>W;UOs@#<~MmU~z+GmTd-TY{Oe5=r0lyvHuCA4fUgu>bdN!89-`p)1Q|`Lu1G_0#J)JapbxTsLK#fS_!}(%5nZ9 z1vTJO2a*Pd>>tE1h_sV{<_v>LhqLm>v=wu?&>^Jv1@XK!bQtOT0i8JAa5Az1x-w{F zAe{5>&X7pPRsf&!2^~X;X#ruZSx$+nUqmu&qr^3UXoh1n3eMc0H$_6v(5Pkr=Rj7S zrBRDOgBh;SsARw}hA+~~M=I!(ibl_XhNqK#JZbbxz!=tKqA>x0@eH|X%y@yKof%jJe>@yL=)EmPIEFJn&b}P>10@Un$!Ytku^POk{UwS7<$vB zF94oShK17K^#ONS6Hb560z3eOxQ9j3->*SWSQSN+s{)=g^ry)a0k0WiXtEj}?-=4} ziW(lD7!qiT8XjL62Gf-5fFBG)X=)jh$!2mTcYTY0?~;vZ-gZN z_e3A&c8~rZ1?D-2(>*K#hra$+R{F6dP^YRUX9PbJj&e&&lbeEH3ipSPn~VzjzY&hX zlyCAS@O$An%!sETNcI0HoPbHlly>0H@;rn3(iHXTzbQodgZt2w+u)y~9$JZ}qC?wc z3KBNlgQj)`%I6EiMtIRwj8a=F$$KOoe(GbeOhCdkszuXa=4|OiZ{XplIlvi(N8v=; zv{hhPstp_C#PoO?x}Hs@AYtS1@Y5@RWeO5D0in`#3@KY4$zT%Vh3V)gHi?eH7UKt~ z)6r3E1*GT5+`h*xm}VRYORyBS7jycVh<^LCmEJ7SYyDeMW7p9jh*tg!cHng!!%^Od`QP#T)m8Z2wI;i+ z+3*-teU57C7Lt4rf`K`3hBjH74aY|n=DY>A7xhRKHn(z`BscB7#|iSk{Kxs6-%3%{7&m%b3>ffzrx)3UC_T}g1D@IYXEUg|Bg=YcSHYP9pbkBeGtSwU7bPl zd!YY_hIp+1ED!Na|A}h)z0`jdhxk_|IR86EywQKbY5Bc3275t#G6si0BpXB2lYKRY zbbeAUuuXc(?xPjS<-)G8rRO;FQ%EiAM0x zVW__q=%33_e=E>Gui>f<@t2{cfGA{WXcGUzhVBO8W$1Q@5{9k}SZTvR1NoOV)Yp*w zD;SBs5S5L@0T4chb5s>bUt<(n&)?q|T?r!47##^w-57(m^si}*35Td-jD?N$uWyV+ z%lbDm#-U&MH#WxMvHhDG-g^ z4}-l)FXL~gdbCjEZ}n*5#$;vG`WlmKLPQx;%n$>NDeBSUjHv_>Z%kFO>>y*R@~A@$ z^{pcR;l?!epe|#&AB1j9kAoOxs81F7k2Pi}-2`K%4Pufpb0EYNW0uNgx-lDnWY^?B z%b2Zjt}(|1G2fV@Xt6O@(K2JMqLs!xMXQZ@s=#%|ym*L>#(dSXNydCt;5K7{DsYFf zK(*{HW1(uwy~aY-cn1vimjwI|8H-eN9W@rK20CFZ?u3(H{-=y3`GIGRC2b)t7)!HH z9{vFbOIm-Z+K+5E{}4&qQnTCBXd3ddskKt~FoUMy71}Z|jG<|V0leWKHjbvF&279? z8a9EZs{x#wlTD)OI|2C^mePz20N(HqTTe5TmH0zB&VQsJK3Nd9hi2?Tsl~ZIdub*- zl&v(wHJXXeWW)J&M3P~5Y34={htOefXcp!xHa=Mp_Ct)LpSmd=mU1~X972cZq*>nq zHCWSyW;X)VX6Qk)@v3e07{X}wJ3wQGD4J6PzjLuk$uP#Xq~=At9m zcoQakG|h$UwRL9A-!%6Ypc}&yQk(I%9t^u^-cUd&!(p0t0T9mch~~qH*f_Ece?{|S z0R36>p5`Azhqc8reWV3>0SOFWXh9fY2*X!eum><)K}1Sgm>J+=$Vv+_L~V%-UbIlT zxv>m3T75vyrg zMG%L!5o>ALZ~#ZP5$kE$CBR|M?ldj03^=Z!Z+%)m5^$QKB`v=KIL{D6D=@paU1rc} zMFQZOEBW+?q~y~dvlJo!-BUb2e2jM5WIi800Zwr4RwR=7eE3|mmF9i{w-cYa)J*eW z2y8OF58s6&AV51@*yBIdd5P#VtK8Q13;S z3&Ov-<)j6t!6W&Gh?Iy3xVy=j$B2yhIN`$P(94;}2rmSh3#WkPH&_wAD*aWkoOz6} zrTR{b@_;8ve$}C0)B!xj$rX%fP>~kFOxt9uBciF&-vi55M?@FwMW{b)Ws|Lrh~5=x zad)7cd5nlM)8eJTMUol5WVl#$KUph?7>HAV>U(iESu2RZU-+OUeZZ?w8Q7hOk)}1I z{%NryOh7W4=w!+ z+$HMO=7+S*7ra+^EdteLsynKk4xHbQSf7rTDT94T^y}S2XxT6DQRcp7Q}(3gaHO`A z!VM9)E*}d%E8IfyS+IPmt#6bUt;hnDSx4U?sQro-;H#2+3_AJ>^{#FxcDnbSftR^L zy{y|@{l3f0v{Jn*nRWEtjQdx11oN!JJu=WO9j!8hpK$WXrXjRSnF09{TjbPiv`PcY zK1SqJ^K)8t44llRM^3}n!d9mPe-(CpP5y{~pOU^NCug*f{m7sE;$1a}BTLcBi@1@u zF(U(MRWZP8)-hW2Moy(w#{nN%GmTc~1AJkaNvp#FJh_aVORM+dk7m0mO~19I zzVB!ILpjcWq9D7OOWjOs2BHcc3Zgw}Eoy2{$B=>6qNa8@di2le%(PZjl|6lQc3Ov* zYiCa%or~6m0RG~e^3u8_Kp}<#v>pTA&JH*l&<;?7HHB#XDnJ>ABD5Yo&CWj{i}s=o zK>$A47hRDyEC4v!0Y^8W4Ie@5fTLT}#=d|6&Y=@++z+V15JH=>0ctaZ(k6sO_WBG_ zwCO&e5kmrPt^!~O96gjay8u{C!lRC&&FG2tb_|m!sf801%ru*lmH_zRL-b-w`U2?5 zn&q?w#?0P_VGV7W1&Cl+Pg`CC`Y~*#tyKZ+bE9|D)`@^P)*Pa(4*`Q1PS7?V0K44i z^G@2PgV^UrU#D%?0qk@8r=#sEyHV<<{`qLT%8nP1`rBx`%I}CfvpOXrK5@?U=KL?YND}ByVne6yYUC+<( ziQi>#&NZ*4)#orUelcU1gwPsTQhN#>`u#SWX$`^?)4!SCE>r$~_+v=4W)rgW5KRQQ>rL?XFI3L$9 zI*$jf8v!gRn6EsoI}9u$h=cPyMr7+Gx&WNwdex6gicgL%1gE$jPSLJ@XXWM|U8Di6 zKLeBresqx(DQE+xId(aX743zy?;GF~?Q$9`x-^CpB1^kP>aLc!H>kdCmE7?C?Ts*- zcAKc%VncN!?54e%Y_>#qf}z|9!)dQ63A&+iHhF^U2s_}#HZ=yzd93J2FWRI!gG~9O z)l$!=}=TQ&ZmI63f)TgmlwyidJ znDF@&i)AE_l;qcaXxj;>C7_AEjpo>%9<261U~~It!SinKh6`gjeg8sc+OEuxYW7sEi_Wff}yB!wrG~splSlNy#;2CoJb}!nYUcSUH{f~Ol&OAVgU;1CENISa# z-TCGLNnahGGZrD+<3Fyf_+K@aWx$N`hOw*x#4Tev488wdV>x=0|9xWxT(kcpV?}j{ zr^ZT*PX8CiN;||WV`V(VTVoX}>i@w|A9V6hHda@H_+qSvN%#M5tSJxiuS#(KcZ&FB zs88wI$XHtv!fdSV0g=*JR|LYtSl1cC(^&r(M0#U=2Z&6D`joCMtJ6^5(zWF{ zav2*{7@F7Egpp|b%h=QiqL8sU4MY)Rvx-ZK8A)goTL~knDnuzGNrhfzjV*o<6%5rU zZB>k|=&Cl0u@ybsW;M2zfv_3dRGk8i?S&w!8{0cU)HIws3PaX0cHo8C>Ki*1H8OUh ztJs2!U8?%cja`ab8M})?v^943gy>-GQ4iY5*b@rT)!3_w=x(UPzqVe+K1B{=pQ1j- zelLg!V?Ru@t)Fo~l@M(lP!wYvbSh=Mq5hnwZIE#YF@tTeai}H4FynA$h!MtN)d-q# z1ii#&7)P2wj5dy{1{!A^tp_pDIF=gXZ{t`Eh^fYLGsFzzcpZq@#)-5L^NbS>AQl)W zRW6H-lT9I(8K<(sA=_3Or&>U)F;1&@u+BK$8e*eyCO1Tqai$%_Hsh?KoyJ*}a<_3# zRc)VfPF3xoabCTjBgXl<5XX!Qs`wMeg=!FIjEjoS85h;NyJ%ce6}@6yQq{h0T=v9H zx$Ty5S!Hv_xRMd#f$PV_&-nKx>Bq#c=~`1j9egnIYn;ym9+}Q71snK3XORF+)?WqdiNYm&rlQm{eh4@5jrxKZY6;o7kGrt6=P@=+(~{_p3*hUwOy2@S+14 zTlUwY?f~_HZs2#qAt?NSdeo1?eUx53s!Szf)t>Lc(qP_>aE~47MF+!xvK!%y9SvS< zG6l$XMC>Fp9ef8gOWkL9(V;59l#=;8{0Q;T0I-LsmsO)f+kg_9#IA9()1mL+43c~k zegt?JMl~R_@J`h2FuYShHsSqdClc((g&cC>7%F=N{wW~0@LAI`I)d>QAfZa^6*C<% zfD)?2-mXSRP6CTa>W8S^Q4GWYFX5N&jp!&wWPltTk4-ky(MiD4qW*>2IFDY&h4OqM zE)_~YhVd9sNjL)%97CTBs3M$0>F0ng{Pb}J%yjG?&`;FGpgvv{Y!@y|v2?r#xT+-g z_Mqd-fD)?2+29P1C;fW;HSGxsnDd7To&5wypVMit>Mds&=F#3&fb$AsbI`uL0A9R_ zEkOI!YrV;ulC*CRfOjHeE7N|M75jbG*lEA&JUl&&twZ}UA^%f3&VQt!XIzCKIskKJ zf5p&_4zvaEY1-HjIzm=vV~6n;|b9 z+XwJvC`!kb{|NABDoe+^19%!4XQAU;0M%I&NGISR0%|kVqZ6v9@;Yu@Gdi&vz|+XM z_Jko~3TVp7LP)u&fEEm4baEk}HA4)Y`~=`RW!w-aol?V&7j@%wIyD{8mD5e2Q!fB~ zLLqKComRuU7i$)h`q*dyFY3mvq|<6>^kvN^I#U@C&9IBkj0D6m9HKK<0PzfG=xj-V z6AdtT#S zdWihSJ$Pt)0pq?IqOfs45W>s2kJ{Ud8xO1yrHlu#n)b5BLxhp`3dTc3Q1(j3BTOA^ z&yB0~AYK{QQbD{iuGN5eZ(LI$g!7YeT}5D@jq5QG-;5jRj<%o1jWFDAGH#-|>}1^R z3X#&d^%q2H<5nk#w8m{Tkv+X}ThagcA)NoePhs~q9)+SLU*mBZ2!G>o9|*hgqy$7& zJy75%$Y8mPrqP?#13{P)wU_4W8(b#ydS|-SNuBf^30#3)?(r~`$581|e=>yT; zc!@@^cQ#%rWmn^s(segpD_t++bu5I#c;gQdX1o~$(bss3VAdXGymN;bV7x=i+GCCP zDIpS!_vqkur||(}$UfBg&;VkD@lm}{&G@KnjbVIpW`-PXd};JOdO)x;&kFr)uiVV zhpOhjkT|Ru#HGYx9U-nI4p-gdM&fXU#P-{XBUJQsFL4CoW&6X#k;NgNB#!J2@jTI0 z8scRlJ|&UKWPg*Wl?T30)FL51CH=VkJ>3Daufi_|e)M++)E8gZ0M6`01jdw1tm9Pp zcw#NMhUnYE7oKcFzA-eyyaq2t)=a)sSy19OLd! zC%=Q`7-QUExb9PE$pHE3RGbUdJEZ~@+4hJV<7TB(Do&AYkGM%@A3BXV#Tg(Q9dR@9 zyrjV1Ojwjoi*m z0>=rf&orI61eRHR+$k?Qn+rHt_66cDf)OJG$i4)AZ}5T6&IXT=((Xfj2dGJI&yd?W zy#9bhQNQz|bIQfY96s&~a4B#cr;axvvvbeE6FGH!Dla;ZVI06ydZ&AQdgOLqy?Hsq z7@yrt=hp*ePa-}qLcsGMz_aD{B7t-P12#a8Fvgd}-4`%&0~ShY6%=m=FA=uj#a;Lb zmR*VXK(x$74C;WDtarw%FEd;mh6`)thDNCF#Y15EGDCa|GpWf@z$Q_*$BVdBAG}4l zD_;Di(ctaEy;0t!(_oo*#7CM=(B;fvnRmqFrA5F>z2?AA^bA8NaH7%-JWJ1dq8{_vpO&W! zYOEszxLsem=p@iGrfPH%W-nkBLp{2<7{JT2@y+StX8x%RItCnzv}hG$l{SZAQDzYeVL=0%S$VVLDf zgHNqv&}D64hEEQ_+~1vmaN(waTQc^K!Bkztzzeu5d=|FkT3_&E;Vb4yy0!r<3j^_Y zU>mP}0?Wcc{1Xql?hC}ifckSx@vor24tpISD+BQ#U_7qx1b-A`@!gAVsN&_AVuCxc z8c?_mO-5-RC~f{gHn2R#jJ3e_K^|)pQ@x0&rQpgvnYT z#gEcTv_Jt8n#dFGQwM4RL{h!EL~DV$-5y^RwFd%QIhvYq^vFim#Y|_Com>f zv+YF7{HGy}nmfQ-OFjeNII4LW{7!H@i-s95(Q4KpAy!_1H(skb1KL*7tQ748CZoDs z*6@ff+6l~(boruOPj(;e#QUIf+oEUo1ntB{&|@?Wxl>z-Cl6{Gjq=ucWWovBjrX$&w*g= zr?C;>R0a6U zk~gZYeA;Oh31h8<%y4|v~1#z6t2cIDGJD|eTm<{Q2Hk6s@)XqeJPO_w( ziO+?fc^Y)8ERAPR+L?vm|INkNry#7%Aut@RKBlPM&Hc1LMyJ@;r11Eu9V+O1!((J)C&soO=ZE#yR&$ zDn>+ekEUhpFR#$MnEAQK)4PuWCeZR`fOJ~^1Yi=a;LtveRtyBpq!ReM+e0NhW$<}v zg1i)fiI}S7O>y-Zx z;3n~oMz==9{S6}G{x+ckH=r36LL2TdD(nS_q&Xpg7CxGTnD36Jx!mCx;up8M+t9o) zz}+;jKfpzALl5qI=xxZ~9Z&OH0y@zAA%KpwAO_Hh77PO<(?afVXIl6opbIT>0lLwm zQGkc3=pKMhMYy?e_auLNKp*n?dE)XoExr%%|DX!rUv%+*q6+s?daom{Wf}248TSfW z*9owS)@1uCKXz((4T!X~ttHcSO15ier*vfX$CN8pz-q!&Y zw3&7IDQ$)$x%W^h+uU9%Wt;nowjdI_57HI{VE4DQH5BkK+WI7*iatP)b$?GEJOc>O zHdf&Y!jfy0;;y6Ztw2xF_Gbb0bN}e1suh29(o`%6wB5QqG*45f_;mS<+Zz0Jd~MT$A)dk$4#(6z(bkYMGc4PxW>w_j%0n(w5?JG>v7qiN69Kjkh-`Ue(RNf z%5O2LF9x3_`2>7fwLSnoTk?s)u^L`(=~MK#WWqGWnTD58piuHRP+`ML@VPQh98A+1 zeg>T{vkwN}t2L&8FOvK)UZ&azY3qw6zl`=9%fXAS)4fio*7zIfyH>V02{HF9^x)G= zWPZv3?JN|c%R3Wq3f=&Ewh~+}n0Qn1@xyZ(_(pkt7o2~tH@I9t@%F|q-_GTNZ;{mv zLiRpTxm(IR7{lfHrr>4r^dU{EwewJ??%N>~p2L@^&Jz-LNxmIX`vT;v@0R={yfp3t zhsn<+--V!hVG;N~+1|l)?ZOezZ<<1Ts*)9y&>HMd-D@#k6!@aKfxz*r8y}YNFL2@HHaE6=w5T^OHpF$ZJw;G3~LA82yV6iI6h$mVa1-e3%rOJ&;7}?`B+E~?iKr^&BtLfF$Me= zn~%rNKBzkOvONoXJxN3RvA@~buRE_ex`SW0d74(_@PcClhipGX4RQPf?P>P;{^SP{ z16|nAVNL2vng(Gi;c8*i^$9^E6h*D9*DpYg*;CJ1(x#e=#Vojayi#;Q6&N^ChJ1a@5ic#Ga zA;~cH60YE)B;&LJ=#b(X*VN%ak7|`|!@=&5>|^D_>FCdXp3iy@YZno$^)D=PP`mIQ z$Y&P$Si2xVtj#e#FN_a^rv8zY^{#gQD9DEv86xd=t3?KB=dL2hy4~~k*3PX3*h0Cs|0ft`Rg6D z?!(O^-$GmW#<$!x-*P{F>zy{%9rzrVvKNZ^U+6CuJN3AN@mdMuQ0%Xgy{`QNa!ryn z2O>@^7Dc#Ry2B48V^M@jhT|fLQ^n6n}u0LzzIIgwc)g_0CUbq0aLxrWQp zJwW^x5o+|OAYWQ`FPf{L!PH%cWLAsYp_VR{lC+6|mAG*0j~YaL%oq%8N>9bFI4(Kr z9mgEs%#AG8R2&>?MP#v4y*5IN`=cWCx>Iec`feEzJ*ei^@v<#u$<}z;RqGH2uW?}# zZ1L`DWH>^wD;*qR!|d%;9i`xt?EJs0Ey6K;x-bp4%0H#73_lHyX|TmJwGI&m#;f~+ z>;#{Z7Xj6}-jrd>AVv0D|oieXKBwxvZyfHTJ`0qp^-V@1@`Gn6Qbfl z=i1q&N=eidaE!KA@vDx@(Qrf2~!#V?xYA&|15?2eE(mnJZU{p^Hs3F^r{iW0ZLK4UUE(69n6j{Y+txL$3j!AIZ*+X++ElWHTZFV+5Jo|vIV zspq#N;c2^59(C=#@PpLn?M{^>bb^Ja@<7Sgur|2Zm#Cu6<8~i5B`DWkQ>F0jp6tZJ1=u+=Wgg6k6@*N%AinI(oDDT(XRK&?%8 zB_^H)t+P9@%jw&fbUzYK+X=hXR!JG)jduCUw4`mI_CP8sgPSK^0=EZFu}7^*eh}Oq zI7NeUC>bMRoIP-g3kk^>$m6crwOw|ms1S!TP;IZM?bSrJtNKYqkor4mdX+!pczi~M zryxi*RGa*0?@<-*EA&;wFZ|GbE9l?2EPWaA zC;aqx=wDp{OX#~d0Y&s3{6=3$-vtBSrhj5itniQwcAc~{84%*5o$wXC8GYOZ5Jn%r0f?Yo z@HhQ7+J$hgx1vwD7csOOkyUR)yL$odq6%1(?xKo^0rybFOh5uv^0joJ%E^F^^bc69 zen0W1C_RxrMQ8M6`gANHl{mub50URP^jq&vd+r20OnX?CI(;4uc$7YW2GEDTVEy!^ zFIYeQXfNyFN!rUL2hu)1?-|<1>{;5+Iv7U#hXIDumz+0(_)~QHDEi6;7(-u;0=!BG z+5ujp1CMcU(&%gM+eG?$JYWia!wQ;C-@v-{nRF0S9^FF+`I>xmC>3DRq3M7eI@}$Q zONYHh*B9@r*f%do?fHt9aHS&jYLNPv=5w8r0eyrr>tjVp9xT~pwc%|zd)|Ms<8ahL zl1)_`p@`I>lFd-h!>Ch-%W&@Ks!?o8Yb;*exl;(63~Qj*{9qb4T7v{Rsu{skricklOS>*-L5P4AqR}@GP?U;YMmbLPxNL)`6Jt=NB9T4l@aczN$FAkA7`ew_RkY(A^wfq(!<4f30JL34RN#fjDP8P}CB~ z%~hCr81Gm84aaAzVADps>=8I_%0}cdHfF;Sjb(KZx3TujZMcCo*46a`dG`jg^>fgW zVywB5hGTIZLX9!B4r(`s*TKb&foAF50L3J@gR#gYFJNAvD~_vfOv3T;Kmv{}%q{4M z{(A{`Oqc~t08PvSL=Iz?xs;iwO2_fdsz!GoJXX~W#~J*T)|ehZurU?{;8;fV zQ9fY`?;0>B2OudUF#tz5rUqCRX2uKE zFdJiaHK)8-&7!@%j=TM~&&)$8GxAFyOa1$7h($k^-5=&QTUgf=J@|_IZ&T|DJK^H-8v9kdVSd9S$G2^`(TyvK?AIrF_ znGc6Bo-_0M0-iGS(F0?lS)ic!6*HezVwy`?YU$>(X8{kHrFQ~gx-S6UF-!R(RI{`L zpp#k3rk7&!uL_J*vy?BSvsubkIl(OLi8`yzQV3l?ZElIY16X60_CY3@AM}Del@Tgz zUNOd$hxW~QLG>9uMb>!mts;x9@_9kr*AkJ-lGa3SE5J&T8wdEi$VCN4kr0TV#_MJc zODWf^oPkEph^&ra{X`B+zrVSYy&+2!!~@dI>q&sQ=1vw?p1G41J=?5g9s6?3oe-W8 zYF2ufih?k}y@Cb+=7@s!sQbEkipAkJ8(9vm%^Ggv72)pNi9Y8*(pE z$h{vezGSo1#h2Xe5rSVHWULp2sAf1s9vgd*PY^e8SqhE%)Niz!9Ud=f0 EAB}WEWB>pF From 9ecfc475e3150b4d4339256ae540458fceff4944 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sat, 3 Dec 2016 15:28:48 -0800 Subject: [PATCH 25/53] Bump dev, fix for #874 (cherry picked from commit b375d3c) --- config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py b/config.py index c0b943585..3e2df676a 100644 --- a/config.py +++ b/config.py @@ -18,8 +18,8 @@ debug = False saveInRoot = False # Version data -version = "1.25.0" -tag = "Stable" +version = "1.25.1" +tag = "git" expansionName = "Ascension" expansionVersion = "1.9" evemonMinVersion = "4081" From 9f4b31979c945279ea6de718ee33207b6899595d Mon Sep 17 00:00:00 2001 From: Indiction Date: Sun, 4 Dec 2016 10:56:42 -0800 Subject: [PATCH 26/53] Adding Fighter Support CREST (cherry picked from commit ab5f348) --- service/port.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/service/port.py b/service/port.py index aa5cf48c7..471ee84ed 100644 --- a/service/port.py +++ b/service/port.py @@ -57,6 +57,7 @@ INV_FLAGS = { INV_FLAG_CARGOBAY = 5 INV_FLAG_DRONEBAY = 87 +INV_FLAG_FIGHTER = 158 class Port(object): @@ -254,6 +255,15 @@ class Port(object): item['type']['name'] = '' fit['items'].append(item) + for fighter in ofit.fighters: + item = nested_dict() + item['flag'] = INV_FLAG_FIGHTER + item['quantity'] = fighter.amountActive + item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, fighter.item.ID) + item['type']['id'] = fighter.item.ID + item['type']['name'] = fighter.item.name + fit['items'].append(item) + return json.dumps(fit) @classmethod @@ -319,6 +329,9 @@ class Port(object): c = Cargo(item) c.amount = module['quantity'] f.cargo.append(c) + elif module['flag'] == INV_FLAG_FIGHTER: + fighter = Fighter(item) + f.fighters.append(fighter) else: try: m = Module(item) From 7fbd89392a68859eba729a4ab155e2dddabad773 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sun, 4 Dec 2016 20:10:09 -0800 Subject: [PATCH 27/53] Revert "Remove effect files that are not used by any item" This reverts commit b29fa2467a4ac0079d12a00429537c6655859b44. (cherry picked from commit 4a95156) --- eos/effects/armorwarfarearmorhpreplacer.py | 10 ++++ eos/effects/decreasetargetspeed.py | 9 ++++ eos/effects/dronedamagebonusrequringdrones.py | 7 +++ .../elitebonuscommanddestroyerinfohidden1.py | 6 +++ ...elitebonuscommanddestroyermwdsigradius3.py | 7 +++ ...itebonuscommandshipinformationhiddencs3.py | 5 ++ eos/effects/energydestabilizationnew.py | 13 +++++ eos/effects/energytransfer.py | 9 ++++ eos/effects/ewtargetpaint.py | 8 +++ eos/effects/fighterabilityattackm.py | 16 ++++++ eos/effects/fighterabilityecm.py | 21 ++++++++ .../fighterabilityenergyneutralizer.py | 17 ++++++ eos/effects/fighterabilityevasivemaneuvers.py | 37 +++++++++++++ eos/effects/fighterabilitylaunchbomb.py | 19 +++++++ eos/effects/fighterabilitymicrowarpdrive.py | 20 +++++++ eos/effects/fighterabilitymissiles.py | 19 +++++++ eos/effects/fighterabilitystasiswebifier.py | 18 +++++++ eos/effects/fighterabilitywarpdisruption.py | 16 ++++++ eos/effects/flagshipmultirelayeffect.py | 11 ++++ .../iceharvestercapacitorneedmultiplier.py | 7 +++ .../informationwarfaremaxtargetrangebonus.py | 8 +++ .../informationwarfaremindlinkhidden.py | 8 +++ eos/effects/lightningweapon.py | 6 +++ eos/effects/maraudermodeeffect26.py | 52 +++++++++++++++++++ ...iningdirectorbonuscommandbonuseffective.py | 7 +++ .../orecapitalshipshieldtransferfalloff.py | 8 +++ .../orecapitalshipshieldtransferrange.py | 8 +++ eos/effects/pointdefense.py | 6 +++ eos/effects/remotehullrepair.py | 10 ++++ eos/effects/rigdrawbackbonuseffect.py | 7 +++ eos/effects/setbonusasklepian.py | 2 +- eos/effects/shipbonusenergyvampirerangead2.py | 7 +++ ...apshipdronearmorhpandshieldhpandhpbonus.py | 9 ++++ .../shipbonusorecapshipdronedmgbonus.py | 8 +++ ...mandbonuseffectivemultiplierorecapital2.py | 8 +++ eos/effects/shipmodescanrespostdiv.py | 11 ++++ eos/effects/shipxlprojectiledamagerole.py | 7 +++ ...siegewarfareshieldcapacitybonusreplacer.py | 8 +++ .../skirmishwarfareagilitybonusreplacer.py | 8 +++ .../structureballisticcontrolsystem.py | 17 ++++++ .../structureenergyneutralizerfalloff.py | 13 +++++ eos/effects/structuremoduleeffectecm.py | 10 ++++ ...ructuremoduleeffectremotesensordampener.py | 14 +++++ .../structuremoduleeffectstasiswebifier.py | 8 +++ .../structuremoduleeffecttargetpainter.py | 8 +++ .../structuremoduleeffectweapondisruption.py | 26 ++++++++++ ...rigaoevelocitybonussingletargetmissiles.py | 10 ++++ eos/effects/structurerigdoomsdaydamageloss.py | 8 +++ .../structurerigdoomsdaytargetamountbonus.py | 8 +++ eos/effects/structurerigewcapacitorneed.py | 9 ++++ eos/effects/structurerigewmaxrangefalloff.py | 18 +++++++ ...cturerigexplosionradiusbonusaoemissiles.py | 8 +++ eos/effects/structurerigmaxtargets.py | 6 +++ .../structurerigneutralizercapacitorneed.py | 8 +++ ...neutralizermaxrangefalloffeffectiveness.py | 12 +++++ eos/effects/structurerigpdbcapacitorneed.py | 8 +++ eos/effects/structurerigpdbmaxrange.py | 8 +++ eos/effects/structurerigsensorresolution.py | 7 +++ .../structurerigvelocitybonusaoemissiles.py | 8 +++ ...urerigvelocitybonussingletargetmissiles.py | 9 ++++ ...ucturewarpscrambleblockmwdwithnpceffect.py | 15 ++++++ ...samarrdefensiveinformationwarfarehidden.py | 5 ++ ...aldaridefensiveinformationwarfarehidden.py | 5 ++ ...llentedefensiveinformationwarfarehidden.py | 5 ++ eos/effects/techtwocommandburstbonus.py | 7 +++ eos/effects/titanturretdamagescaling.py | 7 +++ ...llofftrackingspeedmultiplytargethostile.py | 15 ++++++ 67 files changed, 744 insertions(+), 1 deletion(-) create mode 100644 eos/effects/armorwarfarearmorhpreplacer.py create mode 100644 eos/effects/decreasetargetspeed.py create mode 100644 eos/effects/dronedamagebonusrequringdrones.py create mode 100644 eos/effects/elitebonuscommanddestroyerinfohidden1.py create mode 100644 eos/effects/elitebonuscommanddestroyermwdsigradius3.py create mode 100644 eos/effects/elitebonuscommandshipinformationhiddencs3.py create mode 100644 eos/effects/energydestabilizationnew.py create mode 100644 eos/effects/energytransfer.py create mode 100644 eos/effects/ewtargetpaint.py create mode 100644 eos/effects/fighterabilityattackm.py create mode 100644 eos/effects/fighterabilityecm.py create mode 100644 eos/effects/fighterabilityenergyneutralizer.py create mode 100644 eos/effects/fighterabilityevasivemaneuvers.py create mode 100644 eos/effects/fighterabilitylaunchbomb.py create mode 100644 eos/effects/fighterabilitymicrowarpdrive.py create mode 100644 eos/effects/fighterabilitymissiles.py create mode 100644 eos/effects/fighterabilitystasiswebifier.py create mode 100644 eos/effects/fighterabilitywarpdisruption.py create mode 100644 eos/effects/flagshipmultirelayeffect.py create mode 100644 eos/effects/iceharvestercapacitorneedmultiplier.py create mode 100644 eos/effects/informationwarfaremaxtargetrangebonus.py create mode 100644 eos/effects/informationwarfaremindlinkhidden.py create mode 100644 eos/effects/lightningweapon.py create mode 100644 eos/effects/maraudermodeeffect26.py create mode 100644 eos/effects/miningdirectorbonuscommandbonuseffective.py create mode 100644 eos/effects/orecapitalshipshieldtransferfalloff.py create mode 100644 eos/effects/orecapitalshipshieldtransferrange.py create mode 100644 eos/effects/pointdefense.py create mode 100644 eos/effects/remotehullrepair.py create mode 100644 eos/effects/rigdrawbackbonuseffect.py create mode 100644 eos/effects/shipbonusenergyvampirerangead2.py create mode 100644 eos/effects/shipbonusorecapshipdronearmorhpandshieldhpandhpbonus.py create mode 100644 eos/effects/shipbonusorecapshipdronedmgbonus.py create mode 100644 eos/effects/shipcommandbonuseffectivemultiplierorecapital2.py create mode 100644 eos/effects/shipmodescanrespostdiv.py create mode 100644 eos/effects/shipxlprojectiledamagerole.py create mode 100644 eos/effects/siegewarfareshieldcapacitybonusreplacer.py create mode 100644 eos/effects/skirmishwarfareagilitybonusreplacer.py create mode 100644 eos/effects/structureballisticcontrolsystem.py create mode 100644 eos/effects/structureenergyneutralizerfalloff.py create mode 100644 eos/effects/structuremoduleeffectecm.py create mode 100644 eos/effects/structuremoduleeffectremotesensordampener.py create mode 100644 eos/effects/structuremoduleeffectstasiswebifier.py create mode 100644 eos/effects/structuremoduleeffecttargetpainter.py create mode 100644 eos/effects/structuremoduleeffectweapondisruption.py create mode 100644 eos/effects/structurerigaoevelocitybonussingletargetmissiles.py create mode 100644 eos/effects/structurerigdoomsdaydamageloss.py create mode 100644 eos/effects/structurerigdoomsdaytargetamountbonus.py create mode 100644 eos/effects/structurerigewcapacitorneed.py create mode 100644 eos/effects/structurerigewmaxrangefalloff.py create mode 100644 eos/effects/structurerigexplosionradiusbonusaoemissiles.py create mode 100644 eos/effects/structurerigmaxtargets.py create mode 100644 eos/effects/structurerigneutralizercapacitorneed.py create mode 100644 eos/effects/structurerigneutralizermaxrangefalloffeffectiveness.py create mode 100644 eos/effects/structurerigpdbcapacitorneed.py create mode 100644 eos/effects/structurerigpdbmaxrange.py create mode 100644 eos/effects/structurerigsensorresolution.py create mode 100644 eos/effects/structurerigvelocitybonusaoemissiles.py create mode 100644 eos/effects/structurerigvelocitybonussingletargetmissiles.py create mode 100644 eos/effects/structurewarpscrambleblockmwdwithnpceffect.py create mode 100644 eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py create mode 100644 eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py create mode 100644 eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py create mode 100644 eos/effects/techtwocommandburstbonus.py create mode 100644 eos/effects/titanturretdamagescaling.py create mode 100644 eos/effects/turretweaponrangefallofftrackingspeedmultiplytargethostile.py diff --git a/eos/effects/armorwarfarearmorhpreplacer.py b/eos/effects/armorwarfarearmorhpreplacer.py new file mode 100644 index 000000000..4cbe460c0 --- /dev/null +++ b/eos/effects/armorwarfarearmorhpreplacer.py @@ -0,0 +1,10 @@ +# Not used by any item +type = "gang", "active" +gangBonus = "armorHpBonus2" +gangBoost = "armorHP" + + +def handler(fit, module, context): + if "gang" not in context: + return + fit.ship.boostItemAttr("armorHP", module.getModifiedItemAttr("armorHpBonus2")) diff --git a/eos/effects/decreasetargetspeed.py b/eos/effects/decreasetargetspeed.py new file mode 100644 index 000000000..dd482175d --- /dev/null +++ b/eos/effects/decreasetargetspeed.py @@ -0,0 +1,9 @@ +# Not used by any item +type = "active", "projected" + + +def handler(fit, module, context): + if "projected" not in context: + return + fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"), + stackingPenalties=True) diff --git a/eos/effects/dronedamagebonusrequringdrones.py b/eos/effects/dronedamagebonusrequringdrones.py new file mode 100644 index 000000000..5f3e13ea1 --- /dev/null +++ b/eos/effects/dronedamagebonusrequringdrones.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" + + +def handler(fit, skill, context): + fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), + "damageMultiplier", skill.getModifiedItemAttr("damageMultiplierBonus") * skill.level) diff --git a/eos/effects/elitebonuscommanddestroyerinfohidden1.py b/eos/effects/elitebonuscommanddestroyerinfohidden1.py new file mode 100644 index 000000000..4ffc352e0 --- /dev/null +++ b/eos/effects/elitebonuscommanddestroyerinfohidden1.py @@ -0,0 +1,6 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", src.getModifiedItemAttr("eliteBonusCommandDestroyer1"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommanddestroyermwdsigradius3.py b/eos/effects/elitebonuscommanddestroyermwdsigradius3.py new file mode 100644 index 000000000..f9c1fded2 --- /dev/null +++ b/eos/effects/elitebonuscommanddestroyermwdsigradius3.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("High Speed Maneuvering"), "signatureRadiusBonus", + src.getModifiedItemAttr("eliteBonusCommandDestroyer3"), skill="Command Destroyers") diff --git a/eos/effects/elitebonuscommandshipinformationhiddencs3.py b/eos/effects/elitebonuscommandshipinformationhiddencs3.py new file mode 100644 index 000000000..27148ebec --- /dev/null +++ b/eos/effects/elitebonuscommandshipinformationhiddencs3.py @@ -0,0 +1,5 @@ +# Not used by any item +type = "passive" +def handler(fit, module, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), + "commandBonusHidden", module.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/energydestabilizationnew.py b/eos/effects/energydestabilizationnew.py new file mode 100644 index 000000000..6cf82afa5 --- /dev/null +++ b/eos/effects/energydestabilizationnew.py @@ -0,0 +1,13 @@ +# Not used by any item +from eos.types import State + +type = "active", "projected" + + +def handler(fit, src, context): + if "projected" in context and ( + (hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): + multiplier = src.amountActive if hasattr(src, "amountActive") else 1 + amount = src.getModifiedItemAttr("energyNeutralizerAmount") + time = src.getModifiedItemAttr("duration") + fit.addDrain(src, time, amount * multiplier, 0) diff --git a/eos/effects/energytransfer.py b/eos/effects/energytransfer.py new file mode 100644 index 000000000..4a5ef6530 --- /dev/null +++ b/eos/effects/energytransfer.py @@ -0,0 +1,9 @@ +# Not used by any item +type = "projected", "active" + + +def handler(fit, src, context): + if "projected" in context: + amount = src.getModifiedItemAttr("powerTransferAmount") + duration = src.getModifiedItemAttr("duration") + fit.addDrain(src, duration, -amount, 0) diff --git a/eos/effects/ewtargetpaint.py b/eos/effects/ewtargetpaint.py new file mode 100644 index 000000000..c61adeb8b --- /dev/null +++ b/eos/effects/ewtargetpaint.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "projected", "active" + + +def handler(fit, container, context): + if "projected" in context: + fit.ship.boostItemAttr("signatureRadius", container.getModifiedItemAttr("signatureRadiusBonus"), + stackingPenalties=True) diff --git a/eos/effects/fighterabilityattackm.py b/eos/effects/fighterabilityattackm.py new file mode 100644 index 000000000..2ea002819 --- /dev/null +++ b/eos/effects/fighterabilityattackm.py @@ -0,0 +1,16 @@ +# Not used by any item +""" +Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the +effects, and thus this effect file contains some custom information useful only to fighters. +""" +# User-friendly name for the ability +displayName = "Turret Attack" + +# Attribute prefix that this ability targets +prefix = "fighterAbilityAttackMissile" + +type = "active" + + +def handler(fit, src, context): + pass diff --git a/eos/effects/fighterabilityecm.py b/eos/effects/fighterabilityecm.py new file mode 100644 index 000000000..743df38ed --- /dev/null +++ b/eos/effects/fighterabilityecm.py @@ -0,0 +1,21 @@ +# Not used by any item +""" +Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the +effects, and thus this effect file contains some custom information useful only to fighters. +""" + +# User-friendly name for the ability +displayName = "ECM" + +prefix = "fighterAbilityECM" + +type = "projected", "active" + + +def handler(fit, module, context): + if "projected" not in context: + return + # jam formula: 1 - (1- (jammer str/ship str))^(# of jam mods with same str)) + strModifier = 1 - module.getModifiedItemAttr("{}Strength{}".format(prefix, fit.scanType)) / fit.scanStrength + + fit.ecmProjectedStr *= strModifier diff --git a/eos/effects/fighterabilityenergyneutralizer.py b/eos/effects/fighterabilityenergyneutralizer.py new file mode 100644 index 000000000..dd771b750 --- /dev/null +++ b/eos/effects/fighterabilityenergyneutralizer.py @@ -0,0 +1,17 @@ +# Not used by any item +""" +Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the +effects, and thus this effect file contains some custom information useful only to fighters. +""" +# User-friendly name for the ability +displayName = "Energy Neutralizer" +prefix = "fighterAbilityEnergyNeutralizer" +type = "active", "projected" + + +def handler(fit, src, context): + if "projected" in context: + amount = src.getModifiedItemAttr("{}Amount".format(prefix)) + time = src.getModifiedItemAttr("{}Duration".format(prefix)) + + fit.addDrain(src, time, amount, 0) diff --git a/eos/effects/fighterabilityevasivemaneuvers.py b/eos/effects/fighterabilityevasivemaneuvers.py new file mode 100644 index 000000000..bf17edc92 --- /dev/null +++ b/eos/effects/fighterabilityevasivemaneuvers.py @@ -0,0 +1,37 @@ +# Not used by any item +""" +Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the +effects, and thus this effect file contains some custom information useful only to fighters. +""" +# User-friendly name for the ability +displayName = "Evasive Maneuvers" + +prefix = "fighterAbilityEvasiveManeuvers" + +# Is ability applied to the fighter squad as a whole, or per fighter? +grouped = True + +type = "active" +runTime = "late" + + +def handler(fit, container, context): + container.boostItemAttr("maxVelocity", + container.getModifiedItemAttr("fighterAbilityEvasiveManeuversSpeedBonus")) + container.boostItemAttr("signatureRadius", + container.getModifiedItemAttr("fighterAbilityEvasiveManeuversSignatureRadiusBonus"), + stackingPenalties=True) + + # These may not have stacking penalties, but there's nothing else that affects the attributes yet to check. + container.multiplyItemAttr("shieldEmDamageResonance", + container.getModifiedItemAttr("fighterAbilityEvasiveManeuversEmResonance"), + stackingPenalties=True) + container.multiplyItemAttr("shieldThermalDamageResonance", + container.getModifiedItemAttr("fighterAbilityEvasiveManeuversThermResonance"), + stackingPenalties=True) + container.multiplyItemAttr("shieldKineticDamageResonance", + container.getModifiedItemAttr("fighterAbilityEvasiveManeuversKinResonance"), + stackingPenalties=True) + container.multiplyItemAttr("shieldExplosiveDamageResonance", + container.getModifiedItemAttr("fighterAbilityEvasiveManeuversExpResonance"), + stackingPenalties=True) diff --git a/eos/effects/fighterabilitylaunchbomb.py b/eos/effects/fighterabilitylaunchbomb.py new file mode 100644 index 000000000..bd46513bf --- /dev/null +++ b/eos/effects/fighterabilitylaunchbomb.py @@ -0,0 +1,19 @@ +# Not used by any item +""" +Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the +effects, and thus this effect file contains some custom information useful only to fighters. +""" +# User-friendly name for the ability +displayName = "Bomb" + +# Attribute prefix that this ability targets +prefix = "fighterAbilityLaunchBomb" + +type = "active" + +# This flag is required for effects that use charges in order to properly calculate reload time +hasCharges = True + + +def handler(fit, src, context): + pass diff --git a/eos/effects/fighterabilitymicrowarpdrive.py b/eos/effects/fighterabilitymicrowarpdrive.py new file mode 100644 index 000000000..01bea209a --- /dev/null +++ b/eos/effects/fighterabilitymicrowarpdrive.py @@ -0,0 +1,20 @@ +# Not used by any item +""" +Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the +effects, and thus this effect file contains some custom information useful only to fighters. +""" +# User-friendly name for the ability +displayName = "Microwarpdrive" + +# Is ability applied to the fighter squad as a whole, or per fighter? +grouped = True + +type = "active" +runTime = "late" + + +def handler(fit, module, context): + module.boostItemAttr("maxVelocity", module.getModifiedItemAttr("fighterAbilityMicroWarpDriveSpeedBonus")) + module.boostItemAttr("signatureRadius", + module.getModifiedItemAttr("fighterAbilityMicroWarpDriveSignatureRadiusBonus"), + stackingPenalties=True) diff --git a/eos/effects/fighterabilitymissiles.py b/eos/effects/fighterabilitymissiles.py new file mode 100644 index 000000000..8dc752ee2 --- /dev/null +++ b/eos/effects/fighterabilitymissiles.py @@ -0,0 +1,19 @@ +# Not used by any item +""" +Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the +effects, and thus this effect file contains some custom information useful only to fighters. +""" +# User-friendly name for the ability +displayName = "Missile Attack" + +# Attribute prefix that this ability targets +prefix = "fighterAbilityMissiles" + +type = "active" + +# This flag is required for effects that use charges in order to properly calculate reload time +hasCharges = True + + +def handler(fit, src, context): + pass diff --git a/eos/effects/fighterabilitystasiswebifier.py b/eos/effects/fighterabilitystasiswebifier.py new file mode 100644 index 000000000..cf5143f98 --- /dev/null +++ b/eos/effects/fighterabilitystasiswebifier.py @@ -0,0 +1,18 @@ +# Not used by any item +""" +Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the +effects, and thus this effect file contains some custom information useful only to fighters. +""" + +# User-friendly name for the ability +displayName = "Stasis Webifier" + +prefix = "fighterAbilityStasisWebifier" + +type = "active", "projected" + + +def handler(fit, src, context): + if "projected" not in context: + return + fit.ship.boostItemAttr("maxVelocity", src.getModifiedItemAttr("{}SpeedPenalty".format(prefix))) diff --git a/eos/effects/fighterabilitywarpdisruption.py b/eos/effects/fighterabilitywarpdisruption.py new file mode 100644 index 000000000..0d7013434 --- /dev/null +++ b/eos/effects/fighterabilitywarpdisruption.py @@ -0,0 +1,16 @@ +# Not used by any item +""" +Since fighter abilities do not have any sort of item entity in the EVE database, we must derive the abilities from the +effects, and thus this effect file contains some custom information useful only to fighters. +""" + +# User-friendly name for the ability +displayName = "Warp Disruption" +prefix = "fighterAbilityWarpDisruption" +type = "active", "projected" + + +def handler(fit, src, context): + if "projected" not in context: + return + fit.ship.increaseItemAttr("warpScrambleStatus", src.getModifiedItemAttr("{}PointStrength".format(prefix))) diff --git a/eos/effects/flagshipmultirelayeffect.py b/eos/effects/flagshipmultirelayeffect.py new file mode 100644 index 000000000..20bf757c2 --- /dev/null +++ b/eos/effects/flagshipmultirelayeffect.py @@ -0,0 +1,11 @@ +# Not used by any item +type = "passive" + + +def handler(fit, module, context): + # Note: we increase maxGroupActive by two. + # If we only increased it by one, we'd get the number to stay equal + # As Comman Processors use one themselves too + fit.modules.filteredItemIncrease(lambda mod: mod.item.group.name == "Gang Coordinator" and + "maxGroupActive" in mod.itemModifiedAttributes, + "maxGroupActive", 1) diff --git a/eos/effects/iceharvestercapacitorneedmultiplier.py b/eos/effects/iceharvestercapacitorneedmultiplier.py new file mode 100644 index 000000000..93338c76f --- /dev/null +++ b/eos/effects/iceharvestercapacitorneedmultiplier.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Ice Harvesting"), + "capacitorNeed", ship.getModifiedItemAttr("iceHarvestCycleBonus")) diff --git a/eos/effects/informationwarfaremaxtargetrangebonus.py b/eos/effects/informationwarfaremaxtargetrangebonus.py new file mode 100644 index 000000000..dfb0a01fc --- /dev/null +++ b/eos/effects/informationwarfaremaxtargetrangebonus.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "gang" +gangBoost = "maxTargetRange" +gangBonus = "maxTargetRangeBonus" + + +def handler(fit, container, context): + fit.ship.boostItemAttr(gangBoost, container.getModifiedItemAttr(gangBonus)) diff --git a/eos/effects/informationwarfaremindlinkhidden.py b/eos/effects/informationwarfaremindlinkhidden.py new file mode 100644 index 000000000..014ad1d31 --- /dev/null +++ b/eos/effects/informationwarfaremindlinkhidden.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, implant, context): + fit.character.getSkill("Information Command").suppress() + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), + "commandBonusHidden", implant.getModifiedItemAttr("mindlinkBonus")) diff --git a/eos/effects/lightningweapon.py b/eos/effects/lightningweapon.py new file mode 100644 index 000000000..4f2324035 --- /dev/null +++ b/eos/effects/lightningweapon.py @@ -0,0 +1,6 @@ +# Not used by any item +type = 'active' + + +def handler(fit, module, context): + pass diff --git a/eos/effects/maraudermodeeffect26.py b/eos/effects/maraudermodeeffect26.py new file mode 100644 index 000000000..f892a5a52 --- /dev/null +++ b/eos/effects/maraudermodeeffect26.py @@ -0,0 +1,52 @@ +# Not used by any item +type = "active" +runTime = "early" + + +def handler(fit, module, context): + # Resistances + for layer, attrPrefix in (('shield', 'shield'), ('armor', 'armor'), ('hull', '')): + for damageType in ('Kinetic', 'Thermal', 'Explosive', 'Em'): + bonus = "%s%sDamageResonance" % (attrPrefix, damageType) + bonus = "%s%s" % (bonus[0].lower(), bonus[1:]) + booster = "%s%sDamageResonance" % (layer, damageType) + penalize = False if layer == 'hull' else True + fit.ship.multiplyItemAttr(bonus, module.getModifiedItemAttr(booster), + stackingPenalties=penalize, penaltyGroup="preMul") + + # Turrets + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Energy Turret") or + mod.item.requiresSkill("Large Hybrid Turret") or + mod.item.requiresSkill("Large Projectile Turret"), + "maxRange", module.getModifiedItemAttr("maxRangeBonus"), + stackingPenalties=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Large Energy Turret") or + mod.item.requiresSkill("Large Hybrid Turret") or + mod.item.requiresSkill("Large Projectile Turret"), + "falloff", module.getModifiedItemAttr("falloffBonus"), + stackingPenalties=True) + + # Missiles + fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Torpedoes") or + mod.charge.requiresSkill("Cruise Missiles") or + mod.charge.requiresSkill("Heavy Missiles"), + "maxVelocity", module.getModifiedItemAttr("missileVelocityBonus")) + + # Tanking + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Capital Repair Systems") or mod.item.requiresSkill("Repair Systems"), + "armorDamageAmount", module.getModifiedItemAttr("armorDamageAmountBonus"), + stackingPenalties=True) + fit.modules.filteredItemBoost( + lambda mod: mod.item.requiresSkill("Capital Shield Operation") or mod.item.requiresSkill("Shield Operation"), + "shieldBonus", module.getModifiedItemAttr("shieldBoostMultiplier"), + stackingPenalties=True) + + # Speed penalty + fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor")) + + # Max locked targets + fit.ship.forceItemAttr("maxLockedTargets", module.getModifiedItemAttr("maxLockedTargets")) + + # Block Hostile ewar + fit.ship.forceItemAttr("disallowOffensiveModifiers", module.getModifiedItemAttr("disallowOffensiveModifiers")) diff --git a/eos/effects/miningdirectorbonuscommandbonuseffective.py b/eos/effects/miningdirectorbonuscommandbonuseffective.py new file mode 100644 index 000000000..43f712ff5 --- /dev/null +++ b/eos/effects/miningdirectorbonuscommandbonuseffective.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining Director"), + "commandBonus", ship.getModifiedItemAttr("commandBonusEffective")) diff --git a/eos/effects/orecapitalshipshieldtransferfalloff.py b/eos/effects/orecapitalshipshieldtransferfalloff.py new file mode 100644 index 000000000..6ec5e26c5 --- /dev/null +++ b/eos/effects/orecapitalshipshieldtransferfalloff.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Capital Shield Emission Systems"), + "falloffEffectiveness", src.getModifiedItemAttr("shipBonusORECapital3"), + skill="Capital Industrial Ships") diff --git a/eos/effects/orecapitalshipshieldtransferrange.py b/eos/effects/orecapitalshipshieldtransferrange.py new file mode 100644 index 000000000..2e7f24d88 --- /dev/null +++ b/eos/effects/orecapitalshipshieldtransferrange.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Capital Shield Emission Systems"), + "maxRange", ship.getModifiedItemAttr("shipBonusORECapital3"), + skill="Capital Industrial Ships") diff --git a/eos/effects/pointdefense.py b/eos/effects/pointdefense.py new file mode 100644 index 000000000..4f2324035 --- /dev/null +++ b/eos/effects/pointdefense.py @@ -0,0 +1,6 @@ +# Not used by any item +type = 'active' + + +def handler(fit, module, context): + pass diff --git a/eos/effects/remotehullrepair.py b/eos/effects/remotehullrepair.py new file mode 100644 index 000000000..099e0c2ac --- /dev/null +++ b/eos/effects/remotehullrepair.py @@ -0,0 +1,10 @@ +# Not used by any item +type = "projected", "active" +runTime = "late" + + +def handler(fit, module, context): + if "projected" not in context: return + bonus = module.getModifiedItemAttr("structureDamageAmount") + duration = module.getModifiedItemAttr("duration") / 1000.0 + fit.extraAttributes.increase("hullRepair", bonus / duration) diff --git a/eos/effects/rigdrawbackbonuseffect.py b/eos/effects/rigdrawbackbonuseffect.py new file mode 100644 index 000000000..58f066b1d --- /dev/null +++ b/eos/effects/rigdrawbackbonuseffect.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" + + +def handler(fit, skill, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill(skill), + "drawback", skill.getModifiedItemAttr("rigDrawbackBonus") * skill.level) diff --git a/eos/effects/setbonusasklepian.py b/eos/effects/setbonusasklepian.py index 4bd989f13..5d063f470 100644 --- a/eos/effects/setbonusasklepian.py +++ b/eos/effects/setbonusasklepian.py @@ -1,8 +1,8 @@ # setBonusAsklepian # # Used by: -# Implants named like: Asklepian Omega (3 of 3) # Implants named like: Grade Asklepian (16 of 16) +# Implants named like: grade Asklepian Omega (2 of 2) runTime = "early" type = "passive" diff --git a/eos/effects/shipbonusenergyvampirerangead2.py b/eos/effects/shipbonusenergyvampirerangead2.py new file mode 100644 index 000000000..28f9f7c8c --- /dev/null +++ b/eos/effects/shipbonusenergyvampirerangead2.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Energy Nosferatu", + "maxRange", ship.getModifiedItemAttr("shipBonusAD2"), skill="Amarr Destroyer") diff --git a/eos/effects/shipbonusorecapshipdronearmorhpandshieldhpandhpbonus.py b/eos/effects/shipbonusorecapshipdronearmorhpandshieldhpandhpbonus.py new file mode 100644 index 000000000..e8860ee63 --- /dev/null +++ b/eos/effects/shipbonusorecapshipdronearmorhpandshieldhpandhpbonus.py @@ -0,0 +1,9 @@ +# Not used by any item +type = "passive" + + +def handler(fit, ship, context): + for type in ("shieldCapacity", "armorHP", "hp"): + fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), + type, ship.getModifiedItemAttr("shipBonusORECapital4"), + skill="Capital Industrial Ships") diff --git a/eos/effects/shipbonusorecapshipdronedmgbonus.py b/eos/effects/shipbonusorecapshipdronedmgbonus.py new file mode 100644 index 000000000..eaa95fe6d --- /dev/null +++ b/eos/effects/shipbonusorecapshipdronedmgbonus.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, ship, context): + fit.drones.filteredItemBoost(lambda drone: drone.item.requiresSkill("Drones"), + "damageMultiplier", ship.getModifiedItemAttr("shipBonusORECapital4"), + skill="Capital Industrial Ships") diff --git a/eos/effects/shipcommandbonuseffectivemultiplierorecapital2.py b/eos/effects/shipcommandbonuseffectivemultiplierorecapital2.py new file mode 100644 index 000000000..76673f21f --- /dev/null +++ b/eos/effects/shipcommandbonuseffectivemultiplierorecapital2.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, ship, context): + if fit.extraAttributes["siege"]: + fit.ship.increaseItemAttr("commandBonusEffective", ship.getModifiedItemAttr("shipBonusORECapital2"), + skill="Capital Industrial Ships") diff --git a/eos/effects/shipmodescanrespostdiv.py b/eos/effects/shipmodescanrespostdiv.py new file mode 100644 index 000000000..2ad04ec22 --- /dev/null +++ b/eos/effects/shipmodescanrespostdiv.py @@ -0,0 +1,11 @@ +# Not used by any item +type = "passive" + + +def handler(fit, module, context): + fit.ship.multiplyItemAttr( + "scanResolution", + 1 / module.getModifiedItemAttr("modeScanResPostDiv"), + stackingPenalties=True, + penaltyGroup="postDiv" + ) diff --git a/eos/effects/shipxlprojectiledamagerole.py b/eos/effects/shipxlprojectiledamagerole.py new file mode 100644 index 000000000..62f670f18 --- /dev/null +++ b/eos/effects/shipxlprojectiledamagerole.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Capital Projectile Turret"), + "damageMultiplier", ship.getModifiedItemAttr("shipBonusPirateFaction")) diff --git a/eos/effects/siegewarfareshieldcapacitybonusreplacer.py b/eos/effects/siegewarfareshieldcapacitybonusreplacer.py new file mode 100644 index 000000000..4ec89d183 --- /dev/null +++ b/eos/effects/siegewarfareshieldcapacitybonusreplacer.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "gang" +gangBoost = "shieldCapacity" +gangBonus = "shieldCapacityBonus" + + +def handler(fit, container, context): + fit.ship.boostItemAttr(gangBoost, container.getModifiedItemAttr(gangBonus)) diff --git a/eos/effects/skirmishwarfareagilitybonusreplacer.py b/eos/effects/skirmishwarfareagilitybonusreplacer.py new file mode 100644 index 000000000..75aff212a --- /dev/null +++ b/eos/effects/skirmishwarfareagilitybonusreplacer.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "gang" +gangBoost = "agility" +gangBonus = "agilityBonus" + + +def handler(fit, container, context): + fit.ship.boostItemAttr(gangBoost, container.getModifiedItemAttr(gangBonus)) diff --git a/eos/effects/structureballisticcontrolsystem.py b/eos/effects/structureballisticcontrolsystem.py new file mode 100644 index 000000000..e075af277 --- /dev/null +++ b/eos/effects/structureballisticcontrolsystem.py @@ -0,0 +1,17 @@ +# Not used by any item +type = "passive" + + +def handler(fit, module, context): + missileGroups = ("Structure Anti-Capital Missile", "Structure Anti-Subcapital Missile") + + for dmgType in ("em", "kinetic", "explosive", "thermal"): + fit.modules.filteredChargeMultiply(lambda mod: mod.charge.group.name in missileGroups, + "%sDamage" % dmgType, + module.getModifiedItemAttr("missileDamageMultiplierBonus"), + stackingPenalties=True) + + launcherGroups = ("Structure AXL Missile Launcher", "Structure ASML Missile Launcher") + fit.modules.filteredItemMultiply(lambda mod: mod.item.group.name in launcherGroups, + "speed", module.getModifiedItemAttr("speedMultiplier"), + stackingPenalties=True) diff --git a/eos/effects/structureenergyneutralizerfalloff.py b/eos/effects/structureenergyneutralizerfalloff.py new file mode 100644 index 000000000..8b6bf5846 --- /dev/null +++ b/eos/effects/structureenergyneutralizerfalloff.py @@ -0,0 +1,13 @@ +# Not used by any item +from eos.types import State + +type = "active", "projected" + + +def handler(fit, container, context): + amount = 0 + if "projected" in context and ((hasattr(container, "state") + and container.state >= State.ACTIVE) or hasattr(container, "amountActive")): + amount = container.getModifiedItemAttr("energyNeutralizerAmount") + time = container.getModifiedItemAttr("duration") + fit.addDrain(time, amount, 0) diff --git a/eos/effects/structuremoduleeffectecm.py b/eos/effects/structuremoduleeffectecm.py new file mode 100644 index 000000000..9a39b452a --- /dev/null +++ b/eos/effects/structuremoduleeffectecm.py @@ -0,0 +1,10 @@ +# Not used by any item +type = "projected", "active" + + +def handler(fit, module, context): + if "projected" in context: + # jam formula: 1 - (1- (jammer str/ship str))^(# of jam mods with same str)) + strModifier = 1 - module.getModifiedItemAttr("scan{0}StrengthBonus".format(fit.scanType)) / fit.scanStrength + + fit.ecmProjectedStr *= strModifier diff --git a/eos/effects/structuremoduleeffectremotesensordampener.py b/eos/effects/structuremoduleeffectremotesensordampener.py new file mode 100644 index 000000000..7260ef623 --- /dev/null +++ b/eos/effects/structuremoduleeffectremotesensordampener.py @@ -0,0 +1,14 @@ +# Not used by any item + +type = "projected", "active" + + +def handler(fit, module, context): + if "projected" not in context: + return + + fit.ship.boostItemAttr("maxTargetRange", module.getModifiedItemAttr("maxTargetRangeBonus"), + stackingPenalties=True, remoteResists=True) + + fit.ship.boostItemAttr("scanResolution", module.getModifiedItemAttr("scanResolutionBonus"), + stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/structuremoduleeffectstasiswebifier.py b/eos/effects/structuremoduleeffectstasiswebifier.py new file mode 100644 index 000000000..dc4b6f046 --- /dev/null +++ b/eos/effects/structuremoduleeffectstasiswebifier.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "active", "projected" + + +def handler(fit, module, context): + if "projected" not in context: return + fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"), + stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/structuremoduleeffecttargetpainter.py b/eos/effects/structuremoduleeffecttargetpainter.py new file mode 100644 index 000000000..aac9005ba --- /dev/null +++ b/eos/effects/structuremoduleeffecttargetpainter.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "projected", "active" + + +def handler(fit, container, context): + if "projected" in context: + fit.ship.boostItemAttr("signatureRadius", container.getModifiedItemAttr("signatureRadiusBonus"), + stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/structuremoduleeffectweapondisruption.py b/eos/effects/structuremoduleeffectweapondisruption.py new file mode 100644 index 000000000..82e67797e --- /dev/null +++ b/eos/effects/structuremoduleeffectweapondisruption.py @@ -0,0 +1,26 @@ +# Not used by any item + +type = "active", "projected" + + +def handler(fit, module, context): + if "projected" in context: + for srcAttr, tgtAttr in ( + ("aoeCloudSizeBonus", "aoeCloudSize"), + ("aoeVelocityBonus", "aoeVelocity"), + ("missileVelocityBonus", "maxVelocity"), + ("explosionDelayBonus", "explosionDelay"), + ): + fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill("Missile Launcher Operation"), + tgtAttr, module.getModifiedItemAttr(srcAttr), + stackingPenalties=True, remoteResists=True) + + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"), + "trackingSpeed", module.getModifiedItemAttr("trackingSpeedBonus"), + stackingPenalties=True, remoteResists=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"), + "maxRange", module.getModifiedItemAttr("maxRangeBonus"), + stackingPenalties=True, remoteResists=True) + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Gunnery"), + "falloff", module.getModifiedItemAttr("falloffBonus"), + stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/structurerigaoevelocitybonussingletargetmissiles.py b/eos/effects/structurerigaoevelocitybonussingletargetmissiles.py new file mode 100644 index 000000000..6d4503ec7 --- /dev/null +++ b/eos/effects/structurerigaoevelocitybonussingletargetmissiles.py @@ -0,0 +1,10 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + groups = ("Structure Anti-Subcapital Missile", "Structure Anti-Capital Missile") + + fit.modules.filteredItemBoost(lambda mod: mod.charge.group.name in groups, + "aoeVelocity", src.getModifiedItemAttr("structureRigMissileExploVeloBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigdoomsdaydamageloss.py b/eos/effects/structurerigdoomsdaydamageloss.py new file mode 100644 index 000000000..e2c6c613b --- /dev/null +++ b/eos/effects/structurerigdoomsdaydamageloss.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemIncrease(lambda mod: mod.item.group.name == "Structure Doomsday Weapon", + "lightningWeaponDamageLossTarget", + src.getModifiedItemAttr("structureRigDoomsdayDamageLossTargetBonus")) diff --git a/eos/effects/structurerigdoomsdaytargetamountbonus.py b/eos/effects/structurerigdoomsdaytargetamountbonus.py new file mode 100644 index 000000000..a68ca1054 --- /dev/null +++ b/eos/effects/structurerigdoomsdaytargetamountbonus.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemIncrease(lambda mod: mod.item.group.name == "Structure Doomsday Weapon", + "lightningWeaponTargetAmount", + src.getModifiedItemAttr("structureRigDoomsdayTargetAmountBonus")) diff --git a/eos/effects/structurerigewcapacitorneed.py b/eos/effects/structurerigewcapacitorneed.py new file mode 100644 index 000000000..dfeae5753 --- /dev/null +++ b/eos/effects/structurerigewcapacitorneed.py @@ -0,0 +1,9 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + groups = ("Structure ECM Battery", "Structure Disruption Battery") + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, + "capacitorNeed", src.getModifiedItemAttr("structureRigEwarCapUseBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigewmaxrangefalloff.py b/eos/effects/structurerigewmaxrangefalloff.py new file mode 100644 index 000000000..fa6ed13c9 --- /dev/null +++ b/eos/effects/structurerigewmaxrangefalloff.py @@ -0,0 +1,18 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + groups = ("Structure ECM Battery", "Structure Disruption Battery") + + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, + "falloff", src.getModifiedItemAttr("structureRigEwarFalloffBonus"), + stackingPenalties=True) + + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, + "maxRange", src.getModifiedItemAttr("structureRigEwarOptimalBonus"), + stackingPenalties=True) + + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name in groups, + "falloffEffectiveness", src.getModifiedItemAttr("structureRigEwarFalloffBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigexplosionradiusbonusaoemissiles.py b/eos/effects/structurerigexplosionradiusbonusaoemissiles.py new file mode 100644 index 000000000..34e840638 --- /dev/null +++ b/eos/effects/structurerigexplosionradiusbonusaoemissiles.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name == "Structure Guided Bomb", + "aoeCloudSize", src.getModifiedItemAttr("structureRigMissileExplosionRadiusBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigmaxtargets.py b/eos/effects/structurerigmaxtargets.py new file mode 100644 index 000000000..d7b5ecfe5 --- /dev/null +++ b/eos/effects/structurerigmaxtargets.py @@ -0,0 +1,6 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.ship.increaseItemAttr("maxLockedTargets", src.getModifiedItemAttr("structureRigMaxTargetBonus")) diff --git a/eos/effects/structurerigneutralizercapacitorneed.py b/eos/effects/structurerigneutralizercapacitorneed.py new file mode 100644 index 000000000..c570161fa --- /dev/null +++ b/eos/effects/structurerigneutralizercapacitorneed.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Structure Energy Neutralizer", + "capacitorNeed", src.getModifiedItemAttr("structureRigEwarCapUseBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigneutralizermaxrangefalloffeffectiveness.py b/eos/effects/structurerigneutralizermaxrangefalloffeffectiveness.py new file mode 100644 index 000000000..d123e0278 --- /dev/null +++ b/eos/effects/structurerigneutralizermaxrangefalloffeffectiveness.py @@ -0,0 +1,12 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Structure Energy Neutralizer", + "maxRange", src.getModifiedItemAttr("structureRigEwarOptimalBonus"), + stackingPenalties=True) + + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Structure Energy Neutralizer", + "falloffEffectiveness", src.getModifiedItemAttr("structureRigEwarFalloffBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigpdbcapacitorneed.py b/eos/effects/structurerigpdbcapacitorneed.py new file mode 100644 index 000000000..a32e63ecc --- /dev/null +++ b/eos/effects/structurerigpdbcapacitorneed.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Structure Area Denial Module", + "capacitorNeed", src.getModifiedItemAttr("structureRigPDCapUseBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigpdbmaxrange.py b/eos/effects/structurerigpdbmaxrange.py new file mode 100644 index 000000000..64994bf26 --- /dev/null +++ b/eos/effects/structurerigpdbmaxrange.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == "Structure Area Denial Module", + "empFieldRange", src.getModifiedItemAttr("structureRigPDRangeBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigsensorresolution.py b/eos/effects/structurerigsensorresolution.py new file mode 100644 index 000000000..8d68050e6 --- /dev/null +++ b/eos/effects/structurerigsensorresolution.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.ship.boostItemAttr("scanResolution", src.getModifiedItemAttr("structureRigScanResBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigvelocitybonusaoemissiles.py b/eos/effects/structurerigvelocitybonusaoemissiles.py new file mode 100644 index 000000000..636134b1f --- /dev/null +++ b/eos/effects/structurerigvelocitybonusaoemissiles.py @@ -0,0 +1,8 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + fit.modules.filteredChargeBoost(lambda mod: mod.charge.group.name == "Structure Guided Bomb", + "maxVelocity", src.getModifiedItemAttr("structureRigMissileVelocityBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurerigvelocitybonussingletargetmissiles.py b/eos/effects/structurerigvelocitybonussingletargetmissiles.py new file mode 100644 index 000000000..1af82afd0 --- /dev/null +++ b/eos/effects/structurerigvelocitybonussingletargetmissiles.py @@ -0,0 +1,9 @@ +# Not used by any item +type = "passive" + + +def handler(fit, src, context): + groups = ("Structure Anti-Subcapital Missile", "Structure Anti-Capital Missile") + fit.modules.filteredItemBoost(lambda mod: mod.charge.group.name in groups, + "maxVelocity", src.getModifiedItemAttr("structureRigMissileVelocityBonus"), + stackingPenalties=True) diff --git a/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py b/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py new file mode 100644 index 000000000..4bf33ce41 --- /dev/null +++ b/eos/effects/structurewarpscrambleblockmwdwithnpceffect.py @@ -0,0 +1,15 @@ +# Not used by any item +from eos.types import State + +# Not used by any item +runTime = "early" +type = "projected", "active" + + +def handler(fit, module, context): + if "projected" not in context: + return + # this is such a dirty hack + for mod in fit.modules: + if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE: + mod.state = State.ONLINE diff --git a/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py b/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py new file mode 100644 index 000000000..9dfb9ad19 --- /dev/null +++ b/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py @@ -0,0 +1,5 @@ +# Not used by any item +type = "passive" +def handler(fit, module, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), + "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py b/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py new file mode 100644 index 000000000..f01b7efb7 --- /dev/null +++ b/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py @@ -0,0 +1,5 @@ +# Not used by any item +type = "passive" +def handler(fit, module, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), + "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py b/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py new file mode 100644 index 000000000..7a3f8205f --- /dev/null +++ b/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py @@ -0,0 +1,5 @@ +# Not used by any item +type = "passive" +def handler(fit, module, context): + fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), + "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") diff --git a/eos/effects/techtwocommandburstbonus.py b/eos/effects/techtwocommandburstbonus.py new file mode 100644 index 000000000..a85f78ab9 --- /dev/null +++ b/eos/effects/techtwocommandburstbonus.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" +runTime = "late" + +def handler(fit, module, context): + for x in xrange(1, 4): + module.boostChargeAttr("warfareBuff{}Multiplier".format(x), module.getModifiedItemAttr("commandBurstStrengthBonus")) diff --git a/eos/effects/titanturretdamagescaling.py b/eos/effects/titanturretdamagescaling.py new file mode 100644 index 000000000..a8f00c006 --- /dev/null +++ b/eos/effects/titanturretdamagescaling.py @@ -0,0 +1,7 @@ +# Not used by any item +type = "passive" + + +def handler(fit, ship, context): + fit.modules.filteredItemIncrease(lambda mod: mod.item.requiresSkill("Gunnery"), + "turretDamageScalingRadius", ship.getModifiedItemAttr("titanBonusScalingRadius")) diff --git a/eos/effects/turretweaponrangefallofftrackingspeedmultiplytargethostile.py b/eos/effects/turretweaponrangefallofftrackingspeedmultiplytargethostile.py new file mode 100644 index 000000000..12d535bb2 --- /dev/null +++ b/eos/effects/turretweaponrangefallofftrackingspeedmultiplytargethostile.py @@ -0,0 +1,15 @@ +# Not used by any item +type = "projected", "active" + + +def handler(fit, container, context): + if "projected" in context: + fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Gunnery"), + "trackingSpeed", container.getModifiedItemAttr("trackingSpeedMultiplier"), + stackingPenalties=True, penaltyGroup="postMul") + fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Gunnery"), + "maxRange", container.getModifiedItemAttr("maxRangeMultiplier"), + stackingPenalties=True, penaltyGroup="postMul") + fit.modules.filteredItemMultiply(lambda mod: mod.item.requiresSkill("Gunnery"), + "falloff", container.getModifiedItemAttr("fallofMultiplier"), + stackingPenalties=True, penaltyGroup="postMul") From edfa13093979befefd37b6360ef709777b60b74f Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sun, 4 Dec 2016 22:36:22 -0800 Subject: [PATCH 28/53] Fix tooltip order (cherry picked from commit 61d1878) --- gui/builtinViewColumns/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index 6e9c5cb45..20cc829f9 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -226,8 +226,8 @@ class Miscellanea(ViewColumn): tooltip = "ECM Jammer Strength:\n{0} Gravimetric | {1} Ladar | {2} Magnetometric | {3} Radar".format( formatAmount(grav, 3, 0, 3), formatAmount(ladar, 3, 0, 3), - formatAmount(radar, 3, 0, 3), formatAmount(magnet, 3, 0, 3), + formatAmount(radar, 3, 0, 3), ) return text, tooltip elif itemGroup in ("Remote Sensor Booster", "Sensor Booster", "Signal Amplifier"): From 1a127bb1a6e56fff90e7fffc9fddca6507cd3fca Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 8 Dec 2016 13:12:15 -0800 Subject: [PATCH 29/53] Add ability to look at tactical mode item info. (cherry picked from commit f5776a0) --- eos/saveddata/mode.py | 5 +++++ gui/builtinViews/fittingView.py | 23 ++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/eos/saveddata/mode.py b/eos/saveddata/mode.py index 2b3108a9d..d505807b9 100644 --- a/eos/saveddata/mode.py +++ b/eos/saveddata/mode.py @@ -53,3 +53,8 @@ class Mode(ItemAttrShortcut, HandledItem): for effect in self.item.effects.itervalues(): if effect.runTime == runTime and effect.activeByDefault: effect.handler(fit, self, context=("module",)) + + def getValidCharges(self): + # Modes don't have charges, but it is queried for so return nothing. + validCharges = set() + return validCharges diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 7e9d09d80..f707731c6 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -344,7 +344,11 @@ class FittingView(d.Display): def removeModule(self, module): sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) - populate = sFit.removeModule(self.activeFitID, fit.modules.index(module)) + try: + populate = sFit.removeModule(self.activeFitID, fit.modules.index(module)) + except ValueError: + # This module isn't in our list of modules, don't remove anything. Likely a special snowflake. + populate = None if populate is not None: self.slotsChanged() @@ -446,12 +450,11 @@ class FittingView(d.Display): if fit.mode: # Modes are special snowflakes and need a little manual loving # We basically append the Mode rack and Mode to the modules - # while also marking their positions in the Blanks list + # while also marking the mode header position in the Blanks list if sFit.serviceFittingOptions["rackSlots"]: self.blanks.append(len(self.mods)) self.mods.append(Rack.buildRack(Slot.MODE)) - self.blanks.append(len(self.mods)) self.mods.append(fit.mode) else: self.mods = None @@ -493,12 +496,26 @@ class FittingView(d.Display): while sel != -1 and sel not in self.blanks: mod = self.mods[self.GetItemData(sel)] + + # Test if mod.isEmpty exists. + try: + mod.isEmpty + except AttributeError: + mod.isEmpty = False + if not mod.isEmpty: srcContext = "fittingModule" itemContext = sMkt.getCategoryByItem(mod.item).name fullContext = (srcContext, itemContext) if srcContext not in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) + + # Test if mod.charge exists + try: + mod.charge + except AttributeError: + mod.charge = None + if mod.charge is not None: srcContext = "fittingCharge" itemContext = sMkt.getCategoryByItem(mod.charge).name From 2eacadf08f1338350f6dededb4b5745a68f66fae Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 8 Dec 2016 16:59:26 -0800 Subject: [PATCH 30/53] Don't use try for fittingView, and don't show remove item for modes. (cherry picked from commit fd224d6) --- gui/builtinViews/fittingView.py | 9 +++++---- gui/contextMenu.py | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index f707731c6..de9d5ea38 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -497,10 +497,11 @@ class FittingView(d.Display): while sel != -1 and sel not in self.blanks: mod = self.mods[self.GetItemData(sel)] - # Test if mod.isEmpty exists. - try: - mod.isEmpty - except AttributeError: + # Test if mod.isEmpty does not exist. + # Certain special module can be missing this trait + # Example: T3D modes + if not hasattr(mod, 'isEmpty'): + # Set it if missing, prevents later stack traces. mod.isEmpty = False if not mod.isEmpty: diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 5e8839cac..a881a995c 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -70,6 +70,17 @@ class ContextMenu(object): if m.display(srcContext, selection): amount += 1 texts = m.getText(itemContext, selection) + + # Check the selected item to see if it has special reasons for not showing the menu item + skip_menu_item = False + for sel in selection: + if hasattr(sel, "_Mode__item") and texts == "Remove Module": + # Don't show remove for modes, these are special modules that cannot be removed + skip_menu_item = True + + if skip_menu_item == True: + continue + if isinstance(texts, basestring): texts = (texts,) From ea3a374ced2a5ed4c45a1542cabadba00d719b6e Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 8 Dec 2016 17:31:04 -0800 Subject: [PATCH 31/53] Add logging, because why not (cherry picked from commit 9403a1f) --- gui/builtinViews/fittingView.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index de9d5ea38..61d337f62 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -30,12 +30,15 @@ from gui.builtinViewColumns.state import State from gui.bitmapLoader import BitmapLoader import gui.builtinViews.emptyView from gui.utils.exportHtml import exportHtml +from logging import getLogger, Formatter from service.fit import Fit from service.market import Market import gui.globalEvents as GE +logger = getLogger(__name__) + # Tab spawning handler class FitSpawner(gui.multiSwitch.TabSpawner): @@ -348,6 +351,7 @@ class FittingView(d.Display): populate = sFit.removeModule(self.activeFitID, fit.modules.index(module)) except ValueError: # This module isn't in our list of modules, don't remove anything. Likely a special snowflake. + logger.debug("Failed attempt to remove %s from fit" % module.item.name) populate = None if populate is not None: @@ -515,6 +519,7 @@ class FittingView(d.Display): try: mod.charge except AttributeError: + # The attribute doesn't exist at all. Set to none so we don't get errors later. mod.charge = None if mod.charge is not None: From b529a287155636c8eb00c990b3b6af1589500270 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 8 Dec 2016 18:56:43 -0800 Subject: [PATCH 32/53] Implement fittingMode (cherry picked from commit 5eb2fef) --- gui/builtinContextMenus/itemRemove.py | 3 ++- gui/builtinContextMenus/itemStats.py | 5 ++++- gui/builtinViews/fittingView.py | 25 +++++++++++++------------ gui/contextMenu.py | 9 ++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/gui/builtinContextMenus/itemRemove.py b/gui/builtinContextMenus/itemRemove.py index 400890b1a..3beeb56b9 100644 --- a/gui/builtinContextMenus/itemRemove.py +++ b/gui/builtinContextMenus/itemRemove.py @@ -15,7 +15,8 @@ class ItemRemove(ContextMenu): "boosterItem", "projectedModule", "projectedCharge", "cargoItem", "projectedFit", "projectedDrone", - "fighterItem", "projectedFighter") + "fighterItem", "projectedFighter", + "fittingMode",) def getText(self, itmContext, selection): return "Remove {0}".format(itmContext if itmContext is not None else "Item") diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index 2f822aec3..2ccc3d364 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -19,7 +19,8 @@ class ItemStats(ContextMenu): "skillItem", "projectedModule", "projectedDrone", "projectedCharge", "itemStats", "fighterItem", - "implantItemChar", "projectedFighter") + "implantItemChar", "projectedFighter", + "fittingMode") def getText(self, itmContext, selection): return "{0} Stats".format(itmContext if itmContext is not None else "Item") @@ -30,6 +31,8 @@ class ItemStats(ContextMenu): fitID = self.mainFrame.getActiveFit() sFit = Fit.getInstance() stuff = sFit.getFit(fitID).ship + elif srcContext == "fittingMode": + stuff = selection[0].item else: stuff = selection[0] diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 61d337f62..0733a8054 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -501,12 +501,19 @@ class FittingView(d.Display): while sel != -1 and sel not in self.blanks: mod = self.mods[self.GetItemData(sel)] - # Test if mod.isEmpty does not exist. - # Certain special module can be missing this trait - # Example: T3D modes - if not hasattr(mod, 'isEmpty'): - # Set it if missing, prevents later stack traces. - mod.isEmpty = False + # Test if this is a mode, which is a special snowflake of a Module + if hasattr(mod, "_Mode__item"): + srcContext = "fittingMode" + # Skip the normal processing + mod.isEmpty = True + + itemContext = sMkt.getCategoryByItem(mod.item).name + fullContext = (srcContext, itemContext) + if not srcContext in tuple(fCtxt[0] for fCtxt in contexts): + contexts.append(fullContext) + + selection.append(mod) + if not mod.isEmpty: srcContext = "fittingModule" @@ -515,12 +522,6 @@ class FittingView(d.Display): if srcContext not in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) - # Test if mod.charge exists - try: - mod.charge - except AttributeError: - # The attribute doesn't exist at all. Set to none so we don't get errors later. - mod.charge = None if mod.charge is not None: srcContext = "fittingCharge" diff --git a/gui/contextMenu.py b/gui/contextMenu.py index a881a995c..83a260414 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -72,13 +72,8 @@ class ContextMenu(object): texts = m.getText(itemContext, selection) # Check the selected item to see if it has special reasons for not showing the menu item - skip_menu_item = False - for sel in selection: - if hasattr(sel, "_Mode__item") and texts == "Remove Module": - # Don't show remove for modes, these are special modules that cannot be removed - skip_menu_item = True - - if skip_menu_item == True: + if srcContext == "fittingMode" and texts == "Remove Module": + # Don't show remove for modes, these are special modules that cannot be removed continue if isinstance(texts, basestring): From 96ddb0bbff97c7fa1f26cb14e410efc70b055271 Mon Sep 17 00:00:00 2001 From: blitzman Date: Thu, 8 Dec 2016 21:07:35 -0800 Subject: [PATCH 33/53] Fixed a few issues with command bursts (cherry picked from commit 9071960) --- eos/effects/chargebonuswarfarecharge.py | 2 +- eos/effects/modulebonuswarfarelinkarmor.py | 2 +- eos/effects/modulebonuswarfarelinkinfo.py | 2 +- eos/effects/modulebonuswarfarelinkshield.py | 2 +- eos/effects/modulebonuswarfarelinkskirmish.py | 2 +- eos/saveddata/fit.py | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eos/effects/chargebonuswarfarecharge.py b/eos/effects/chargebonuswarfarecharge.py index fc4af6c2d..49f583beb 100644 --- a/eos/effects/chargebonuswarfarecharge.py +++ b/eos/effects/chargebonuswarfarecharge.py @@ -24,7 +24,7 @@ def handler(fit, module, context, **kwargs): if id == 10: # Shield Burst: Shield Harmonizing: Shield Resistance for damageType in ("Em", "Explosive", "Thermal", "Kinetic"): - fit.ship.boostItemAttr("shield%sDamageResonance" % damageType, value, stackingPenalties=True) + fit.ship.boostItemAttr("shield%sDamageResonance" % damageType, value) if id == 11: # Shield Burst: Active Shielding: Repair Duration/Capacitor fit.modules.filteredItemBoost( diff --git a/eos/effects/modulebonuswarfarelinkarmor.py b/eos/effects/modulebonuswarfarelinkarmor.py index daa55444e..7cb5aeb2e 100644 --- a/eos/effects/modulebonuswarfarelinkarmor.py +++ b/eos/effects/modulebonuswarfarelinkarmor.py @@ -3,7 +3,7 @@ # Used by: # Variations of module: Armor Command Burst I (2 of 2) type = "active" -runTime = "late" +runTime = "early" def handler(fit, module, context): diff --git a/eos/effects/modulebonuswarfarelinkinfo.py b/eos/effects/modulebonuswarfarelinkinfo.py index 32b493279..d9bbeae33 100644 --- a/eos/effects/modulebonuswarfarelinkinfo.py +++ b/eos/effects/modulebonuswarfarelinkinfo.py @@ -3,7 +3,7 @@ # Used by: # Variations of module: Information Command Burst I (2 of 2) type = "active" -runTime = "late" +runTime = "early" def handler(fit, module, context): diff --git a/eos/effects/modulebonuswarfarelinkshield.py b/eos/effects/modulebonuswarfarelinkshield.py index 856137e74..567364749 100644 --- a/eos/effects/modulebonuswarfarelinkshield.py +++ b/eos/effects/modulebonuswarfarelinkshield.py @@ -3,7 +3,7 @@ # Used by: # Variations of module: Shield Command Burst I (2 of 2) type = "active" -runTime = "late" +runTime = "early" def handler(fit, module, context): diff --git a/eos/effects/modulebonuswarfarelinkskirmish.py b/eos/effects/modulebonuswarfarelinkskirmish.py index c0bedf2b1..aaee36640 100644 --- a/eos/effects/modulebonuswarfarelinkskirmish.py +++ b/eos/effects/modulebonuswarfarelinkskirmish.py @@ -3,7 +3,7 @@ # Used by: # Variations of module: Skirmish Command Burst I (2 of 2) type = "active" -runTime = "late" +runTime = "early" def handler(fit, module, context): diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 8e5435acf..5ca38dba0 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -458,9 +458,9 @@ class Fit(object): def __runCommandBoosts(self, runTime="normal"): logger.debug("Applying gang boosts for %r", self) - for warfareBuffID, info in self.commandBonuses.iteritems(): + for warfareBuffID in self.commandBonuses.keys(): # Unpack all data required to run effect properly - effect_runTime, value, thing, effect = info + effect_runTime, value, thing, effect = self.commandBonuses[warfareBuffID] if runTime != effect_runTime: continue @@ -491,7 +491,7 @@ class Fit(object): except: pass - self.commandBonuses.clear() + del self.commandBonuses[warfareBuffID] def calculateModifiedAttributes(self, targetFit=None, withBoosters=False, dirtyStorage=None): timer = Timer(u'Fit: {}, {}'.format(self.ID, self.name), logger) From 52257d60ac48009007c014b777a917e366e7716e Mon Sep 17 00:00:00 2001 From: blitzman Date: Fri, 9 Dec 2016 22:00:14 -0800 Subject: [PATCH 34/53] fix a couple command effects (cherry picked from commit 9bb86b4) --- eos/effects/chargebonuswarfarecharge.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/eos/effects/chargebonuswarfarecharge.py b/eos/effects/chargebonuswarfarecharge.py index 49f583beb..e04ebe6f0 100644 --- a/eos/effects/chargebonuswarfarecharge.py +++ b/eos/effects/chargebonuswarfarecharge.py @@ -39,7 +39,7 @@ def handler(fit, module, context, **kwargs): if id == 13: # Armor Burst: Armor Energizing: Armor Resistance for damageType in ("Em", "Thermal", "Explosive", "Kinetic"): - fit.ship.boostItemAttr("armor%sDamageResonance" % damageType, value, stackingPenalties=True) + fit.ship.boostItemAttr("armor%sDamageResonance" % damageType, value) if id == 14: # Armor Burst: Rapid Repair: Repair Duration/Capacitor fit.modules.filteredItemBoost( @@ -120,6 +120,9 @@ def handler(fit, module, context, **kwargs): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"), "crystalVolatilityChance", value, stackingPenalties=True) + if id == 60: # Skirmish Burst: Evasive Maneuvers: Agility + fit.ship.boostItemAttr("agility", value, stackingPenalties=True) + for x in xrange(1, 4): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x)) From df859a21325b11b215e55eb1d6a33c3f350929e3 Mon Sep 17 00:00:00 2001 From: blitzman Date: Fri, 9 Dec 2016 22:11:38 -0800 Subject: [PATCH 35/53] Fix for #883 (cherry picked from commit 753b5c5) --- eos/effects/iceharvestingdroneoperationdurationbonus.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/eos/effects/iceharvestingdroneoperationdurationbonus.py b/eos/effects/iceharvestingdroneoperationdurationbonus.py index 18622ccf8..7d8287cab 100644 --- a/eos/effects/iceharvestingdroneoperationdurationbonus.py +++ b/eos/effects/iceharvestingdroneoperationdurationbonus.py @@ -7,5 +7,6 @@ type = "passive" def handler(fit, src, context): - fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Operation"), "duration", - src.getModifiedItemAttr("rofBonus")) + lvl = src.level if "skill" in context else 1 + fit.drones.filteredItemBoost(lambda mod: mod.item.requiresSkill("Ice Harvesting Drone Operation"), + "duration", src.getModifiedItemAttr("rofBonus") * lvl) From 9cfa0748aca6d75a8ecf1e38aca7b6b0bbf8432a Mon Sep 17 00:00:00 2001 From: blitzman Date: Fri, 9 Dec 2016 22:27:17 -0800 Subject: [PATCH 36/53] fix for #871 (cherry picked from commit 70af2b0) --- eos/effects/shipmodesmallmissiledamagepostdiv.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/eos/effects/shipmodesmallmissiledamagepostdiv.py b/eos/effects/shipmodesmallmissiledamagepostdiv.py index 7321f4848..406d7d92b 100644 --- a/eos/effects/shipmodesmallmissiledamagepostdiv.py +++ b/eos/effects/shipmodesmallmissiledamagepostdiv.py @@ -8,6 +8,9 @@ type = "passive" def handler(fit, module, context): types = ("thermal", "em", "explosive", "kinetic") for type in types: - fit.modules.filteredChargeBoost( - lambda mod: mod.charge.requiresSkill("Rockets") or mod.charge.requiresSkill("Light Missiles"), - "{}Damage".format(type), 1 / module.getModifiedItemAttr("modeDamageBonusPostDiv")) + fit.modules.filteredChargeMultiply(lambda mod: mod.charge.requiresSkill("Rockets") or + mod.charge.requiresSkill("Light Missiles"), + "{}Damage".format(type), + 1 / module.getModifiedItemAttr("modeDamageBonusPostDiv"), + stackingPenalties = True, + penaltyGroup="postDiv") From 57edc32a4bfcb4acf91474b293d9343f6e748e82 Mon Sep 17 00:00:00 2001 From: blitzman Date: Fri, 9 Dec 2016 23:04:51 -0800 Subject: [PATCH 37/53] Clean up new tactical mode stats stuff (cherry picked from commit c9bc234) --- eos/saveddata/mode.py | 5 ----- gui/builtinContextMenus/itemRemove.py | 3 +-- gui/builtinViews/fittingView.py | 20 ++++++-------------- gui/contextMenu.py | 5 ----- 4 files changed, 7 insertions(+), 26 deletions(-) diff --git a/eos/saveddata/mode.py b/eos/saveddata/mode.py index d505807b9..2b3108a9d 100644 --- a/eos/saveddata/mode.py +++ b/eos/saveddata/mode.py @@ -53,8 +53,3 @@ class Mode(ItemAttrShortcut, HandledItem): for effect in self.item.effects.itervalues(): if effect.runTime == runTime and effect.activeByDefault: effect.handler(fit, self, context=("module",)) - - def getValidCharges(self): - # Modes don't have charges, but it is queried for so return nothing. - validCharges = set() - return validCharges diff --git a/gui/builtinContextMenus/itemRemove.py b/gui/builtinContextMenus/itemRemove.py index 3beeb56b9..400890b1a 100644 --- a/gui/builtinContextMenus/itemRemove.py +++ b/gui/builtinContextMenus/itemRemove.py @@ -15,8 +15,7 @@ class ItemRemove(ContextMenu): "boosterItem", "projectedModule", "projectedCharge", "cargoItem", "projectedFit", "projectedDrone", - "fighterItem", "projectedFighter", - "fittingMode",) + "fighterItem", "projectedFighter") def getText(self, itmContext, selection): return "Remove {0}".format(itmContext if itmContext is not None else "Item") diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 0733a8054..f0aab0b7d 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -25,7 +25,7 @@ import gui.display as d from gui.contextMenu import ContextMenu import gui.shipBrowser import gui.multiSwitch -from eos.types import Slot, Rack, Module +from eos.types import Slot, Rack, Module, Mode from gui.builtinViewColumns.state import State from gui.bitmapLoader import BitmapLoader import gui.builtinViews.emptyView @@ -336,7 +336,7 @@ class FittingView(d.Display): def removeItem(self, event): row, _ = self.HitTest(event.Position) - if row != -1 and row not in self.blanks: + if row != -1 and row not in self.blanks and isinstance(self.mods[row], Module): col = self.getColumn(event.Position) if col != self.getColIndex(State): self.removeModule(self.mods[row]) @@ -347,12 +347,7 @@ class FittingView(d.Display): def removeModule(self, module): sFit = Fit.getInstance() fit = sFit.getFit(self.activeFitID) - try: - populate = sFit.removeModule(self.activeFitID, fit.modules.index(module)) - except ValueError: - # This module isn't in our list of modules, don't remove anything. Likely a special snowflake. - logger.debug("Failed attempt to remove %s from fit" % module.item.name) - populate = None + populate = sFit.removeModule(self.activeFitID, fit.modules.index(module)) if populate is not None: self.slotsChanged() @@ -502,20 +497,17 @@ class FittingView(d.Display): mod = self.mods[self.GetItemData(sel)] # Test if this is a mode, which is a special snowflake of a Module - if hasattr(mod, "_Mode__item"): + if isinstance(mod, Mode): srcContext = "fittingMode" - # Skip the normal processing - mod.isEmpty = True - itemContext = sMkt.getCategoryByItem(mod.item).name + itemContext = "Tactical Mode" fullContext = (srcContext, itemContext) if not srcContext in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) selection.append(mod) - - if not mod.isEmpty: + elif not mod.isEmpty: srcContext = "fittingModule" itemContext = sMkt.getCategoryByItem(mod.item).name fullContext = (srcContext, itemContext) diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 83a260414..520ad8065 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -71,11 +71,6 @@ class ContextMenu(object): amount += 1 texts = m.getText(itemContext, selection) - # Check the selected item to see if it has special reasons for not showing the menu item - if srcContext == "fittingMode" and texts == "Remove Module": - # Don't show remove for modes, these are special modules that cannot be removed - continue - if isinstance(texts, basestring): texts = (texts,) From 4186e19c65d4ebe2b704f08dfd21c2d62e9d8b05 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sat, 10 Dec 2016 16:43:34 -0800 Subject: [PATCH 38/53] bump stable (cherry picked from commit e0e7478) --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index 3e2df676a..972786d3a 100644 --- a/config.py +++ b/config.py @@ -19,7 +19,7 @@ saveInRoot = False # Version data version = "1.25.1" -tag = "git" +tag = "Stable" expansionName = "Ascension" expansionVersion = "1.9" evemonMinVersion = "4081" From 9df87f61cdcbc45aa6972ca906e0d068fc73938e Mon Sep 17 00:00:00 2001 From: blitzman Date: Sun, 11 Dec 2016 11:18:48 -0800 Subject: [PATCH 39/53] bump dev (cherry picked from commit 2b3e646) --- config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py b/config.py index 972786d3a..611426672 100644 --- a/config.py +++ b/config.py @@ -18,8 +18,8 @@ debug = False saveInRoot = False # Version data -version = "1.25.1" -tag = "Stable" +version = "1.25.2" +tag = "git" expansionName = "Ascension" expansionVersion = "1.9" evemonMinVersion = "4081" From eccf405ba82f43c085e92797872a705e5d9b8946 Mon Sep 17 00:00:00 2001 From: Stefan Dresselhaus Date: Sun, 11 Dec 2016 11:25:55 -0800 Subject: [PATCH 40/53] fixed parameters in call when adding neuts to structures. (cherry picked from commit 4f77dff) --- eos/effects/structureenergyneutralizerfalloff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/effects/structureenergyneutralizerfalloff.py b/eos/effects/structureenergyneutralizerfalloff.py index 8b6bf5846..673630a6b 100644 --- a/eos/effects/structureenergyneutralizerfalloff.py +++ b/eos/effects/structureenergyneutralizerfalloff.py @@ -10,4 +10,4 @@ def handler(fit, container, context): and container.state >= State.ACTIVE) or hasattr(container, "amountActive")): amount = container.getModifiedItemAttr("energyNeutralizerAmount") time = container.getModifiedItemAttr("duration") - fit.addDrain(time, amount, 0) + fit.addDrain(container, time, amount, 0) From bb216aa6edb1d044139620fb64f243737153128b Mon Sep 17 00:00:00 2001 From: Stefan Dresselhaus Date: Sun, 11 Dec 2016 12:56:17 -0800 Subject: [PATCH 41/53] wrong index in tuple. Array looks normally so: (RunTime, Value, etc., etc.) ("normal", -10, ..., ...) I guess "RunTime" got added and this is an artifact as it is rarely called. Fixes Orca-Mining-Boost onto itself and other ships. (cherry picked from commit 0ba88d0) --- eos/saveddata/fit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 5ca38dba0..7138c8ca3 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -453,7 +453,7 @@ class Fit(object): # (abs is old method, ccp now provides the aggregate function in their data) print "Add command bonus: ", warfareBuffID, " - value: ", value - if warfareBuffID not in self.commandBonuses or abs(self.commandBonuses[warfareBuffID][0]) < abs(value): + if warfareBuffID not in self.commandBonuses or abs(self.commandBonuses[warfareBuffID][1]) < abs(value): self.commandBonuses[warfareBuffID] = (runTime, value, module, effect) def __runCommandBoosts(self, runTime="normal"): From 0d44cbf74af86ece2bbc464148b67245dc58f622 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sun, 11 Dec 2016 20:03:09 -0800 Subject: [PATCH 42/53] clean up path stuff (cherry picked from commit d3e360b) --- config.py | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/config.py b/config.py index 611426672..4115e04b2 100644 --- a/config.py +++ b/config.py @@ -143,31 +143,16 @@ def defPaths(customSavePath): eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False" -def getPyfaPath(Append=None): +def getPyfaPath(append=None): base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] root = os.path.dirname(os.path.realpath(os.path.abspath(base))) - if type(root) == str: # leave unicode ones alone - try: - root = root.decode('utf8') - except UnicodeDecodeError: - root = root.decode('windows-1252') + return _getPath(root, append) - if not Append: - return root - - if type(root) == str: # leave unicode ones alone - try: - path = os.path.abspath(os.path.join(root, Append)).decode('utf8') - except UnicodeDecodeError: - path = os.path.abspath(os.path.join(root, Append)).decode('windows-1252') - else: - path = os.path.abspath(os.path.join(root, Append)) - - return path - - -def getSavePath(Append=None): +def getSavePath(append=None): root = os.path.expanduser(os.path.join("~", ".pyfa")) + return _getPath(root, append) + +def _getPath(root, Append=None): if type(root) == str: # leave unicode ones alone try: root = root.decode('utf8') From 200ff5a1a490301956aa31f35c1155bb90c7cfb3 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sun, 11 Dec 2016 21:24:50 -0800 Subject: [PATCH 43/53] Fix titan command effects (cherry picked from commit 7875c2a) --- eos/effects/chargebonuswarfarecharge.py | 8 +------ eos/effects/moduletitaneffectgenerator.py | 29 ++++++++++++----------- eos/saveddata/fit.py | 6 +---- eos/saveddata/module.py | 5 +++- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/eos/effects/chargebonuswarfarecharge.py b/eos/effects/chargebonuswarfarecharge.py index e04ebe6f0..139a1a116 100644 --- a/eos/effects/chargebonuswarfarecharge.py +++ b/eos/effects/chargebonuswarfarecharge.py @@ -17,11 +17,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - print "In chargeBonusWarfareEffect, context: ", context - def runEffect(id, value): - print "RUN EFFECT: ", fit, - if id == 10: # Shield Burst: Shield Harmonizing: Shield Resistance for damageType in ("Em", "Explosive", "Thermal", "Kinetic"): fit.ship.boostItemAttr("shield%sDamageResonance" % damageType, value) @@ -127,11 +123,9 @@ def handler(fit, module, context, **kwargs): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) - print "Buff ID: ", id, " value: ", value + if id: if 'commandRun' not in context: - print "Add buffID", id, " to ", fit fit.addCommandBonus(id, value, module, kwargs['effect']) elif kwargs['warfareBuffID'] is not None and kwargs['warfareBuffID'] == id: - print "Running buffID ", kwargs['warfareBuffID'], " on ", fit runEffect(kwargs['warfareBuffID'], value) diff --git a/eos/effects/moduletitaneffectgenerator.py b/eos/effects/moduletitaneffectgenerator.py index df371813f..1e5868587 100644 --- a/eos/effects/moduletitaneffectgenerator.py +++ b/eos/effects/moduletitaneffectgenerator.py @@ -13,43 +13,44 @@ def handler(fit, module, context, **kwargs): if id == 40: # Avatar Effect Generator : Kinetic resistance bonus for attr in ("armorKineticDamageResonance", "shieldKineticDamageResonance", "hullKineticDamageResonance"): - fit.ship.boostItemAttr(attr, value, stackingPenalties=True) + fit.ship.boostItemAttr(attr, value) - if id == 41: # Erebus Effect Generator : Armor HP bonus + if id == 49: # Avatar Effect Generator : EM resistance penalty + for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "hullEmDamageResonance"): + fit.ship.boostItemAttr(attr, value) + + if id == 42: # Erebus Effect Generator : Armor HP bonus fit.ship.boostItemAttr("armorHP", value, stackingPenalties=True) - if id == 42: # Leviathan Effect Generator : Shield HP bonus - fit.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True) - if id == 43: # Erebus Effect Generator : Explosive resistance bonus for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "hullExplosiveDamageResonance"): - fit.ship.boostItemAttr(attr, value, stackingPenalties=True) + fit.ship.boostItemAttr(attr, value) if id == 44: # Erebus Effect Generator : Thermal resistance penalty for attr in ("armorThermalDamageResonance", "shieldThermalDamageResonance", "hullThermalDamageResonance"): - fit.ship.boostItemAttr(attr, value, stackingPenalties=True) + fit.ship.boostItemAttr(attr, value) if id == 45: # Ragnarok Effect Generator : Signature Radius bonus fit.ship.boostItemAttr("signatureRadius", value, stackingPenalties=True) if id == 46: # Ragnarok Effect Generator : Thermal resistance bonus for attr in ("armorThermalDamageResonance", "shieldThermalDamageResonance", "hullThermalDamageResonance"): - fit.ship.boostItemAttr(attr, value, stackingPenalties=True) + fit.ship.boostItemAttr(attr, value) if id == 47: # Ragnarok Effect Generator : Explosive resistance penaly for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "hullExplosiveDamageResonance"): - fit.ship.boostItemAttr(attr, value, stackingPenalties=True) + fit.ship.boostItemAttr(attr, value) if id == 48: # Leviathan Effect Generator : Shield HP bonus fit.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True) if id == 49: # Leviathan Effect Generator : EM resistance bonus for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "hullEmDamageResonance"): - fit.ship.boostItemAttr(attr, value, stackingPenalties=True) + fit.ship.boostItemAttr(attr, value) if id == 50: # Leviathan Effect Generator : Kinetic resistance penalty for attr in ("armorKineticDamageResonance", "shieldKineticDamageResonance", "hullKineticDamageResonance"): - fit.ship.boostItemAttr(attr, value, stackingPenalties=True) + fit.ship.boostItemAttr(attr, value) if id == 51: # Avatar Effect Generator : Velocity penalty fit.ship.boostItemAttr("maxVelocity", value, stackingPenalties=True) @@ -68,9 +69,9 @@ def handler(fit, module, context, **kwargs): stackingPenalties=True) for x in xrange(1, 4): - if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): - value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x)) - id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) + if module.getModifiedItemAttr("warfareBuff{}ID".format(x)): + value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) + id = module.getModifiedItemAttr("warfareBuff{}ID".format(x)) if id: if 'commandRun' not in context: diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 7138c8ca3..cd4615170 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -451,8 +451,6 @@ class Fit(object): # oh fuck this is so janky # @todo should we pass in min/max to this function, or is abs okay? # (abs is old method, ccp now provides the aggregate function in their data) - print "Add command bonus: ", warfareBuffID, " - value: ", value - if warfareBuffID not in self.commandBonuses or abs(self.commandBonuses[warfareBuffID][1]) < abs(value): self.commandBonuses[warfareBuffID] = (runTime, value, module, effect) @@ -516,12 +514,10 @@ class Fit(object): eos.db.saveddata_session.delete(self) if self.commandFits and not withBoosters: - print "Calculatate command fits and apply to fit" for fit in self.commandFits: if self == fit: - print "nope" continue - print "calculating ", fit + fit.calculateModifiedAttributes(self, True) # # for thing in chain(fit.modules, fit.implants, fit.character.skills, (fit.ship,)): diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index f72431a00..6f6af24f9 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -658,7 +658,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, l (effect.isType("active") and self.state >= State.ACTIVE)) \ and ((projected and effect.isType("projected")) or not projected) \ and ((gang and effect.isType("gang")) or not gang): - effect.handler(fit, self, context) + try: + effect.handler(fit, self, context, effect=effect) + except: + effect.handler(fit, self, context) @property def cycleTime(self): From 37f8253836798cb44e6859a93e884493d36a01a6 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sun, 11 Dec 2016 21:27:36 -0800 Subject: [PATCH 44/53] oops (cherry picked from commit 0a28fb6) --- eos/effects/moduletitaneffectgenerator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/effects/moduletitaneffectgenerator.py b/eos/effects/moduletitaneffectgenerator.py index 1e5868587..0b705a438 100644 --- a/eos/effects/moduletitaneffectgenerator.py +++ b/eos/effects/moduletitaneffectgenerator.py @@ -15,7 +15,7 @@ def handler(fit, module, context, **kwargs): for attr in ("armorKineticDamageResonance", "shieldKineticDamageResonance", "hullKineticDamageResonance"): fit.ship.boostItemAttr(attr, value) - if id == 49: # Avatar Effect Generator : EM resistance penalty + if id == 41: # Avatar Effect Generator : EM resistance penalty for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "hullEmDamageResonance"): fit.ship.boostItemAttr(attr, value) From 0ebb992354c5c96caed4b04d807fb18b84fdc700 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sun, 11 Dec 2016 21:49:47 -0800 Subject: [PATCH 45/53] more clean up (cherry picked from commit f198ff1) --- eos/saveddata/fit.py | 28 ---------------------------- service/fit.py | 2 +- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index cd4615170..f031f4c5b 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -136,7 +136,6 @@ class Fit(object): self.__capRecharge = None self.__calculatedTargets = [] self.factorReload = False - self.fleet = None self.boostsFits = set() self.gangBoosts = None self.ecmProjectedStr = 1 @@ -473,14 +472,6 @@ class Fit(object): self.register(thing) effect.handler(self, thing, context, warfareBuffID=warfareBuffID) - # if effect.isType("offline") or (effect.isType("passive") and thing.state >= State.ONLINE) or \ - # (effect.isType("active") and thing.state >= State.ACTIVE): - # # Run effect, and get proper bonuses applied - # try: - # self.register(thing) - # effect.handler(self, thing, context) - # except: - # pass else: # Run effect, and get proper bonuses applied try: @@ -506,7 +497,6 @@ class Fit(object): # Don't inspect this, we genuinely want to reassign self # noinspection PyMethodFirstArgAssignment self = copy.deepcopy(self) - self.fleet = copied.fleet logger.debug("Handling self projection - making shadow copy of fit. %r => %r", copied, self) # we delete the fit because when we copy a fit, flush() is # called to properly handle projection updates. However, we do @@ -519,24 +509,6 @@ class Fit(object): continue fit.calculateModifiedAttributes(self, True) - # - # for thing in chain(fit.modules, fit.implants, fit.character.skills, (fit.ship,)): - # if thing.item is None: - # continue - # for effect in thing.item.effects.itervalues(): - # # And check if it actually has gang boosting effects - # if effect.isType("gang"): - # effect.handler(self, thing, ("commandRun")) - - # if self.fleet is not None and withBoosters is True: - # logger.debug("Fleet is set, gathering gang boosts") - # - # self.gangBoosts = self.fleet.recalculateLinear(withBoosters=withBoosters) - # - # timer.checkpoint("Done calculating gang boosts for %r"%self) - - # elif self.fleet is None: - # self.gangBoosts = None # If we're not explicitly asked to project fit onto something, # set self as target fit diff --git a/service/fit.py b/service/fit.py index 204b8df32..6b4fe7813 100644 --- a/service/fit.py +++ b/service/fit.py @@ -199,7 +199,7 @@ class Fit(object): self.recalc(fit, withBoosters=True) def getFit(self, fitID, projected=False, basic=False): - ''' Gets fit from database. + ''' Gets fit from database Projected is a recursion flag that is set to reduce recursions into projected fits Basic is a flag to simply return the fit without any other processing From 956fa7a8b71af8b533e390c999cda7470fcc157c Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Wed, 14 Dec 2016 13:06:13 -0800 Subject: [PATCH 46/53] Fixing some issues due to import cleanup --- eos/effectHandlerHelpers.py | 7 +++++++ eos/saveddata/module.py | 7 ------- gui/mainFrame.py | 3 +-- service/market.py | 18 ++++++++---------- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py index 09ac4500b..c231f1519 100644 --- a/eos/effectHandlerHelpers.py +++ b/eos/effectHandlerHelpers.py @@ -159,6 +159,13 @@ class HandledModuleList(HandledList): for i in xrange(oldPos, len(self)): self[i].position -= 1 + def toDummy(self, index): + mod = self[index] + if not mod.isEmpty: + dummy = eos.types.Module.buildEmpty(mod.slot) + dummy.position = index + self[index] = dummy + def toModule(self, index, mod): mod.position = index self[index] = mod diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 6f6af24f9..d20efad5f 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -132,13 +132,6 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, l self.__chargeModifiedAttributes.original = self.__charge.attributes self.__chargeModifiedAttributes.overrides = self.__charge.overrides - def toDummy(self, index): - mod = self[index] - if not mod.isEmpty: - dummy = Module.buildEmpty(mod.slot) - dummy.position = index - self[index] = dummy - @classmethod def buildEmpty(cls, slot): empty = Module(None) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 4c7062f5a..25487a27d 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -50,9 +50,8 @@ from gui.preferenceDialog import PreferenceDialog from gui.graphFrame import GraphFrame from gui.copySelectDialog import CopySelectDialog from gui.utils.clipboard import toClipboard, fromClipboard -from gui.fleetBrowser import FleetBrowser # Noqa - TODO: unsure if this is needed here from gui.updateDialog import UpdateDialog -from gui.builtinViews import * # Noqa - TODO: unsure if this is needed here +from gui.builtinViews import * # TODO: unsure if this is needed here from gui import graphFrame from service.settings import SettingsProvider diff --git a/service/market.py b/service/market.py index 8bd49e9cd..d9b132377 100644 --- a/service/market.py +++ b/service/market.py @@ -28,7 +28,7 @@ from sqlalchemy.sql import or_ import config import eos.db # TODO: Find out what this is. There is no conversions -# from service import conversions +from service import conversions from service.settings import SettingsProvider from service.price import Price @@ -410,15 +410,13 @@ class Market(): item = identity elif isinstance(identity, int): item = eos.db.getItem(identity, *args, **kwargs) - # TODO: Find out what this is. There is no conversions - """ - # Unindent 1 tab - elif isinstance(identity, basestring): - # We normally lookup with string when we are using import/export - # features. Check against overrides - identity = conversions.all.get(identity, identity) - item = eos.db.getItem(identity, *args, **kwargs) - """ + # TODO: Find out what this is. There is no conversions + elif isinstance(identity, basestring): + # We normally lookup with string when we are using import/export + # features. Check against overrides + identity = conversions.all.get(identity, identity) + item = eos.db.getItem(identity, *args, **kwargs) + elif isinstance(identity, float): id = int(identity) item = eos.db.getItem(id, *args, **kwargs) From 658a87cbc0ee05571b60ffc3f02e647baf9729f8 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Dec 2016 12:10:03 -0800 Subject: [PATCH 47/53] Single line change to fix most (all??) of the problems --- eos/saveddata/module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index d20efad5f..4778be41e 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -64,7 +64,7 @@ class Hardpoint(Enum): TURRET = 2 -class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, list): +class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): """An instance of this class represents a module together with its charge and modified attributes""" DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") MINING_ATTRIBUTES = ("miningAmount",) From 4fb07cc1d0fb6d7021b7991fce0a33c2f27e1d90 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Dec 2016 12:43:19 -0800 Subject: [PATCH 48/53] Bunch of pep8 and inspection cleanup --- config.py | 8 +-- eos/db/saveddata/queries.py | 3 + ...itebonuscommandshipinformationhiddencs3.py | 2 + eos/effects/energydestabilizationnew.py | 3 +- eos/effects/remotehullrepair.py | 3 +- .../shipmodesmallmissiledamagepostdiv.py | 2 +- .../structureenergyneutralizerfalloff.py | 10 ++-- .../structuremoduleeffectstasiswebifier.py | 3 +- ...samarrdefensiveinformationwarfarehidden.py | 2 + ...aldaridefensiveinformationwarfarehidden.py | 2 + ...llentedefensiveinformationwarfarehidden.py | 2 + eos/effects/techtwocommandburstbonus.py | 1 + gui/builtinViews/fittingView.py | 3 +- gui/mainFrame.py | 2 +- service/attribute.py | 4 +- service/character.py | 4 +- service/conversions/releaseMar2016.py | 1 - service/crest.py | 7 ++- service/eveapi.py | 24 ++++---- service/fit.py | 5 +- service/implantSet.py | 4 +- service/market.py | 60 +++++++++---------- service/port.py | 42 ++++++------- service/server.py | 1 + 24 files changed, 105 insertions(+), 93 deletions(-) diff --git a/config.py b/config.py index 4115e04b2..33bb99046 100644 --- a/config.py +++ b/config.py @@ -44,11 +44,6 @@ class StreamToLogger(object): From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ """ - def __init__(self, logger, log_level=logging.INFO): - self.logger = logger - self.log_level = log_level - self.linebuf = '' - def write(self, buf): for line in buf.rstrip().splitlines(): self.logger.log(self.log_level, line.rstrip()) @@ -61,7 +56,6 @@ def isFrozen(): return False - def __createDirs(path): if not os.path.exists(path): os.makedirs(path) @@ -148,10 +142,12 @@ def getPyfaPath(append=None): root = os.path.dirname(os.path.realpath(os.path.abspath(base))) return _getPath(root, append) + def getSavePath(append=None): root = os.path.expanduser(os.path.join("~", ".pyfa")) return _getPath(root, append) + def _getPath(root, Append=None): if type(root) == str: # leave unicode ones alone try: diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 84633b5a8..7e1948884 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -208,6 +208,7 @@ def getFit(lookfor, eager=None): return fit + def getFitsWithShip(shipID, ownerID=None, where=None, eager=None): """ Get all the fits using a certain ship. @@ -284,6 +285,7 @@ def getFitList(eager=None): return fits + @cachedQuery(Price, 1, "typeID") def getPrice(typeID): if isinstance(typeID, int): @@ -407,6 +409,7 @@ def searchFits(nameLike, where=None, eager=None): return fits + def getProjectedFits(fitID): if isinstance(fitID, int): with sd_lock: diff --git a/eos/effects/elitebonuscommandshipinformationhiddencs3.py b/eos/effects/elitebonuscommandshipinformationhiddencs3.py index 27148ebec..eeee692ed 100644 --- a/eos/effects/elitebonuscommandshipinformationhiddencs3.py +++ b/eos/effects/elitebonuscommandshipinformationhiddencs3.py @@ -1,5 +1,7 @@ # Not used by any item type = "passive" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", module.getModifiedItemAttr("eliteBonusCommandShips3"), skill="Command Ships") diff --git a/eos/effects/energydestabilizationnew.py b/eos/effects/energydestabilizationnew.py index 6cf82afa5..52a6b40ee 100644 --- a/eos/effects/energydestabilizationnew.py +++ b/eos/effects/energydestabilizationnew.py @@ -5,8 +5,7 @@ type = "active", "projected" def handler(fit, src, context): - if "projected" in context and ( - (hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): + if "projected" in context and ((hasattr(src, "state") and src.state >= State.ACTIVE) or hasattr(src, "amountActive")): multiplier = src.amountActive if hasattr(src, "amountActive") else 1 amount = src.getModifiedItemAttr("energyNeutralizerAmount") time = src.getModifiedItemAttr("duration") diff --git a/eos/effects/remotehullrepair.py b/eos/effects/remotehullrepair.py index 099e0c2ac..508b5684e 100644 --- a/eos/effects/remotehullrepair.py +++ b/eos/effects/remotehullrepair.py @@ -4,7 +4,8 @@ runTime = "late" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return bonus = module.getModifiedItemAttr("structureDamageAmount") duration = module.getModifiedItemAttr("duration") / 1000.0 fit.extraAttributes.increase("hullRepair", bonus / duration) diff --git a/eos/effects/shipmodesmallmissiledamagepostdiv.py b/eos/effects/shipmodesmallmissiledamagepostdiv.py index 406d7d92b..92c8378b1 100644 --- a/eos/effects/shipmodesmallmissiledamagepostdiv.py +++ b/eos/effects/shipmodesmallmissiledamagepostdiv.py @@ -12,5 +12,5 @@ def handler(fit, module, context): mod.charge.requiresSkill("Light Missiles"), "{}Damage".format(type), 1 / module.getModifiedItemAttr("modeDamageBonusPostDiv"), - stackingPenalties = True, + stackingPenalties=True, penaltyGroup="postDiv") diff --git a/eos/effects/structureenergyneutralizerfalloff.py b/eos/effects/structureenergyneutralizerfalloff.py index 673630a6b..e383f523b 100644 --- a/eos/effects/structureenergyneutralizerfalloff.py +++ b/eos/effects/structureenergyneutralizerfalloff.py @@ -6,8 +6,8 @@ type = "active", "projected" def handler(fit, container, context): amount = 0 - if "projected" in context and ((hasattr(container, "state") - and container.state >= State.ACTIVE) or hasattr(container, "amountActive")): - amount = container.getModifiedItemAttr("energyNeutralizerAmount") - time = container.getModifiedItemAttr("duration") - fit.addDrain(container, time, amount, 0) + if "projected" in context: + if (hasattr(container, "state") and container.state >= State.ACTIVE) or hasattr(container, "amountActive"): + amount = container.getModifiedItemAttr("energyNeutralizerAmount") + time = container.getModifiedItemAttr("duration") + fit.addDrain(container, time, amount, 0) diff --git a/eos/effects/structuremoduleeffectstasiswebifier.py b/eos/effects/structuremoduleeffectstasiswebifier.py index dc4b6f046..0aa907fc6 100644 --- a/eos/effects/structuremoduleeffectstasiswebifier.py +++ b/eos/effects/structuremoduleeffectstasiswebifier.py @@ -3,6 +3,7 @@ type = "active", "projected" def handler(fit, module, context): - if "projected" not in context: return + if "projected" not in context: + return fit.ship.boostItemAttr("maxVelocity", module.getModifiedItemAttr("speedFactor"), stackingPenalties=True, remoteResists=True) diff --git a/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py b/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py index 9dfb9ad19..7dd70fefa 100644 --- a/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py +++ b/eos/effects/subsystembonusamarrdefensiveinformationwarfarehidden.py @@ -1,5 +1,7 @@ # Not used by any item type = "passive" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusAmarrDefensive"), skill="Amarr Defensive Systems") diff --git a/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py b/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py index f01b7efb7..0cf7a3ef8 100644 --- a/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py +++ b/eos/effects/subsystembonuscaldaridefensiveinformationwarfarehidden.py @@ -1,5 +1,7 @@ # Not used by any item type = "passive" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusCaldariDefensive"), skill="Caldari Defensive Systems") diff --git a/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py b/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py index 7a3f8205f..19b0cf02e 100644 --- a/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py +++ b/eos/effects/subsystembonusgallentedefensiveinformationwarfarehidden.py @@ -1,5 +1,7 @@ # Not used by any item type = "passive" + + def handler(fit, module, context): fit.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Information Command Specialist"), "commandBonusHidden", module.getModifiedItemAttr("subsystemBonusGallenteDefensive"), skill="Gallente Defensive Systems") diff --git a/eos/effects/techtwocommandburstbonus.py b/eos/effects/techtwocommandburstbonus.py index a85f78ab9..757513cef 100644 --- a/eos/effects/techtwocommandburstbonus.py +++ b/eos/effects/techtwocommandburstbonus.py @@ -2,6 +2,7 @@ type = "passive" runTime = "late" + def handler(fit, module, context): for x in xrange(1, 4): module.boostChargeAttr("warfareBuff{}Multiplier".format(x), module.getModifiedItemAttr("commandBurstStrengthBonus")) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index f0aab0b7d..475a425af 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -502,7 +502,7 @@ class FittingView(d.Display): itemContext = "Tactical Mode" fullContext = (srcContext, itemContext) - if not srcContext in tuple(fCtxt[0] for fCtxt in contexts): + if srcContext not in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) selection.append(mod) @@ -514,7 +514,6 @@ class FittingView(d.Display): if srcContext not in tuple(fCtxt[0] for fCtxt in contexts): contexts.append(fullContext) - if mod.charge is not None: srcContext = "fittingCharge" itemContext = sMkt.getCategoryByItem(mod.charge).name diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 25487a27d..6d94c970b 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -51,7 +51,7 @@ from gui.graphFrame import GraphFrame from gui.copySelectDialog import CopySelectDialog from gui.utils.clipboard import toClipboard, fromClipboard from gui.updateDialog import UpdateDialog -from gui.builtinViews import * # TODO: unsure if this is needed here +from gui.builtinViews import * # TODO: unsure if this is needed here from gui import graphFrame from service.settings import SettingsProvider diff --git a/service/attribute.py b/service/attribute.py index 41d10b80a..ee4f43328 100644 --- a/service/attribute.py +++ b/service/attribute.py @@ -34,8 +34,8 @@ class Attribute(): if isinstance(identity, (int, basestring)): info = eos.db.getAttributeInfo(identity, eager=("icon", "unit")) elif isinstance(identity, (int, float)): - id = int(identity) - info = eos.db.getAttributeInfo(id, eager=("icon", "unit")) + id_ = int(identity) + info = eos.db.getAttributeInfo(id_, eager=("icon", "unit")) else: info = None return info diff --git a/service/character.py b/service/character.py index 3851c44bb..1e5385519 100644 --- a/service/character.py +++ b/service/character.py @@ -279,8 +279,8 @@ class Character(object): return (char.apiID or "", char.apiKey or "", char.defaultChar or "", chars or []) def apiEnabled(self, charID): - id, key, default, _ = self.getApiDetails(charID) - return id is not "" and key is not "" and default is not "" + id_, key, default, _ = self.getApiDetails(charID) + return id_ is not "" and key is not "" and default is not "" def apiCharList(self, charID, userID, apiKey): char = eos.db.getCharacter(charID) diff --git a/service/conversions/releaseMar2016.py b/service/conversions/releaseMar2016.py index da7591da5..65e26adb1 100644 --- a/service/conversions/releaseMar2016.py +++ b/service/conversions/releaseMar2016.py @@ -118,7 +118,6 @@ CONVERSIONS = { "'Full Duplex' Ballistic Targeting System": "'Full Duplex' Ballistic Control System", "'Kindred' Stabilization Actuator I": "'Kindred' Gyrostabilizer", "Process-Interruptive Warp Disruptor": "'Interruptive' Warp Disruptor", - "Multi Sensor Firewall": "'Firewall' Signal Amplifier", "'Inception' Target Painter I": "'Inception' Target Painter", "Citadel Torpedoes": "XL Torpedoes", "'Shady' ECCM - Gravimetric I": "'Shady' Sensor Booster", diff --git a/service/crest.py b/service/crest.py index 4af0ccb89..74c74d311 100644 --- a/service/crest.py +++ b/service/crest.py @@ -118,9 +118,9 @@ class Crest(): return chars2 def getCrestCharacter(self, charID): - ''' + """ Get character, and modify to include the eve connection - ''' + """ if self.settings.get('mode') == CrestModes.IMPLICIT: if self.implicitCharacter.ID != charID: raise ValueError("CharacterID does not match currently logged in character.") @@ -141,7 +141,8 @@ class Crest(): return char.eve.get('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID)) def postFitting(self, charID, json): - # @todo: new fitting ID can be recovered from Location header, ie: Location -> https://api-sisi.testeveonline.com/characters/1611853631/fittings/37486494/ + # @todo: new fitting ID can be recovered from Location header, + # ie: Location -> https://api-sisi.testeveonline.com/characters/1611853631/fittings/37486494/ char = self.getCrestCharacter(charID) return char.eve.post('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID), data=json) diff --git a/service/eveapi.py b/service/eveapi.py index d1c9835ae..67a238f47 100644 --- a/service/eveapi.py +++ b/service/eveapi.py @@ -949,28 +949,28 @@ class FilterRowset(object): # - Each key maps to a Rowset, containing only the rows where the value # of the column this FilterRowset was made on matches the key. - def __init__(self, cols=None, rows=None, key=None, key2=None, dict=None): - if dict is not None: - self._items = items = dict + def __init__(self, cols=None, rows=None, key=None, key2=None, dict_=None): + if dict_ is not None: + self._items = items = dict_ elif cols is not None: self._items = items = {} idfield = cols.index(key) if not key2: for row in rows: - id = row[idfield] - if id in items: - items[id].append(row) + id_ = row[idfield] + if id_ in items: + items[id_].append(row) else: - items[id] = [row] + items[id_] = [row] else: idfield2 = cols.index(key2) for row in rows: - id = row[idfield] - if id in items: - items[id][row[idfield2]] = row + id_ = row[idfield] + if id_ in items: + items[id_][row[idfield2]] = row else: - items[id] = {row[idfield2]: row} + items[id_] = {row[idfield2]: row} self._cols = cols self.key = key @@ -987,7 +987,7 @@ class FilterRowset(object): self.__iter__ = items.__iter__ def copy(self): - return FilterRowset(self._cols[:], None, self.key, self.key2, dict=copy.deepcopy(self._items)) + return FilterRowset(self._cols[:], None, self.key, self.key2, dict_=copy.deepcopy(self._items)) def get(self, key, default=_unspecified): try: diff --git a/service/fit.py b/service/fit.py index 6b4fe7813..797f31bf2 100644 --- a/service/fit.py +++ b/service/fit.py @@ -199,11 +199,12 @@ class Fit(object): self.recalc(fit, withBoosters=True) def getFit(self, fitID, projected=False, basic=False): - ''' Gets fit from database + """ + Gets fit from database Projected is a recursion flag that is set to reduce recursions into projected fits Basic is a flag to simply return the fit without any other processing - ''' + """ if fitID is None: return None fit = eos.db.getFit(fitID) diff --git a/service/implantSet.py b/service/implantSet.py index f7df17bab..f1da65aed 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -49,9 +49,9 @@ class ImplantSets(object): return eos.db.getImplantSet(setID).implants def addImplant(self, setID, itemID): - set = eos.db.getImplantSet(setID) + implant_set = eos.db.getImplantSet(setID) implant = es_Implant(eos.db.getItem(itemID)) - set.implants.append(implant) + implant_set.implants.append(implant) eos.db.commit() def removeImplant(self, setID, implant): diff --git a/service/market.py b/service/market.py index d9b132377..bf5eea5bc 100644 --- a/service/market.py +++ b/service/market.py @@ -60,13 +60,13 @@ class ShipBrowserWorkerThread(threading.Thread): sMkt = Market.getInstance() while True: try: - id, callback = queue.get() - set = cache.get(id) - if set is None: - set = sMkt.getShipList(id) - cache[id] = set + id_, callback = queue.get() + set_ = cache.get(id_) + if set_ is None: + set_ = sMkt.getShipList(id_) + cache[id_] = set_ - wx.CallAfter(callback, (id, set)) + wx.CallAfter(callback, (id_, set_)) except: pass finally: @@ -131,13 +131,13 @@ class SearchWorkerThread(threading.Thread): sMkt = Market.getInstance() if filterOn is True: # Rely on category data provided by eos as we don't hardcode them much in service - filter = or_(e_Category.name.in_(sMkt.SEARCH_CATEGORIES), e_Group.name.in_(sMkt.SEARCH_GROUPS)) + filter_ = or_(e_Category.name.in_(sMkt.SEARCH_CATEGORIES), e_Group.name.in_(sMkt.SEARCH_GROUPS)) elif filterOn: # filter by selected categories - filter = e_Category.name.in_(filterOn) + filter_ = e_Category.name.in_(filterOn) else: - filter = None + filter_ = None - results = eos.db.searchItems(request, where=filter, + results = eos.db.searchItems(request, where=filter_, join=(e_Item.group, e_Group.category), eager=("icon", "group.category", "metaGroup", "metaGroup.parent")) @@ -418,8 +418,8 @@ class Market(): item = eos.db.getItem(identity, *args, **kwargs) elif isinstance(identity, float): - id = int(identity) - item = eos.db.getItem(id, *args, **kwargs) + id_ = int(identity) + item = eos.db.getItem(id_, *args, **kwargs) else: raise TypeError("Need Item object, integer, float or string as argument") except: @@ -453,8 +453,8 @@ class Market(): elif isinstance(identity, (int, basestring)): category = eos.db.getCategory(identity, *args, **kwargs) elif isinstance(identity, float): - id = int(identity) - category = eos.db.getCategory(id, *args, **kwargs) + id_ = int(identity) + category = eos.db.getCategory(id_, *args, **kwargs) else: raise TypeError("Need Category object, integer, float or string as argument") return category @@ -466,8 +466,8 @@ class Market(): elif isinstance(identity, (int, basestring)): metaGroup = eos.db.getMetaGroup(identity, *args, **kwargs) elif isinstance(identity, float): - id = int(identity) - metaGroup = eos.db.getMetaGroup(id, *args, **kwargs) + id_ = int(identity) + metaGroup = eos.db.getMetaGroup(id_, *args, **kwargs) else: raise TypeError("Need MetaGroup object, integer, float or string as argument") return metaGroup @@ -477,8 +477,8 @@ class Market(): if isinstance(identity, eos.types.MarketGroup): marketGroup = identity elif isinstance(identity, (int, float)): - id = int(identity) - marketGroup = eos.db.getMarketGroup(id, *args, **kwargs) + id_ = int(identity) + marketGroup = eos.db.getMarketGroup(id_, *args, **kwargs) else: raise TypeError("Need MarketGroup object, integer or float as argument") return marketGroup @@ -522,8 +522,8 @@ class Market(): def getMetaGroupIdByItem(self, item, fallback=0): """Get meta group ID by item""" - id = getattr(self.getMetaGroupByItem(item), "ID", fallback) - return id + id_ = getattr(self.getMetaGroupByItem(item), "ID", fallback) + return id_ def getMarketGroupByItem(self, item, parentcheck=True): """Get market group by item, its ID or name""" @@ -607,7 +607,7 @@ class Market(): filter(lambda item: self.getPublicityByItem(item) and self.getGroupByItem(item) == group, groupItems)) return items - def getItemsByMarketGroup(self, mg, vars=True): + def getItemsByMarketGroup(self, mg, vars_=True): """Get items in the given market group""" result = set() # Get items from eos market group @@ -616,7 +616,7 @@ class Market(): if mg.ID in self.ITEMS_FORCEDMARKETGROUP_R: forceditms = set(self.getItem(itmn) for itmn in self.ITEMS_FORCEDMARKETGROUP_R[mg.ID]) baseitms.update(forceditms) - if vars: + if vars_: parents = set() for item in baseitms: # Add one of the base market group items to result @@ -634,7 +634,7 @@ class Market(): else: result = baseitms # Get rid of unpublished items - result = set(filter(lambda item: self.getPublicityByItem(item), result)) + result = set(filter(lambda item_: self.getPublicityByItem(item_), result)) return result def marketGroupHasTypesCheck(self, mg): @@ -669,7 +669,7 @@ class Market(): elif self.marketGroupHasTypesCheck(mg): # Do not request variations to make process faster # Pick random item and use its icon - items = self.getItemsByMarketGroup(mg, vars=False) + items = self.getItemsByMarketGroup(mg, vars_=False) try: item = items.pop() except KeyError: @@ -707,8 +707,8 @@ class Market(): the ID, the name and the icon of the group """ root = set() - for id in self.ROOT_MARKET_GROUPS: - mg = self.getMarketGroup(id, eager="icon") + for id_ in self.ROOT_MARKET_GROUPS: + mg = self.getMarketGroup(id_, eager="icon") root.add(mg) return root @@ -728,14 +728,14 @@ class Market(): ship.race return ships - def getShipListDelayed(self, id, callback): + def getShipListDelayed(self, id_, callback): """Background version of getShipList""" - self.shipBrowserWorkerThread.queue.put((id, callback)) + self.shipBrowserWorkerThread.queue.put((id_, callback)) def searchShips(self, name): """Find ships according to given text pattern""" - filter = e_Category.name.in_(["Ship", "Structure"]) - results = eos.db.searchItems(name, where=filter, + filter_ = e_Category.name.in_(["Ship", "Structure"]) + results = eos.db.searchItems(name, where=filter_, join=(e_Item.group, e_Group.category), eager=("icon", "group.category", "metaGroup", "metaGroup.parent")) ships = set() diff --git a/service/port.py b/service/port.py index 471ee84ed..a59dc78e3 100644 --- a/service/port.py +++ b/service/port.py @@ -35,7 +35,7 @@ from service.fit import Fit import wx -from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel +from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel, Fighter from service.crest import Crest from service.market import Market @@ -83,8 +83,8 @@ class Port(object): if callback: # Pulse wx.CallAfter(callback, 1, "Processing file:\n%s" % path) - file = open(path, "r") - srcString = file.read() + file_ = open(path, "r") + srcString = file_.read() if len(srcString) == 0: # ignore blank files continue @@ -259,7 +259,7 @@ class Port(object): item = nested_dict() item['flag'] = INV_FLAG_FIGHTER item['quantity'] = fighter.amountActive - item['type']['href'] = "%sinventory/types/%d/"%(eve._authed_endpoint, fighter.item.ID) + item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, fighter.item.ID) item['type']['id'] = fighter.item.ID item['type']['name'] = fighter.item.name fit['items'].append(item) @@ -299,8 +299,8 @@ class Port(object): return "DNA", (cls.importDna(string),) @staticmethod - def importCrest(str): - fit = json.loads(str) + def importCrest(str_): + fit = json.loads(str_) sMkt = Market.getInstance() f = Fit() @@ -365,16 +365,16 @@ class Port(object): sMkt = Market.getInstance() ids = map(int, re.findall(r'\d+', string)) - for id in ids: + for id_ in ids: try: try: try: - Ship(sMkt.getItem(sMkt.getItem(id))) + Ship(sMkt.getItem(sMkt.getItem(id_))) except ValueError: - Citadel(sMkt.getItem(sMkt.getItem(id))) + Citadel(sMkt.getItem(sMkt.getItem(id_))) except ValueError: - Citadel(sMkt.getItem(id)) - string = string[string.index(str(id)):] + Citadel(sMkt.getItem(id_)) + string = string[string.index(str(id_)):] break except: pass @@ -389,10 +389,10 @@ class Port(object): f.ship = Citadel(sMkt.getItem(int(info[0]))) f.name = "{0} - DNA Imported".format(f.ship.item.name) except UnicodeEncodeError: - def logtransform(s): - if len(s) > 10: - return s[:10] + "..." - return s + def logtransform(s_): + if len(s_) > 10: + return s_[:10] + "..." + return s_ logger.exception("Couldn't import ship data %r", [logtransform(s) for s in info]) return None @@ -519,7 +519,7 @@ class Port(object): elif item.category.name == "Fighter": extraAmount = int(extraAmount) if extraAmount is not None else 1 fighterItem = Fighter(item) - if (extraAmount > fighterItem.fighterSquadronMaxSize): #Amount bigger then max fightergroup size + if (extraAmount > fighterItem.fighterSquadronMaxSize): # Amount bigger then max fightergroup size extraAmount = fighterItem.fighterSquadronMaxSize if fighterItem.fits(fit): fit.fighters.append(fighterItem) @@ -666,9 +666,10 @@ class Port(object): elif entityState == "Inactive": d.amountActive = 0 f.drones.append(d) - elif droneItem.category.name == "Fighter": # EFT saves fighter as drones + elif droneItem.category.name == "Fighter": # EFT saves fighter as drones ft = Fighter(droneItem) - ft.amount = int(droneAmount) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize + ft.amount = int(droneAmount) if ft.amount <= ft.fighterSquadronMaxSize \ + else ft.fighterSquadronMaxSize f.fighters.append(ft) else: continue @@ -805,7 +806,8 @@ class Port(object): f.drones.append(d) elif item.category.name == "Fighter": ft = Fighter(item) - ft.amount = int(hardware.getAttribute("qty")) if ft.amount <= ft.fighterSquadronMaxSize else ft.fighterSquadronMaxSize + ft.amount = int(hardware.getAttribute("qty")) if ft.amount <= ft.fighterSquadronMaxSize \ + else ft.fighterSquadronMaxSize f.fighters.append(ft) elif hardware.getAttribute("slot").lower() == "cargo": # although the eve client only support charges in cargo, third-party programs @@ -946,7 +948,7 @@ class Port(object): # `or 1` because some charges (ie scripts) are without qty charges[mod.chargeID] += mod.numCharges or 1 - for subsystem in sorted(subsystems, key=lambda mod: mod.getModifiedItemAttr("subSystemSlot")): + for subsystem in sorted(subsystems, key=lambda mod_: mod_.getModifiedItemAttr("subSystemSlot")): dna += ":{0};1".format(subsystem.itemID) for mod in mods: diff --git a/service/server.py b/service/server.py index 98bd138f1..268637515 100644 --- a/service/server.py +++ b/service/server.py @@ -10,6 +10,7 @@ from service.settings import CRESTSettings logger = logging.getLogger(__name__) +# noinspection PyPep8 HTML = ''' From f76f44e9f321a6a6fc74460946a294d4408e8dc8 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Dec 2016 15:20:29 -0800 Subject: [PATCH 49/53] Fix port references, imports, and add some logging --- gui/mainFrame.py | 9 ++++++--- service/port.py | 40 +++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 6d94c970b..f033fe32f 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -19,6 +19,7 @@ import sys import os.path +import logging import sqlalchemy import wx @@ -84,6 +85,8 @@ if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION print("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message) disableOverrideEditor = True +logger = logging.getLogger("pyfa.gui.mainFrame") + # dummy panel(no paint no erasebk) class PFPanel(wx.Panel): @@ -702,11 +705,11 @@ class MainFrame(wx.Frame): toClipboard(Port.exportMultiBuy(fit)) def importFromClipboard(self, event): - sFit = Fit.getInstance() + clipboard = fromClipboard() try: - fits = sFit.importFitFromBuffer(fromClipboard(), self.getActiveFit()) + fits = Port().importFitFromBuffer(clipboard, self.getActiveFit()) except: - pass + logger.error("Attempt to import failed:\n%s", clipboard) else: self._openAfterImport(fits) diff --git a/service/port.py b/service/port.py index a59dc78e3..4bf9b061c 100644 --- a/service/port.py +++ b/service/port.py @@ -31,11 +31,11 @@ from codecs import open import xml.parsers.expat from eos import db -from service.fit import Fit +from service.fit import Fit as svcFit import wx -from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel, Fighter +from eos.types import State, Slot, Module, Cargo, Ship, Drone, Implant, Booster, Citadel, Fighter, Fit from service.crest import Crest from service.market import Market @@ -77,6 +77,7 @@ class Port(object): returns """ defcodepage = locale.getpreferredencoding() + sFit = svcFit.getInstance() fits = [] for path in paths: @@ -148,9 +149,9 @@ class Port(object): numFits = len(fits) for i, fit in enumerate(fits): # Set some more fit attributes and save - fit.character = self.character - fit.damagePattern = self.pattern - fit.targetResists = self.targetResists + fit.character = sFit.character + fit.damagePattern = sFit.pattern + fit.targetResists = sFit.targetResists db.save(fit) IDs.append(fit.ID) if callback: # Pulse @@ -163,11 +164,12 @@ class Port(object): return True, fits def importFitFromBuffer(self, bufferStr, activeFit=None): + sFit = svcFit.getInstance() _, fits = Port.importAuto(bufferStr, activeFit=activeFit) for fit in fits: - fit.character = self.character - fit.damagePattern = self.pattern - fit.targetResists = self.targetResists + fit.character = sFit.character + fit.damagePattern = sFit.pattern + fit.targetResists = sFit.targetResists db.save(fit) return fits @@ -182,7 +184,7 @@ class Port(object): nested_dict = lambda: collections.defaultdict(nested_dict) fit = nested_dict() sCrest = Crest.getInstance() - sFit = Fit.getInstance() + sFit = svcFit.getInstance() eve = sCrest.eve @@ -352,7 +354,7 @@ class Port(object): continue # Recalc to get slot numbers correct for T3 cruisers - Fit.getInstance().recalc(f) + svcFit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -433,7 +435,7 @@ class Port(object): moduleList.append(m) # Recalc to get slot numbers correct for T3 cruisers - Fit.getInstance().recalc(f) + svcFit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -565,7 +567,7 @@ class Port(object): moduleList.append(m) # Recalc to get slot numbers correct for T3 cruisers - Fit.getInstance().recalc(fit) + svcFit.getInstance().recalc(fit) for m in moduleList: if m.fits(fit): @@ -752,7 +754,7 @@ class Port(object): moduleList.append(m) # Recalc to get slot numbers correct for T3 cruisers - Fit.getInstance().recalc(f) + svcFit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -837,7 +839,7 @@ class Port(object): continue # Recalc to get slot numbers correct for T3 cruisers - Fit.getInstance().recalc(f) + svcFit.getInstance().recalc(f) for module in moduleList: if module.fits(f): @@ -855,7 +857,7 @@ class Port(object): offineSuffix = " /OFFLINE" export = "[%s, %s]\n" % (fit.ship.item.name, fit.name) stuff = {} - sFit = Fit.getInstance() + sFit = svcFit.getInstance() for module in fit.modules: slot = module.slot if slot not in stuff: @@ -981,7 +983,7 @@ class Port(object): doc = xml.dom.minidom.Document() fittings = doc.createElement("fittings") doc.appendChild(fittings) - sFit = Fit.getInstance() + sFit = svcFit.getInstance() for i, fit in enumerate(fits): try: @@ -1064,7 +1066,7 @@ class Port(object): def exportMultiBuy(fit): export = "%s\n" % (fit.ship.item.name) stuff = {} - sFit = Fit.getInstance() + sFit = svcFit.getInstance() for module in fit.modules: slot = module.slot if slot not in stuff: @@ -1115,7 +1117,7 @@ class FitBackupThread(threading.Thread): def run(self): path = self.path - sFit = Fit.getInstance() + sFit = svcFit.getInstance() allFits = map(lambda x: x[0], sFit.getAllFits()) backedUpFits = sFit.exportXml(self.callback, *allFits) backupFile = open(path, "w", encoding="utf-8") @@ -1133,7 +1135,7 @@ class FitImportThread(threading.Thread): self.callback = callback def run(self): - sFit = Fit.getInstance() + sFit = svcFit.getInstance() success, result = sFit.importFitFromFiles(self.paths, self.callback) if not success: # there was an error during processing From c858fc2859132a5d2e67ff09b74dadd39afe599f Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Dec 2016 15:39:19 -0800 Subject: [PATCH 50/53] Remove an inline import. Fix a reference that is missing. --- eos/config.py | 2 ++ gui/mainFrame.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/eos/config.py b/eos/config.py index 63bf7695b..01d65cfe0 100644 --- a/eos/config.py +++ b/eos/config.py @@ -4,10 +4,12 @@ from os.path import realpath, join, dirname, abspath debug = False gamedataCache = True saveddataCache = True +gamedata_version = "" gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding()) saveddata_connectionstring = 'sqlite:///' + unicode( realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding()) + # Autodetect path, only change if the autodetection bugs out. path = dirname(unicode(__file__, sys.getfilesystemencoding())) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index f033fe32f..182bf3480 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -31,6 +31,8 @@ from wx.lib.wordwrap import wordwrap import config +from eos.config import gamedata_version + import gui.aboutData import gui.chromeTabs import gui.globalEvents as GE @@ -346,7 +348,6 @@ class MainFrame(wx.Frame): event.Skip() def ShowAboutBox(self, evt): - import eos.config v = sys.version_info info = wx.AboutDialogInfo() info.Name = "pyfa" @@ -357,7 +358,7 @@ class MainFrame(wx.Frame): "\n\t".join(gui.aboutData.credits) + "\n\nLicenses:\n\t" + "\n\t".join(gui.aboutData.licenses) + - "\n\nEVE Data: \t" + eos.config.gamedata_version + + "\n\nEVE Data: \t" + gamedata_version + "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + "\nwxPython: \t" + wx.__version__ + "\nSQLAlchemy: \t" + sqlalchemy.__version__, From d7c71f5d473a2e6954a1f43158fd3d7c667a52fd Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Dec 2016 16:17:10 -0800 Subject: [PATCH 51/53] fix some, missed import references. --- gui/mainFrame.py | 2 +- service/market.py | 27 +++++++++++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 182bf3480..86728c5c7 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -810,7 +810,7 @@ class MainFrame(wx.Frame): parent=self, style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME, ) - Port.backupFits(filePath, self.backupCallback) + Port().backupFits(filePath, self.backupCallback) self.progressDialog.ShowModal() def exportHtml(self, event): diff --git a/service/market.py b/service/market.py index bf5eea5bc..1e8eec29f 100644 --- a/service/market.py +++ b/service/market.py @@ -34,6 +34,9 @@ from service.price import Price from eos.gamedata import Category as e_Category, Group as e_Group, Item as e_Item +from eos.types import MarketGroup as types_MarketGroup, MetaGroup as types_MetaGroup, MetaType as types_MetaType, \ + Category as types_Category, Item as types_Item, Group as types_Group, Price as types_Price + try: from collections import OrderedDict except ImportError: @@ -131,14 +134,14 @@ class SearchWorkerThread(threading.Thread): sMkt = Market.getInstance() if filterOn is True: # Rely on category data provided by eos as we don't hardcode them much in service - filter_ = or_(e_Category.name.in_(sMkt.SEARCH_CATEGORIES), e_Group.name.in_(sMkt.SEARCH_GROUPS)) + filter_ = or_(types_Category.name.in_(sMkt.SEARCH_CATEGORIES), types_Group.name.in_(sMkt.SEARCH_GROUPS)) elif filterOn: # filter by selected categories - filter_ = e_Category.name.in_(filterOn) + filter_ = types_Category.name.in_(filterOn) else: filter_ = None results = eos.db.searchItems(request, where=filter_, - join=(e_Item.group, e_Group.category), + join=(types_Item.group, types_Group.category), eager=("icon", "group.category", "metaGroup", "metaGroup.parent")) items = set() @@ -185,6 +188,7 @@ class Market(): # Items' group overrides self.customGroups = set() # Limited edition ships + # TODO: Import Refactor - Figure out which group this needs :( self.les_grp = e_Group() self.les_grp.ID = -1 self.les_grp.name = "Limited Issue Ships" @@ -406,11 +410,12 @@ class Market(): def getItem(self, identity, *args, **kwargs): """Get item by its ID or name""" try: + # TODO: Import Refactor - Figure out which Item this needs :( if isinstance(identity, e_Item): item = identity elif isinstance(identity, int): item = eos.db.getItem(identity, *args, **kwargs) - # TODO: Find out what this is. There is no conversions + # TODO: Import refactor - Find out what this is. There is no conversions elif isinstance(identity, basestring): # We normally lookup with string when we are using import/export # features. Check against overrides @@ -430,6 +435,7 @@ class Market(): def getGroup(self, identity, *args, **kwargs): """Get group by its ID or name""" + # TODO: Import Refactor - Figure out which group this needs :( if isinstance(identity, e_Group): return identity elif isinstance(identity, (int, float, basestring)): @@ -448,6 +454,7 @@ class Market(): def getCategory(self, identity, *args, **kwargs): """Get category by its ID or name""" + # TODO: Import Refactor - Figure out which category this needs :( if isinstance(identity, e_Category): category = identity elif isinstance(identity, (int, basestring)): @@ -461,7 +468,7 @@ class Market(): def getMetaGroup(self, identity, *args, **kwargs): """Get meta group by its ID or name""" - if isinstance(identity, eos.types.MetaGroup): + if isinstance(identity, types_MetaGroup): metaGroup = identity elif isinstance(identity, (int, basestring)): metaGroup = eos.db.getMetaGroup(identity, *args, **kwargs) @@ -474,7 +481,7 @@ class Market(): def getMarketGroup(self, identity, *args, **kwargs): """Get market group by its ID""" - if isinstance(identity, eos.types.MarketGroup): + if isinstance(identity, types_MarketGroup): marketGroup = identity elif isinstance(identity, (int, float)): id_ = int(identity) @@ -502,7 +509,7 @@ class Market(): # Check if item is in forced metagroup map if item.name in self.ITEMS_FORCEDMETAGROUP: # Create meta group from scratch - metaGroup = eos.types.MetaType() + metaGroup = types_MetaType() # Get meta group info object based on meta group name metaGroupInfo = self.getMetaGroup(self.ITEMS_FORCEDMETAGROUP[item.name][0]) # Get parent item based on its name @@ -734,9 +741,9 @@ class Market(): def searchShips(self, name): """Find ships according to given text pattern""" - filter_ = e_Category.name.in_(["Ship", "Structure"]) + filter_ = types_Category.name.in_(["Ship", "Structure"]) results = eos.db.searchItems(name, where=filter_, - join=(e_Item.group, e_Group.category), + join=(types_Item.group, types_Group.category), eager=("icon", "group.category", "metaGroup", "metaGroup.parent")) ships = set() for item in results: @@ -790,7 +797,7 @@ class Market(): if price is None: price = eos.db.getPrice(typeID) if price is None: - price = eos.types.Price(typeID) + price = types_Price(typeID) eos.db.add(price) self.priceCache[typeID] = price From af7272cd1021579863bcd3d0b4ad74a4843823cc Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Dec 2016 17:08:59 -0800 Subject: [PATCH 52/53] fix dual reference back to same object --- service/market.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/service/market.py b/service/market.py index 1e8eec29f..4b9e94ee1 100644 --- a/service/market.py +++ b/service/market.py @@ -32,7 +32,8 @@ from service import conversions from service.settings import SettingsProvider from service.price import Price -from eos.gamedata import Category as e_Category, Group as e_Group, Item as e_Item +# TODO: Convert eos.types over to eos.gamedata +# from eos.gamedata import Category as e_Category, Group as e_Group, Item as e_Item from eos.types import MarketGroup as types_MarketGroup, MetaGroup as types_MetaGroup, MetaType as types_MetaType, \ Category as types_Category, Item as types_Item, Group as types_Group, Price as types_Price @@ -188,8 +189,7 @@ class Market(): # Items' group overrides self.customGroups = set() # Limited edition ships - # TODO: Import Refactor - Figure out which group this needs :( - self.les_grp = e_Group() + self.les_grp = types_Group() self.les_grp.ID = -1 self.les_grp.name = "Limited Issue Ships" self.les_grp.published = True @@ -410,8 +410,7 @@ class Market(): def getItem(self, identity, *args, **kwargs): """Get item by its ID or name""" try: - # TODO: Import Refactor - Figure out which Item this needs :( - if isinstance(identity, e_Item): + if isinstance(identity, types_Item): item = identity elif isinstance(identity, int): item = eos.db.getItem(identity, *args, **kwargs) @@ -435,8 +434,7 @@ class Market(): def getGroup(self, identity, *args, **kwargs): """Get group by its ID or name""" - # TODO: Import Refactor - Figure out which group this needs :( - if isinstance(identity, e_Group): + if isinstance(identity, types_Group): return identity elif isinstance(identity, (int, float, basestring)): if isinstance(identity, float): @@ -454,8 +452,7 @@ class Market(): def getCategory(self, identity, *args, **kwargs): """Get category by its ID or name""" - # TODO: Import Refactor - Figure out which category this needs :( - if isinstance(identity, e_Category): + if isinstance(identity, types_Category): category = identity elif isinstance(identity, (int, basestring)): category = eos.db.getCategory(identity, *args, **kwargs) From 836504ee01cca9ee7dd066a0b3567074ddc0a67f Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Tue, 17 Jan 2017 08:53:01 -0800 Subject: [PATCH 53/53] Expand gitattributes to cover a few more scenarios --- .gitattributes | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index c30fccabf..880b12415 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,11 +7,34 @@ # *.h text # Declare files that will always have CRLF line endings on checkout. -*.py text eol=crlf +# Source files +# ============ +*.pxd text eol=crlf +*.py text eol=crlf +*.py3 text eol=crlf +*.pyw text eol=crlf +*.pyx text eol=crlf + # Denote all files that are truly binary and should not be modified. +# Binary files +# ============ +*.db binary +*.p binary +*.pkl binary +*.pyc binary +*.pyd binary +*.pyo binary + +# Note: .db, .p, and .pkl files are associated +# with the python modules ``pickle``, ``dbm.*``, +# ``shelve``, ``marshal``, ``anydbm``, & ``bsddb`` +# (among others). + +# Denote all files that are truly binary and should not be modified. +# Image files +# ============ *.png binary *.jpg binary *.icns binary *.ico binary -*.db binary