From 828b18d0fdbfc5a9f195e7b08b8ad942d3588238 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 16:12:45 -0400 Subject: [PATCH 001/212] py2to3 automatic conversion. Woot! --- _development/helpers.py | 6 +- _development/helpers_fits.py | 4 +- _development/helpers_locale.py | 4 +- config.py | 4 +- eos/capSim.py | 5 +- eos/config.py | 6 +- eos/db/__init__.py | 2 +- eos/db/gamedata/queries.py | 24 +-- eos/db/migration.py | 4 +- eos/db/migrations/upgrade1.py | 2 +- eos/db/migrations/upgrade11.py | 2 +- eos/db/migrations/upgrade12.py | 2 +- eos/db/migrations/upgrade18.py | 2 +- eos/db/migrations/upgrade22.py | 2 +- eos/db/migrations/upgrade4.py | 2 +- eos/db/migrations/upgrade7.py | 2 +- eos/db/migrations/upgrade8.py | 2 +- eos/db/saveddata/queries.py | 28 +-- eos/db/util.py | 4 +- eos/effectHandlerHelpers.py | 24 +-- eos/effects/chargebonuswarfarecharge.py | 2 +- ...chancewithboosterchancebonuspostpercent.py | 2 +- eos/effects/modulebonuswarfarelinkarmor.py | 2 +- eos/effects/modulebonuswarfarelinkinfo.py | 2 +- eos/effects/modulebonuswarfarelinkmining.py | 2 +- eos/effects/modulebonuswarfarelinkshield.py | 2 +- eos/effects/modulebonuswarfarelinkskirmish.py | 2 +- eos/effects/moduletitaneffectgenerator.py | 2 +- eos/effects/techtwocommandburstbonus.py | 2 +- eos/events.py | 2 +- eos/gamedata.py | 22 +-- eos/graph/__init__.py | 12 +- eos/graph/fitDps.py | 4 +- eos/modifiedAttributeDict.py | 16 +- eos/saveddata/booster.py | 2 +- eos/saveddata/cargo.py | 4 +- eos/saveddata/character.py | 16 +- eos/saveddata/drone.py | 11 +- eos/saveddata/fighter.py | 4 +- eos/saveddata/fighterAbility.py | 6 +- eos/saveddata/fit.py | 24 +-- eos/saveddata/implant.py | 2 +- eos/saveddata/mode.py | 2 +- eos/saveddata/module.py | 20 +-- eos/saveddata/ship.py | 2 +- eos/saveddata/user.py | 8 +- gui/bitmapLoader.py | 10 +- gui/builtinContextMenus/amount.py | 2 +- .../changeAffectingSkills.py | 4 +- gui/builtinContextMenus/commandFits.py | 4 +- gui/builtinContextMenus/damagePattern.py | 2 +- gui/builtinContextMenus/droneSplit.py | 2 +- gui/builtinContextMenus/metaSwap.py | 4 +- gui/builtinContextMenus/moduleAmmoPicker.py | 6 +- gui/builtinContextMenus/tabbedFits.py | 2 +- gui/builtinContextMenus/targetResists.py | 2 +- gui/builtinGraphs/fitDps.py | 4 +- gui/builtinMarketBrowser/itemView.py | 4 +- gui/builtinMarketBrowser/searchBox.py | 2 +- gui/builtinPreferenceViews/dummyView.py | 10 +- .../pyfaContextMenuPreferences.py | 2 +- .../pyfaCrestPreferences.py | 10 +- .../pyfaDatabasePreferences.py | 22 +-- .../pyfaEnginePreferences.py | 14 +- .../pyfaGaugePreferences.py | 26 +-- .../pyfaGeneralPreferences.py | 32 ++-- .../pyfaHTMLExportPreferences.py | 6 +- .../pyfaLoggingPreferences.py | 10 +- .../pyfaNetworkPreferences.py | 24 +-- .../pyfaStatViewPreferences.py | 2 +- .../pyfaUpdatePreferences.py | 2 +- gui/builtinShipBrowser/fitItem.py | 6 +- gui/builtinShipBrowser/navigationPanel.py | 2 +- gui/builtinShipBrowser/pfListPane.py | 4 +- gui/builtinShipBrowser/raceSelector.py | 2 +- gui/builtinShipBrowser/shipItem.py | 2 +- gui/builtinStatsViews/capacitorViewFull.py | 2 +- gui/builtinStatsViews/miningyieldViewFull.py | 10 +- gui/builtinStatsViews/outgoingViewFull.py | 12 +- gui/builtinStatsViews/outgoingViewMinimal.py | 12 +- gui/builtinStatsViews/resistancesViewFull.py | 2 +- gui/builtinStatsViews/resourcesViewFull.py | 6 +- .../targetingMiscViewFull.py | 24 +-- .../targetingMiscViewMinimal.py | 24 +-- gui/builtinViewColumns/attributeDisplay.py | 2 +- gui/builtinViewColumns/baseName.py | 6 +- gui/builtinViewColumns/misc.py | 8 +- gui/builtinViews/entityEditor.py | 22 +-- gui/builtinViews/fittingView.py | 10 +- gui/characterEditor.py | 44 ++--- gui/characterSelection.py | 16 +- gui/contextMenu.py | 2 +- gui/copySelectDialog.py | 20 +-- gui/crestFittings.py | 14 +- gui/display.py | 8 +- gui/graphFrame.py | 14 +- gui/itemStats.py | 70 ++++---- gui/mainFrame.py | 6 +- gui/marketBrowser.py | 2 +- gui/patternEditor.py | 12 +- gui/preferenceDialog.py | 2 +- gui/propertyEditor.py | 8 +- gui/pyfatogglepanel.py | 2 +- gui/pygauge.py | 2 +- gui/resistsEditor.py | 10 +- gui/setEditor.py | 8 +- gui/shipBrowser.py | 6 +- gui/utils/exportHtml.py | 4 +- gui/utils/floatspin.py | 32 ++-- gui/utils/helpers_wxPython.py | 2 +- gui/utils/listFormatter.py | 4 +- gui/utils/numberFormatter.py | 8 +- pyfa.py | 6 +- scripts/conversion.py | 36 ++-- scripts/dist.py | 52 +++--- scripts/effectUsedBy.py | 100 +++++------ scripts/findNonMarket.py | 10 +- scripts/icons_update.py | 16 +- scripts/itemDiff.py | 20 +-- scripts/jsonToSql.py | 26 +-- scripts/prep_data.py | 34 ++-- scripts/renders_update.py | 14 +- scripts/sdeReadIcons.py | 4 +- service/attribute.py | 2 +- service/character.py | 24 +-- service/damagePattern.py | 2 +- service/eveapi.py | 36 ++-- service/fit.py | 14 +- service/market.py | 28 +-- service/network.py | 32 ++-- service/port.py | 20 +-- service/price.py | 8 +- service/pycrest/compat.py | 4 +- service/pycrest/eve.py | 8 +- service/server.py | 18 +- service/settings.py | 24 +-- testFits.py | 79 +++++++++ tests/test_locale/file_dialog.py | 2 +- tests/test_locale/test_os_walk.py | 2 +- .../test_eos/test_modifiedAttributeDict.py | 2 +- .../test_service/test_attribute.py | 14 +- tests/test_unread_desc.py | 12 +- utils/compat.py | 14 +- utils/stopwatch.py | 4 +- utils/strfunctions.py | 4 +- utils/timer.py | 4 +- wxthing.py | 159 ++++++++++++++++++ 147 files changed, 1017 insertions(+), 783 deletions(-) create mode 100644 testFits.py create mode 100644 wxthing.py diff --git a/_development/helpers.py b/_development/helpers.py index 4ae17bc5e..8af8f9733 100644 --- a/_development/helpers.py +++ b/_development/helpers.py @@ -28,7 +28,7 @@ def DBInMemory_test(): gamedataCache = True saveddataCache = True gamedata_version = "" - gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(unicode(__file__))), "..", "eve.db")) + gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(str(__file__))), "..", "eve.db")) saveddata_connectionstring = 'sqlite:///:memory:' class ReadOnlyException(Exception): @@ -100,8 +100,8 @@ def DBInMemory(): import eos.db # Output debug info to help us troubleshoot Travis - print(eos.db.saveddata_engine) - print(eos.db.gamedata_engine) + print((eos.db.saveddata_engine)) + print((eos.db.gamedata_engine)) helper = { 'config': eos.config, diff --git a/_development/helpers_fits.py b/_development/helpers_fits.py index 61db7a3ff..a0506827d 100644 --- a/_development/helpers_fits.py +++ b/_development/helpers_fits.py @@ -41,7 +41,7 @@ def CurseFit(DB, Gamedata, Saveddata): mod.state = Saveddata['State'].ONLINE # Add 5 neuts - for _ in xrange(5): + for _ in range(5): fit.modules.append(mod) return fit @@ -60,7 +60,7 @@ def HeronFit(DB, Gamedata, Saveddata): mod.state = Saveddata['State'].ONLINE # Add 5 neuts - for _ in xrange(4): + for _ in range(4): fit.modules.append(mod) return fit \ No newline at end of file diff --git a/_development/helpers_locale.py b/_development/helpers_locale.py index 4d4d29f4e..27983278d 100644 --- a/_development/helpers_locale.py +++ b/_development/helpers_locale.py @@ -94,8 +94,8 @@ def GetUnicodePath(root, file=None, codec=None): path = os.path.join(path, file) if codec: - path = unicode(path, codec) + path = str(path, codec) else: - path = unicode(path) + path = str(path) return path diff --git a/config.py b/config.py index 7f4dde2a2..7162f68d0 100644 --- a/config.py +++ b/config.py @@ -47,12 +47,12 @@ def __createDirs(path): 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()) + root = str(root, sys.getfilesystemencoding()) return root def getDefaultSave(): - return unicode(os.path.expanduser(os.path.join("~", ".pyfa")), sys.getfilesystemencoding()) + return str(os.path.expanduser(os.path.join("~", ".pyfa")), sys.getfilesystemencoding()) def defPaths(customSavePath): diff --git a/eos/capSim.py b/eos/capSim.py index d57567ae6..9b8ffc305 100644 --- a/eos/capSim.py +++ b/eos/capSim.py @@ -1,6 +1,7 @@ import heapq import time from math import sqrt, exp +from functools import reduce DAY = 24 * 60 * 60 * 1000 @@ -87,7 +88,7 @@ class CapSimulator(object): mods[(duration, capNeed, clipSize, disableStagger)] = 1 # Loop over grouped modules, configure staggering and push to the simulation state - for (duration, capNeed, clipSize, disableStagger), amount in mods.iteritems(): + for (duration, capNeed, clipSize, disableStagger), amount in mods.items(): if self.stagger and not disableStagger: if clipSize == 0: duration = int(duration / amount) @@ -192,7 +193,7 @@ class CapSimulator(object): # calculate EVE's stability value try: - avgDrain = reduce(float.__add__, map(lambda x: x[2] / x[1], self.state), 0.0) + avgDrain = reduce(float.__add__, [x[2] / x[1] for x in self.state], 0.0) self.cap_stable_eve = 0.25 * (1.0 + sqrt(-(2.0 * avgDrain * tau - capCapacity) / capCapacity)) ** 2 except ValueError: self.cap_stable_eve = 0.0 diff --git a/eos/config.py b/eos/config.py index ec64e2bd3..2e34df838 100644 --- a/eos/config.py +++ b/eos/config.py @@ -11,14 +11,14 @@ debug = False gamedataCache = True saveddataCache = True gamedata_version = "" -gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding()) +gamedata_connectionstring = 'sqlite:///' + str(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding()) pyfalog.debug("Gamedata connection string: {0}", gamedata_connectionstring) if istravis is True or hasattr(sys, '_called_from_test'): # Running in Travis. Run saveddata database in memory. saveddata_connectionstring = 'sqlite:///:memory:' else: - saveddata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding()) + saveddata_connectionstring = 'sqlite:///' + str(realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding()) pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring) @@ -28,4 +28,4 @@ settings = { } # Autodetect path, only change if the autodetection bugs out. -path = dirname(unicode(__file__, sys.getfilesystemencoding())) +path = dirname(str(__file__, sys.getfilesystemencoding())) diff --git a/eos/db/__init__.py b/eos/db/__init__.py index a29bb86c7..c3bdb9739 100644 --- a/eos/db/__init__.py +++ b/eos/db/__init__.py @@ -22,7 +22,7 @@ import threading from sqlalchemy import MetaData, create_engine from sqlalchemy.orm import sessionmaker -import migration +from . import migration from eos import config from logbook import Logger diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py index fa8d98723..12326b0dc 100644 --- a/eos/db/gamedata/queries.py +++ b/eos/db/gamedata/queries.py @@ -81,7 +81,7 @@ def getItem(lookfor, eager=None): item = gamedata_session.query(Item).get(lookfor) else: item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): if lookfor in itemNameMap: id = itemNameMap[lookfor] if eager is None: @@ -154,7 +154,7 @@ def getGroup(lookfor, eager=None): group = gamedata_session.query(Group).get(lookfor) else: group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): if lookfor in groupNameMap: id = groupNameMap[lookfor] if eager is None: @@ -181,7 +181,7 @@ def getCategory(lookfor, eager=None): else: category = gamedata_session.query(Category).options(*processEager(eager)).filter( Category.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): if lookfor in categoryNameMap: id = categoryNameMap[lookfor] if eager is None: @@ -210,7 +210,7 @@ def getMetaGroup(lookfor, eager=None): else: metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter( MetaGroup.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): if lookfor in metaGroupNameMap: id = metaGroupNameMap[lookfor] if eager is None: @@ -245,7 +245,7 @@ def getMarketGroup(lookfor, eager=None): def getItemsByCategory(filter, where=None, eager=None): if isinstance(filter, int): filter = Category.ID == filter - elif isinstance(filter, basestring): + elif isinstance(filter, str): filter = Category.name == filter else: raise TypeError("Need integer or string as argument") @@ -257,7 +257,7 @@ def getItemsByCategory(filter, where=None, eager=None): @cachedQuery(3, "where", "nameLike", "join") def searchItems(nameLike, where=None, join=None, eager=None): - if not isinstance(nameLike, basestring): + if not isinstance(nameLike, str): raise TypeError("Need string as argument") if join is None: @@ -268,7 +268,7 @@ def searchItems(nameLike, where=None, join=None, eager=None): items = gamedata_session.query(Item).options(*processEager(eager)).join(*join) for token in nameLike.split(' '): - token_safe = u"%{0}%".format(sqlizeString(token)) + token_safe = "%{0}%".format(sqlizeString(token)) if where is not None: items = items.filter(and_(Item.name.like(token_safe, escape="\\"), where)) else: @@ -279,12 +279,12 @@ def searchItems(nameLike, where=None, join=None, eager=None): @cachedQuery(3, "where", "nameLike", "join") def searchSkills(nameLike, where=None, eager=None): - if not isinstance(nameLike, basestring): + if not isinstance(nameLike, str): raise TypeError("Need string as argument") items = gamedata_session.query(Item).options(*processEager(eager)).join(Item.group, Group.category) for token in nameLike.split(' '): - token_safe = u"%{0}%".format(sqlizeString(token)) + token_safe = "%{0}%".format(sqlizeString(token)) if where is not None: items = items.filter(and_(Item.name.like(token_safe, escape="\\"), Category.ID == 16, where)) else: @@ -322,7 +322,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None): @cachedQuery(1, "attr") def getAttributeInfo(attr, eager=None): - if isinstance(attr, basestring): + if isinstance(attr, str): filter = AttributeInfo.name == attr elif isinstance(attr, int): filter = AttributeInfo.ID == attr @@ -337,7 +337,7 @@ def getAttributeInfo(attr, eager=None): @cachedQuery(1, "field") def getMetaData(field): - if isinstance(field, basestring): + if isinstance(field, str): data = gamedata_session.query(MetaData).get(field) else: raise TypeError("Need string as argument") @@ -367,7 +367,7 @@ def getRequiredFor(itemID, attrMapping): skillToLevelClauses = [] - for attrSkill, attrLevel in attrMapping.iteritems(): + for attrSkill, attrLevel in attrMapping.items(): skillToLevelClauses.append(and_(Attribute1.attributeID == attrSkill, Attribute2.attributeID == attrLevel)) queryOr = or_(*skillToLevelClauses) diff --git a/eos/db/migration.py b/eos/db/migration.py index 3d8921eb2..4832daaa9 100644 --- a/eos/db/migration.py +++ b/eos/db/migration.py @@ -3,7 +3,7 @@ import shutil import time import config -import migrations +from . import migrations pyfalog = Logger(__name__) @@ -34,7 +34,7 @@ def update(saveddata_engine): shutil.copyfile(config.saveDB, toFile) - for version in xrange(dbVersion, appVersion): + for version in range(dbVersion, appVersion): func = migrations.updates[version + 1] if func: pyfalog.info("Applying database update: {0}", version + 1) diff --git a/eos/db/migrations/upgrade1.py b/eos/db/migrations/upgrade1.py index 22292dc4f..9059b28de 100644 --- a/eos/db/migrations/upgrade1.py +++ b/eos/db/migrations/upgrade1.py @@ -91,7 +91,7 @@ def upgrade(saveddata_engine): saveddata_engine.execute("ALTER TABLE fits ADD COLUMN targetResistsID INTEGER;") # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade11.py b/eos/db/migrations/upgrade11.py index 2811cf5be..248822484 100644 --- a/eos/db/migrations/upgrade11.py +++ b/eos/db/migrations/upgrade11.py @@ -108,7 +108,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade12.py b/eos/db/migrations/upgrade12.py index 59d6d08d9..6d8e56d3a 100644 --- a/eos/db/migrations/upgrade12.py +++ b/eos/db/migrations/upgrade12.py @@ -332,7 +332,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade18.py b/eos/db/migrations/upgrade18.py index 4a13b7d57..8594c950e 100644 --- a/eos/db/migrations/upgrade18.py +++ b/eos/db/migrations/upgrade18.py @@ -60,7 +60,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade22.py b/eos/db/migrations/upgrade22.py index fb821c2d1..568351a25 100644 --- a/eos/db/migrations/upgrade22.py +++ b/eos/db/migrations/upgrade22.py @@ -29,7 +29,7 @@ def upgrade(saveddata_engine): "targetResists": 2 } - for table in tables.keys(): + for table in list(tables.keys()): # midnight brain, there's probably a much more simple way to do this, but fuck it if tables[table] > 0: diff --git a/eos/db/migrations/upgrade4.py b/eos/db/migrations/upgrade4.py index d1e46d10a..b94d965f2 100644 --- a/eos/db/migrations/upgrade4.py +++ b/eos/db/migrations/upgrade4.py @@ -133,7 +133,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade7.py b/eos/db/migrations/upgrade7.py index fbb74f910..21797e5c5 100644 --- a/eos/db/migrations/upgrade7.py +++ b/eos/db/migrations/upgrade7.py @@ -17,7 +17,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert ships - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: 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 0051034ec..7c1327416 100644 --- a/eos/db/migrations/upgrade8.py +++ b/eos/db/migrations/upgrade8.py @@ -77,7 +77,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index fd8ae93db..98902007f 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -109,9 +109,9 @@ if configVal is True: if type not in queryCache: return functionCache = queryCache[type] - for _, localCache in functionCache.iteritems(): + for _, localCache in functionCache.items(): toDelete = set() - for cacheKey, info in localCache.iteritems(): + for cacheKey, info in localCache.items(): IDs = info[1] if ID in IDs: toDelete.add(cacheKey) @@ -156,7 +156,7 @@ def getUser(lookfor, eager=None): eager = processEager(eager) with sd_lock: user = saveddata_session.query(User).options(*eager).filter(User.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: user = saveddata_session.query(User).options(*eager).filter(User.username == lookfor).first() @@ -175,7 +175,7 @@ def getCharacter(lookfor, eager=None): eager = processEager(eager) with sd_lock: character = saveddata_session.query(Character).options(*eager).filter(Character.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: character = saveddata_session.query(Character).options(*eager).filter( @@ -337,7 +337,7 @@ def clearPrices(): def getMiscData(field): - if isinstance(field, basestring): + if isinstance(field, str): with sd_lock: data = saveddata_session.query(MiscData).get(field) else: @@ -391,7 +391,7 @@ def getDamagePattern(lookfor, eager=None): with sd_lock: pattern = saveddata_session.query(DamagePattern).options(*eager).filter( DamagePattern.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: pattern = saveddata_session.query(DamagePattern).options(*eager).filter( @@ -412,7 +412,7 @@ def getTargetResists(lookfor, eager=None): with sd_lock: pattern = saveddata_session.query(TargetResists).options(*eager).filter( TargetResists.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: pattern = saveddata_session.query(TargetResists).options(*eager).filter( @@ -433,7 +433,7 @@ def getImplantSet(lookfor, eager=None): with sd_lock: pattern = saveddata_session.query(ImplantSet).options(*eager).filter( TargetResists.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.name == lookfor).first() @@ -443,10 +443,10 @@ def getImplantSet(lookfor, eager=None): def searchFits(nameLike, where=None, eager=None): - if not isinstance(nameLike, basestring): + if not isinstance(nameLike, str): raise TypeError("Need string as argument") # Prepare our string for request - nameLike = u"%{0}%".format(sqlizeString(nameLike)) + nameLike = "%{0}%".format(sqlizeString(nameLike)) # Add any extra components to the search to our where clause filter = processWhere(Fit.name.like(nameLike, escape="\\"), where) @@ -484,7 +484,7 @@ def getCrestCharacter(lookfor, eager=None): eager = processEager(eager) with sd_lock: character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.name == lookfor).first() @@ -515,8 +515,8 @@ def removeInvalid(fits): invalids = [f for f in fits if f.isInvalid] if invalids: - map(fits.remove, invalids) - map(saveddata_session.delete, invalids) + list(map(fits.remove, invalids)) + list(map(saveddata_session.delete, invalids)) saveddata_session.commit() return fits @@ -547,4 +547,4 @@ def commit(): except Exception: saveddata_session.rollback() exc_info = sys.exc_info() - raise exc_info[0], exc_info[1], exc_info[2] + raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) diff --git a/eos/db/util.py b/eos/db/util.py index 03a09aceb..7fcf3504d 100644 --- a/eos/db/util.py +++ b/eos/db/util.py @@ -39,7 +39,7 @@ def processEager(eager): return tuple() else: l = [] - if isinstance(eager, basestring): + if isinstance(eager, str): eager = (eager,) for e in eager: @@ -50,7 +50,7 @@ def processEager(eager): def _replacements(eagerString): splitEager = eagerString.split(".") - for i in xrange(len(splitEager)): + for i in range(len(splitEager)): part = splitEager[i] replacement = replace.get(part) if replacement: diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py index 0b0b5e795..ccd620367 100644 --- a/eos/effectHandlerHelpers.py +++ b/eos/effectHandlerHelpers.py @@ -26,7 +26,7 @@ class HandledList(list): def filteredItemPreAssign(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.preAssignItemAttr(*args, **kwargs) except AttributeError: pass @@ -34,7 +34,7 @@ class HandledList(list): def filteredItemIncrease(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.increaseItemAttr(*args, **kwargs) except AttributeError: pass @@ -42,7 +42,7 @@ class HandledList(list): def filteredItemMultiply(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.multiplyItemAttr(*args, **kwargs) except AttributeError: pass @@ -50,7 +50,7 @@ class HandledList(list): def filteredItemBoost(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.boostItemAttr(*args, **kwargs) except AttributeError: pass @@ -58,7 +58,7 @@ class HandledList(list): def filteredItemForce(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.forceItemAttr(*args, **kwargs) except AttributeError: pass @@ -66,7 +66,7 @@ class HandledList(list): def filteredChargePreAssign(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.preAssignChargeAttr(*args, **kwargs) except AttributeError: pass @@ -74,7 +74,7 @@ class HandledList(list): def filteredChargeIncrease(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.increaseChargeAttr(*args, **kwargs) except AttributeError: pass @@ -82,7 +82,7 @@ class HandledList(list): def filteredChargeMultiply(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.multiplyChargeAttr(*args, **kwargs) except AttributeError: pass @@ -90,7 +90,7 @@ class HandledList(list): def filteredChargeBoost(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.boostChargeAttr(*args, **kwargs) except AttributeError: pass @@ -98,7 +98,7 @@ class HandledList(list): def filteredChargeForce(self, filter, *args, **kwargs): for element in self: try: - if filter(element): + if list(filter(element)): element.forceChargeAttr(*args, **kwargs) except AttributeError: pass @@ -115,7 +115,7 @@ class HandledList(list): class HandledModuleList(HandledList): def append(self, mod): emptyPosition = float("Inf") - for i in xrange(len(self)): + for i in range(len(self)): currMod = self[i] if currMod.isEmpty and not mod.isEmpty and currMod.slot == mod.slot: currPos = mod.position or i @@ -149,7 +149,7 @@ class HandledModuleList(HandledList): oldPos = mod.position mod.position = None - for i in xrange(oldPos, len(self)): + for i in range(oldPos, len(self)): self[i].position -= 1 def toDummy(self, index): diff --git a/eos/effects/chargebonuswarfarecharge.py b/eos/effects/chargebonuswarfarecharge.py index 600379c44..b0998fbb4 100644 --- a/eos/effects/chargebonuswarfarecharge.py +++ b/eos/effects/chargebonuswarfarecharge.py @@ -6,6 +6,6 @@ type = "active" def handler(fit, module, context): - for x in xrange(1, 4): + for x in range(1, 4): value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x)) module.multiplyItemAttr("warfareBuff{}Value".format(x), value) diff --git a/eos/effects/modifyboostereffectchancewithboosterchancebonuspostpercent.py b/eos/effects/modifyboostereffectchancewithboosterchancebonuspostpercent.py index 6f123d02c..604e3e14b 100644 --- a/eos/effects/modifyboostereffectchancewithboosterchancebonuspostpercent.py +++ b/eos/effects/modifyboostereffectchancewithboosterchancebonuspostpercent.py @@ -8,7 +8,7 @@ type = "passive" def handler(fit, container, context): level = container.level if "skill" in context else 1 - for i in xrange(5): + for i in range(5): attr = "boosterEffectChance{0}".format(i + 1) fit.boosters.filteredItemBoost(lambda booster: attr in booster.itemModifiedAttributes, attr, container.getModifiedItemAttr("boosterChanceBonus") * level) diff --git a/eos/effects/modulebonuswarfarelinkarmor.py b/eos/effects/modulebonuswarfarelinkarmor.py index 2c792da83..be5843988 100644 --- a/eos/effects/modulebonuswarfarelinkarmor.py +++ b/eos/effects/modulebonuswarfarelinkarmor.py @@ -16,7 +16,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkinfo.py b/eos/effects/modulebonuswarfarelinkinfo.py index 491d824ad..dd1e1111b 100644 --- a/eos/effects/modulebonuswarfarelinkinfo.py +++ b/eos/effects/modulebonuswarfarelinkinfo.py @@ -7,7 +7,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkmining.py b/eos/effects/modulebonuswarfarelinkmining.py index fa28fcf4d..55876bc34 100644 --- a/eos/effects/modulebonuswarfarelinkmining.py +++ b/eos/effects/modulebonuswarfarelinkmining.py @@ -7,7 +7,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkshield.py b/eos/effects/modulebonuswarfarelinkshield.py index f8d3130e0..45cd0ac6f 100644 --- a/eos/effects/modulebonuswarfarelinkshield.py +++ b/eos/effects/modulebonuswarfarelinkshield.py @@ -7,7 +7,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkskirmish.py b/eos/effects/modulebonuswarfarelinkskirmish.py index 84b0cea3e..fc400c791 100644 --- a/eos/effects/modulebonuswarfarelinkskirmish.py +++ b/eos/effects/modulebonuswarfarelinkskirmish.py @@ -7,7 +7,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/moduletitaneffectgenerator.py b/eos/effects/moduletitaneffectgenerator.py index 901a0e89e..741de5ba4 100644 --- a/eos/effects/moduletitaneffectgenerator.py +++ b/eos/effects/moduletitaneffectgenerator.py @@ -6,7 +6,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedItemAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedItemAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/techtwocommandburstbonus.py b/eos/effects/techtwocommandburstbonus.py index 757513cef..1769da1af 100644 --- a/eos/effects/techtwocommandburstbonus.py +++ b/eos/effects/techtwocommandburstbonus.py @@ -4,5 +4,5 @@ runTime = "late" def handler(fit, module, context): - for x in xrange(1, 4): + for x in range(1, 4): module.boostChargeAttr("warfareBuff{}Multiplier".format(x), module.getModifiedItemAttr("commandBurstStrengthBonus")) diff --git a/eos/events.py b/eos/events.py index de8bdfadb..dd4b07b79 100644 --- a/eos/events.py +++ b/eos/events.py @@ -58,7 +58,7 @@ def rel_listener(target, value, initiator): if not target or (isinstance(value, Module) and value.isEmpty): return - print "{} has had a relationship change :D".format(target) + print("{} has had a relationship change :D".format(target)) target.modified = datetime.datetime.now() diff --git a/eos/gamedata.py b/eos/gamedata.py index cf5fe1777..051ad6164 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -22,7 +22,7 @@ import re from sqlalchemy.orm import reconstructor import eos.db -from eqBase import EqBase +from .eqBase import EqBase from eos.saveddata.price import Price as types_Price try: @@ -259,7 +259,7 @@ class Item(EqBase): return default def isType(self, type): - for effect in self.effects.itervalues(): + for effect in self.effects.values(): if effect.isType(type): return True @@ -300,7 +300,7 @@ class Item(EqBase): self.__requiredSkills = requiredSkills # Map containing attribute IDs we may need for required skills # { requiredSkillX : requiredSkillXLevel } - combinedAttrIDs = set(self.srqIDMap.iterkeys()).union(set(self.srqIDMap.itervalues())) + combinedAttrIDs = set(self.srqIDMap.keys()).union(set(self.srqIDMap.values())) # Map containing result of the request # { attributeID : attributeValue } skillAttrs = {} @@ -310,7 +310,7 @@ class Item(EqBase): attrVal = attrInfo[2] skillAttrs[attrID] = attrVal # Go through all attributeID pairs - for srqIDAtrr, srqLvlAttr in self.srqIDMap.iteritems(): + for srqIDAtrr, srqLvlAttr in self.srqIDMap.items(): # Check if we have both in returned result if srqIDAtrr in skillAttrs and srqLvlAttr in skillAttrs: skillID = int(skillAttrs[srqIDAtrr]) @@ -384,7 +384,7 @@ class Item(EqBase): race = None # Check primary and secondary required skills' races if race is None: - skillRaces = tuple(filter(lambda rid: rid, (s.raceID for s in tuple(self.requiredSkills.keys())))) + skillRaces = tuple([rid for rid in (s.raceID for s in tuple(self.requiredSkills.keys())) if rid]) if sum(skillRaces) in map: race = map[sum(skillRaces)] if race == "angelserp": @@ -406,7 +406,7 @@ class Item(EqBase): if self.__assistive is None: assistive = False # Go through all effects and find first assistive - for effect in self.effects.itervalues(): + for effect in self.effects.values(): if effect.isAssistance is True: # If we find one, stop and mark item as assistive assistive = True @@ -421,7 +421,7 @@ class Item(EqBase): if self.__offensive is None: offensive = False # Go through all effects and find first offensive - for effect in self.effects.itervalues(): + for effect in self.effects.values(): if effect.isOffensive is True: # If we find one, stop and mark item as offensive offensive = True @@ -430,8 +430,8 @@ class Item(EqBase): return self.__offensive def requiresSkill(self, skill, level=None): - for s, l in self.requiredSkills.iteritems(): - if isinstance(skill, basestring): + for s, l in self.requiredSkills.items(): + if isinstance(skill, str): if s.name == skill and (level is None or l == level): return True @@ -469,7 +469,7 @@ class Item(EqBase): return self.__price def __repr__(self): - return u"Item(ID={}, name={}) at {}".format( + return "Item(ID={}, name={}) at {}".format( self.ID, self.name, hex(id(self)) ) @@ -523,7 +523,7 @@ class Icon(EqBase): class MarketGroup(EqBase): def __repr__(self): - return u"MarketGroup(ID={}, name={}, parent={}) at {}".format( + return "MarketGroup(ID={}, name={}, parent={}) at {}".format( self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self)) ).encode('utf8') diff --git a/eos/graph/__init__.py b/eos/graph/__init__.py index 44802171f..fe8e8d25d 100644 --- a/eos/graph/__init__.py +++ b/eos/graph/__init__.py @@ -25,7 +25,7 @@ class Graph(object): self.fit = fit self.data = {} if data is not None: - for name, d in data.iteritems(): + for name, d in data.items(): self.setData(Data(name, d)) self.function = function @@ -39,7 +39,7 @@ class Graph(object): def getIterator(self): pointNames = [] pointIterators = [] - for data in self.data.itervalues(): + for data in self.data.values(): pointNames.append(data.name) pointIterators.append(data) @@ -48,7 +48,7 @@ class Graph(object): def _iterator(self, pointNames, pointIterators): for pointValues in itertools.product(*pointIterators): point = {} - for i in xrange(len(pointValues)): + for i in range(len(pointValues)): point[pointNames[i]] = pointValues[i] yield point, self.function(point) @@ -61,12 +61,12 @@ class Data(object): self.data = self.parseString(dataString) def parseString(self, dataString): - if not isinstance(dataString, basestring): + if not isinstance(dataString, str): return Constant(dataString), dataList = [] for data in dataString.split(";"): - if isinstance(data, basestring) and "-" in data: + if isinstance(data, str) and "-" in data: # Dealing with a range dataList.append(Range(data, self.step)) else: @@ -85,7 +85,7 @@ class Data(object): class Constant(object): def __init__(self, const): - if isinstance(const, basestring): + if isinstance(const, str): self.value = None if const == "" else float(const) else: self.value = const diff --git a/eos/graph/fitDps.py b/eos/graph/fitDps.py index 12cfda8d1..58102f2b2 100644 --- a/eos/graph/fitDps.py +++ b/eos/graph/fitDps.py @@ -63,10 +63,10 @@ class FitDpsGraph(Graph): ew['signatureRadius'].sort(key=abssort) ew['velocity'].sort(key=abssort) - for attr, values in ew.iteritems(): + for attr, values in ew.items(): val = data[attr] try: - for i in xrange(len(values)): + for i in range(len(values)): bonus = values[i] val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) data[attr] = val diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index 55463448c..6b3837f69 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -162,9 +162,9 @@ class ModifiedAttributeDict(collections.MutableMapping): def __len__(self): keys = set() - keys.update(self.original.iterkeys()) - keys.update(self.__modified.iterkeys()) - keys.update(self.__intermediary.iterkeys()) + keys.update(iter(self.original.keys())) + keys.update(iter(self.__modified.keys())) + keys.update(iter(self.__intermediary.keys())) return len(keys) def __calculateValue(self, key): @@ -228,11 +228,11 @@ class ModifiedAttributeDict(collections.MutableMapping): val *= multiplier # Each group is penalized independently # Things in different groups will not be stack penalized between each other - for penalizedMultipliers in penalizedMultiplierGroups.itervalues(): + for penalizedMultipliers in penalizedMultiplierGroups.values(): # A quick explanation of how this works: # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them - l1 = filter(lambda _val: _val > 1, penalizedMultipliers) - l2 = filter(lambda _val: _val < 1, penalizedMultipliers) + l1 = [_val for _val in penalizedMultipliers if _val > 1] + l2 = [_val for _val in penalizedMultipliers if _val < 1] # 2: The most significant bonuses take the smallest penalty, # This means we'll have to sort abssort = lambda _val: -abs(_val - 1) @@ -242,7 +242,7 @@ class ModifiedAttributeDict(collections.MutableMapping): # Any module after the first takes penalties according to: # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) for l in (l1, l2): - for i in xrange(len(l)): + for i in range(len(l)): bonus = l[i] val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) val += postIncrease @@ -380,7 +380,7 @@ class ModifiedAttributeDict(collections.MutableMapping): """Force value to attribute and prohibit any changes to it""" self.__forced[attributeName] = value self.__placehold(attributeName) - self.__afflict(attributeName, u"\u2263", value) + self.__afflict(attributeName, "\u2263", value) @staticmethod def getResistance(fit, effect): diff --git a/eos/saveddata/booster.py b/eos/saveddata/booster.py index 62eb4d88f..314d96de5 100644 --- a/eos/saveddata/booster.py +++ b/eos/saveddata/booster.py @@ -118,7 +118,7 @@ class Booster(HandledItem, ItemAttrShortcut): return if not self.active: return - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ (effect.isType("passive") or effect.isType("boosterSideEffect")) and \ effect.activeByDefault: diff --git a/eos/saveddata/cargo.py b/eos/saveddata/cargo.py index 7b6e71349..11c017370 100644 --- a/eos/saveddata/cargo.py +++ b/eos/saveddata/cargo.py @@ -77,8 +77,8 @@ class Cargo(HandledItem, ItemAttrShortcut): "amount": lambda _val: isinstance(_val, int) } - if key == "amount" and val > sys.maxint: - val = sys.maxint + if key == "amount" and val > sys.maxsize: + val = sys.maxsize if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 9ab92c15c..d98b456ad 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -160,7 +160,7 @@ class Character(object): if self.alphaCloneID: clone = eos.db.getAlphaClone(self.alphaCloneID) type = clone.alphaCloneName.split()[1] - name += u' (\u03B1{})'.format(type[0].upper()) + name += ' (\u03B1{})'.format(type[0].upper()) return name @@ -197,7 +197,7 @@ class Character(object): del self.__skillIdMap[skill.itemID] def getSkill(self, item): - if isinstance(item, basestring): + if isinstance(item, str): item = self.getSkillNameMap()[item] elif isinstance(item, int): item = self.getSkillIDMap()[item] @@ -236,17 +236,17 @@ class Character(object): def filteredSkillIncrease(self, filter, *args, **kwargs): for element in self.skills: - if filter(element): + if list(filter(element)): element.increaseItemAttr(*args, **kwargs) def filteredSkillMultiply(self, filter, *args, **kwargs): for element in self.skills: - if filter(element): + if list(filter(element)): element.multiplyItemAttr(*args, **kwargs) def filteredSkillBoost(self, filter, *args, **kwargs): for element in self.skills: - if filter(element): + if list(filter(element)): element.boostItemAttr(*args, **kwargs) def calculateModifiedAttributes(self, fit, runTime, forceProjected=False): @@ -280,7 +280,7 @@ class Character(object): map = { "ID" : lambda _val: isinstance(_val, int), "name" : lambda _val: True, - "apiKey" : lambda _val: _val is None or (isinstance(_val, basestring) and len(_val) > 0), + "apiKey" : lambda _val: _val is None or (isinstance(_val, str) and len(_val) > 0), "ownerID": lambda _val: isinstance(_val, int) or _val is None } @@ -355,7 +355,7 @@ class Skill(HandledItem): if eos.config.settings['strictSkillLevels']: start = time.time() - for item, rlevel in self.item.requiredFor.iteritems(): + for item, rlevel in self.item.requiredFor.items(): if item.group.category.ID == 16: # Skill category if level < rlevel: skill = self.character.getSkill(item.ID) @@ -395,7 +395,7 @@ class Skill(HandledItem): if item is None: return - for effect in item.effects.itervalues(): + for effect in item.effects.values(): if effect.runTime == runTime and \ effect.isType("passive") and \ (not fit.isStructure or effect.isType("structure")) and \ diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index 2ce1b52ab..d5ef1c37a 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -138,8 +138,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): cycleTime = self.getModifiedItemAttr(attr) volley = sum( - map(lambda d: (getter("%sDamage" % d) or 0) * (1 - getattr(targetResists, "%sAmount" % d, 0)), - self.DAMAGE_TYPES)) + [(getter("%sDamage" % d) or 0) * (1 - getattr(targetResists, "%sAmount" % d, 0)) for d in self.DAMAGE_TYPES]) volley *= self.amountActive volley *= self.getModifiedItemAttr("damageMultiplier") or 1 self.__volley = volley @@ -155,7 +154,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): getter = self.getModifiedItemAttr cycleTime = self.getModifiedItemAttr(attr) - volley = sum(map(lambda d: getter(d), self.MINING_ATTRIBUTES)) * self.amountActive + volley = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive self.__miningyield = volley / (cycleTime / 1000.0) else: self.__miningyield = 0 @@ -236,7 +235,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): context = ("drone",) projected = False - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ effect.activeByDefault and \ ((projected is True and effect.isType("projected")) or @@ -251,7 +250,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): i += 1 if self.charge: - for effect in self.charge.effects.itervalues(): + for effect in self.charge.effects.values(): if effect.runTime == runTime and effect.activeByDefault: effect.handler(fit, self, ("droneCharge",)) @@ -263,7 +262,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def fits(self, fit): fitDroneGroupLimits = set() - for i in xrange(1, 3): + for i in range(1, 3): groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i) if groneGrp is not None: fitDroneGroupLimits.add(int(groneGrp)) diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py index 1ea07ebfd..004e86149 100644 --- a/eos/saveddata/fighter.py +++ b/eos/saveddata/fighter.py @@ -98,7 +98,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def __getAbilities(self): """Returns list of FighterAbilities that are loaded with data""" - return [FighterAbility(effect) for effect in self.item.effects.values()] + return [FighterAbility(effect) for effect in list(self.item.effects.values())] def __calculateSlot(self, item): types = { @@ -107,7 +107,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): "Heavy" : Slot.F_HEAVY } - for t, slot in types.iteritems(): + for t, slot in types.items(): if self.getModifiedItemAttr("fighterSquadronIs{}".format(t)): return slot diff --git a/eos/saveddata/fighterAbility.py b/eos/saveddata/fighterAbility.py index b6b099a5c..8c32be23d 100644 --- a/eos/saveddata/fighterAbility.py +++ b/eos/saveddata/fighterAbility.py @@ -57,7 +57,7 @@ class FighterAbility(object): self.__effect = None if self.effectID: - self.__effect = next((x for x in self.fighter.item.effects.itervalues() if x.ID == self.effectID), None) + self.__effect = next((x for x in self.fighter.item.effects.values() if x.ID == self.effectID), None) if self.__effect is None: pyfalog.error("Effect (id: {0}) does not exist", self.effectID) return @@ -127,8 +127,8 @@ class FighterAbility(object): if self.attrPrefix == "fighterAbilityLaunchBomb": # bomb calcs - volley = sum(map(lambda attr: (self.fighter.getModifiedChargeAttr("%sDamage" % attr) or 0) * ( - 1 - getattr(targetResists, "%sAmount" % attr, 0)), self.DAMAGE_TYPES)) + volley = sum([(self.fighter.getModifiedChargeAttr("%sDamage" % attr) or 0) * ( + 1 - getattr(targetResists, "%sAmount" % attr, 0)) for attr in self.DAMAGE_TYPES]) else: volley = sum(map(lambda d2, d: (self.fighter.getModifiedItemAttr( diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 1a186f74b..56386ca00 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -258,11 +258,11 @@ class Fit(object): def projectedFits(self): # only in extreme edge cases will the fit be invalid, but to be sure do # not return them. - return [fit for fit in self.__projectedFits.values() if not fit.isInvalid] + return [fit for fit in list(self.__projectedFits.values()) if not fit.isInvalid] @property def commandFits(self): - return [fit for fit in self.__commandFits.values() if not fit.isInvalid] + return [fit for fit in list(self.__commandFits.values()) if not fit.isInvalid] def getProjectionInfo(self, fitID): return self.projectedOnto.get(fitID, None) @@ -492,7 +492,7 @@ class Fit(object): def __runCommandBoosts(self, runTime="normal"): pyfalog.debug("Applying gang boosts for {0}", repr(self)) - for warfareBuffID in self.commandBonuses.keys(): + for warfareBuffID in list(self.commandBonuses.keys()): # Unpack all data required to run effect properly effect_runTime, value, thing, effect = self.commandBonuses[warfareBuffID] @@ -676,7 +676,7 @@ class Fit(object): def __resetDependentCalcs(self): self.calculated = False - for value in self.projectedOnto.values(): + for value in list(self.projectedOnto.values()): if value.victim_fit: # removing a self-projected fit causes victim fit to be None. @todo: look into why. :3 value.victim_fit.calculated = False @@ -707,14 +707,14 @@ class Fit(object): self.__resetDependentCalcs() # For fits that are under local's Command, we do the same thing - for value in self.boostedOnto.values(): + for value in list(self.boostedOnto.values()): # apparently this is a thing that happens when removing a command fit from a fit and then switching to # that command fit. Same as projected clears, figure out why. if value.boosted_fit: value.boosted_fit.__resetDependentCalcs() if targetFit and type == CalcType.PROJECTED: - pyfalog.debug(u"Calculating projections from {0} to target {1}", repr(self), repr(targetFit)) + pyfalog.debug("Calculating projections from {0} to target {1}", repr(self), repr(targetFit)) projectionInfo = self.getProjectionInfo(targetFit.ID) # Start applying any command fits that we may have. @@ -838,7 +838,7 @@ class Fit(object): for item in c: if item is not None: # apply effects onto target fit x amount of times - for _ in xrange(projectionInfo.amount): + for _ in range(projectionInfo.amount): targetFit.register(item, origin=self) item.calculateModifiedAttributes(targetFit, runTime, True) @@ -854,7 +854,7 @@ class Fit(object): for slotType in (Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM, Slot.SERVICE): amount = self.getSlotsFree(slotType, True) if amount > 0: - for _ in xrange(int(amount)): + for _ in range(int(amount)): self.modules.append(Module.buildEmpty(slotType)) if amount < 0: @@ -870,7 +870,7 @@ class Fit(object): self.modules.remove(mod) def unfill(self): - for i in xrange(len(self.modules) - 1, -1, -1): + for i in range(len(self.modules) - 1, -1, -1): mod = self.modules[i] if mod.isEmpty: del self.modules[i] @@ -878,7 +878,7 @@ class Fit(object): @property def modCount(self): x = 0 - for i in xrange(len(self.modules) - 1, -1, -1): + for i in range(len(self.modules) - 1, -1, -1): mod = self.modules[i] if not mod.isEmpty: x += 1 @@ -1486,11 +1486,11 @@ class Fit(object): return copy_ship def __repr__(self): - return u"Fit(ID={}, ship={}, name={}) at {}".format( + return "Fit(ID={}, ship={}, name={}) at {}".format( self.ID, self.ship.item.name, self.name, hex(id(self)) ).encode('utf8') def __str__(self): - return u"{} ({})".format( + return "{} ({})".format( self.name, self.ship.item.name ).encode('utf8') diff --git a/eos/saveddata/implant.py b/eos/saveddata/implant.py index 869e5c608..d61b9c43b 100644 --- a/eos/saveddata/implant.py +++ b/eos/saveddata/implant.py @@ -93,7 +93,7 @@ class Implant(HandledItem, ItemAttrShortcut): return if not self.active: return - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and effect.isType("passive") and effect.activeByDefault: effect.handler(fit, self, ("implant",)) diff --git a/eos/saveddata/mode.py b/eos/saveddata/mode.py index 50413906d..f0d950549 100644 --- a/eos/saveddata/mode.py +++ b/eos/saveddata/mode.py @@ -50,6 +50,6 @@ class Mode(ItemAttrShortcut, HandledItem): def calculateModifiedAttributes(self, fit, runTime, forceProjected=False): if self.item: - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and effect.activeByDefault: effect.handler(fit, self, context=("module",)) diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index 2b1d2665c..a07fe07f5 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -329,9 +329,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): else: func = self.getModifiedItemAttr - volley = sum(map( - lambda attr: (func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)), - self.DAMAGE_TYPES)) + volley = sum([(func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)) for attr in self.DAMAGE_TYPES]) volley *= self.getModifiedItemAttr("damageMultiplier") or 1 if volley: cycleTime = self.cycleTime @@ -415,13 +413,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if shipType is not None: fitsOnType.add(shipType) - for attr in self.itemModifiedAttributes.keys(): + for attr in list(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(): + for attr in list(self.itemModifiedAttributes.keys()): if attr.startswith("canFitShipGroup"): shipGroup = self.getModifiedItemAttr(attr) if shipGroup is not None: @@ -582,7 +580,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if item is None: return Hardpoint.NONE - for effectName, slot in effectHardpointMap.iteritems(): + for effectName, slot in effectHardpointMap.items(): if effectName in item.effects: return slot @@ -600,7 +598,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): } if item is None: return None - for effectName, slot in effectSlotMap.iteritems(): + for effectName, slot in effectSlotMap.items(): if effectName in item.effects: return slot if item.group.name == "Effect Beacon": @@ -654,7 +652,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): 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(): + for effect in self.charge.effects.values(): if effect.runTime == runTime and \ effect.activeByDefault and \ (effect.isType("offline") or @@ -673,7 +671,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.item: if self.state >= State.OVERHEATED: - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ effect.isType("overheat") \ and not forceProjected \ @@ -681,7 +679,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): and ((gang and effect.isType("gang")) or not gang): effect.handler(fit, self, context) - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ effect.activeByDefault and \ (effect.isType("offline") or @@ -781,7 +779,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def __repr__(self): if self.item: - return u"Module(ID={}, name={}) at {}".format( + return "Module(ID={}, name={}) at {}".format( self.item.ID, self.item.name, hex(id(self)) ) else: diff --git a/eos/saveddata/ship.py b/eos/saveddata/ship.py index 90586dbd7..56b3f7c0d 100644 --- a/eos/saveddata/ship.py +++ b/eos/saveddata/ship.py @@ -87,7 +87,7 @@ class Ship(ItemAttrShortcut, HandledItem): def calculateModifiedAttributes(self, fit, runTime, forceProjected=False): if forceProjected: return - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ effect.isType("passive") and \ effect.activeByDefault: diff --git a/eos/saveddata/user.py b/eos/saveddata/user.py index 39e2eaed2..b623452db 100644 --- a/eos/saveddata/user.py +++ b/eos/saveddata/user.py @@ -33,7 +33,7 @@ class User(object): def encodeAndSetPassword(self, pw): h = hashlib.new("sha256") - salt = "".join([random.choice(string.letters) for _ in xrange(32)]) + salt = "".join([random.choice(string.letters) for _ in range(32)]) h.update(pw) h.update(salt) self.password = ("%s%s" % (h.hexdigest(), salt)) @@ -45,14 +45,14 @@ class User(object): h = hashlib.new("sha256") h.update(pw) h.update(salt) - return self.password == (u"%s%s" % (h.hexdigest(), salt)) + return self.password == ("%s%s" % (h.hexdigest(), salt)) @validates("ID", "username", "password", "admin") def validator(self, key, val): map = { "ID" : lambda _val: isinstance(_val, int), - "username": lambda _val: isinstance(_val, basestring), - "password": lambda _val: isinstance(_val, basestring) and len(_val) == 96, + "username": lambda _val: isinstance(_val, str), + "password": lambda _val: isinstance(_val, str) and len(_val) == 96, "admin" : lambda _val: isinstance(_val, bool) } diff --git a/gui/bitmapLoader.py b/gui/bitmapLoader.py index eb53b1f1d..0c3f90aad 100644 --- a/gui/bitmapLoader.py +++ b/gui/bitmapLoader.py @@ -17,7 +17,7 @@ # along with pyfa. If not, see . # ============================================================================= -import cStringIO +import io import os.path import zipfile @@ -32,7 +32,7 @@ logging = Logger(__name__) try: from collections import OrderedDict except ImportError: - from utils.compat import OrderedDict + from .utils.compat import OrderedDict class BitmapLoader(object): @@ -88,14 +88,14 @@ class BitmapLoader(object): try: img_data = cls.archive.read(path) - sbuf = cStringIO.StringIO(img_data) + sbuf = io.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' + os.sep + location + os.sep + 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/builtinContextMenus/amount.py b/gui/builtinContextMenus/amount.py index f9dd957ff..597f6fef9 100644 --- a/gui/builtinContextMenus/amount.py +++ b/gui/builtinContextMenus/amount.py @@ -46,7 +46,7 @@ class AmountChanger(wx.Dialog): bSizer1.Add(self.input, 1, wx.ALL, 5) self.input.Bind(wx.EVT_CHAR, self.onChar) self.input.Bind(wx.EVT_TEXT_ENTER, self.change) - self.button = wx.Button(self, wx.ID_OK, u"Done") + self.button = wx.Button(self, wx.ID_OK, "Done") bSizer1.Add(self.button, 0, wx.ALL, 5) self.SetSizer(bSizer1) diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index a708ec51f..1f16b8cb8 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -49,7 +49,7 @@ class ChangeAffectingSkills(ContextMenu): if cont[attrName] == 0: continue - for fit, afflictors in cont.getAfflictions(attrName).iteritems(): + for fit, afflictors in cont.getAfflictions(attrName).items(): for afflictor, modifier, amount, used in afflictors: # only add Skills if not isinstance(afflictor, Skill): @@ -89,7 +89,7 @@ class ChangeAffectingSkills(ContextMenu): if bitmap is not None: skillItem.SetBitmap(bitmap) - for i in xrange(-1, 6): + for i in range(-1, 6): levelItem = self.addSkill(rootMenu if msw else grandSub, skill, i) grandSub.AppendItem(levelItem) if (not skill.learned and i == -1) or (skill.learned and skill.level == i): diff --git a/gui/builtinContextMenus/commandFits.py b/gui/builtinContextMenus/commandFits.py index ffcaad6a5..8c73f9b8d 100644 --- a/gui/builtinContextMenus/commandFits.py +++ b/gui/builtinContextMenus/commandFits.py @@ -46,7 +46,7 @@ class CommandFits(ContextMenu): return "Command Fits" def addFit(self, menu, fit, includeShip=False): - label = fit.name if not includeShip else u"({}) {}".format(fit.ship.item.name, fit.name) + label = fit.name if not includeShip else "({}) {}".format(fit.ship.item.name, fit.name) id = ContextMenu.nextID() self.fitMenuItemIds[id] = fit menuItem = wx.MenuItem(menu, id, label) @@ -62,7 +62,7 @@ class CommandFits(ContextMenu): if len(self.__class__.commandFits) < 15: for fit in sorted(self.__class__.commandFits, key=lambda x: x.name): - print fit + print(fit) menuItem = self.addFit(rootMenu if msw else sub, fit, True) sub.AppendItem(menuItem) else: diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index 8399dd069..86899aeab 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -52,7 +52,7 @@ class DamagePattern(ContextMenu): self.singles.append(pattern) # return list of names, with singles first followed by submenu names - self.m = map(lambda p: p.name, self.singles) + self.subMenus.keys() + self.m = [p.name for p in self.singles] + list(self.subMenus.keys()) return self.m def addPattern(self, rootMenu, pattern): diff --git a/gui/builtinContextMenus/droneSplit.py b/gui/builtinContextMenus/droneSplit.py index 7a121ae96..ec52db3f8 100644 --- a/gui/builtinContextMenus/droneSplit.py +++ b/gui/builtinContextMenus/droneSplit.py @@ -45,7 +45,7 @@ class DroneSpinner(wx.Dialog): bSizer1.Add(self.spinner, 1, wx.ALL, 5) - self.button = wx.Button(self, wx.ID_OK, u"Split") + self.button = wx.Button(self, wx.ID_OK, "Split") bSizer1.Add(self.button, 0, wx.ALL, 5) self.SetSizer(bSizer1) diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py index 950a9be8e..d0e510af4 100644 --- a/gui/builtinContextMenus/metaSwap.py +++ b/gui/builtinContextMenus/metaSwap.py @@ -89,7 +89,7 @@ class MetaSwap(ContextMenu): # Sort items by metalevel, and group within that metalevel items = list(self.variations) - print context + print(context) if "implantItem" in context: # sort implants based on name items.sort(key=lambda x: x.name) @@ -112,7 +112,7 @@ class MetaSwap(ContextMenu): if thisgroup != group and context not in ("implantItem", "boosterItem"): group = thisgroup id = ContextMenu.nextID() - m.Append(id, u'─ %s ─' % group) + m.Append(id, '─ %s ─' % group) m.Enable(id, False) id = ContextMenu.nextID() diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 1af970190..79cd790f0 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -50,7 +50,7 @@ class ModuleAmmoPicker(ContextMenu): return False self.modules = modules - self.charges = list(filter(lambda charge: Market.getInstance().getPublicityByItem(charge), validCharges)) + self.charges = list([charge for charge in validCharges if Market.getInstance().getPublicityByItem(charge)]) return len(self.charges) > 0 def getText(self, itmContext, selection): @@ -108,7 +108,7 @@ class ModuleAmmoPicker(ContextMenu): def nameSorter(self, charge): parts = charge.name.split(" ") - return map(self.numericConverter, parts) + return list(map(self.numericConverter, parts)) def addCharge(self, menu, charge): id_ = ContextMenu.nextID() @@ -127,7 +127,7 @@ class ModuleAmmoPicker(ContextMenu): @staticmethod def addSeperator(m, text): id_ = ContextMenu.nextID() - m.Append(id_, u'─ %s ─' % text) + m.Append(id_, '─ %s ─' % text) m.Enable(id_, False) def getSubMenu(self, context, selection, rootMenu, i, pitem): diff --git a/gui/builtinContextMenus/tabbedFits.py b/gui/builtinContextMenus/tabbedFits.py index 30a7bee7a..f95a0229e 100644 --- a/gui/builtinContextMenus/tabbedFits.py +++ b/gui/builtinContextMenus/tabbedFits.py @@ -43,7 +43,7 @@ class TabbedFits(ContextMenu): continue fit = sFit.getFit(page.activeFitID, basic=True) id = ContextMenu.nextID() - mitem = wx.MenuItem(rootMenu, id, u"{}: {}".format(fit.ship.item.name, fit.name)) + mitem = wx.MenuItem(rootMenu, id, "{}: {}".format(fit.ship.item.name, fit.name)) bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem) self.fitLookup[id] = fit m.AppendItem(mitem) diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index 95468ecd0..ca59e22f1 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -95,7 +95,7 @@ class TargetResists(ContextMenu): sub.AppendItem(self.addPattern(rootMenu if msw else sub, pattern)) # Items that have a parent - for menuName, patterns in self.subMenus.items(): + for menuName, patterns in list(self.subMenus.items()): # Create parent item for root menu that is simply name of parent item = wx.MenuItem(rootMenu, ContextMenu.nextID(), menuName) diff --git a/gui/builtinGraphs/fitDps.py b/gui/builtinGraphs/fitDps.py index ae03c180b..9a3048a24 100644 --- a/gui/builtinGraphs/fitDps.py +++ b/gui/builtinGraphs/fitDps.py @@ -54,7 +54,7 @@ class FitDpsGraph(Graph): def getIcons(self): icons = {} sAttr = Attribute.getInstance() - for key, attrName in self.propertyAttributeMap.iteritems(): + for key, attrName in self.propertyAttributeMap.items(): iconFile = sAttr.getAttributeInfo(attrName).icon.iconFile bitmap = BitmapLoader.getBitmap(iconFile, "icons") if bitmap: @@ -69,7 +69,7 @@ class FitDpsGraph(Graph): fitDps.clearData() variable = None - for fieldName, value in fields.iteritems(): + for fieldName, value in fields.items(): d = Data(fieldName, value) if not d.isConstant(): if variable is None: diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py index 382b5fd02..71aa5b125 100644 --- a/gui/builtinMarketBrowser/itemView.py +++ b/gui/builtinMarketBrowser/itemView.py @@ -205,7 +205,7 @@ class ItemView(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) @@ -266,7 +266,7 @@ class ItemView(Display): """ revmap = {} i = 0 - for mgids in self.sMkt.META_MAP.itervalues(): + for mgids in self.sMkt.META_MAP.values(): for mgid in mgids: revmap[mgid] = i i += 1 diff --git a/gui/builtinMarketBrowser/searchBox.py b/gui/builtinMarketBrowser/searchBox.py index 4fd899111..fb86bbb2e 100644 --- a/gui/builtinMarketBrowser/searchBox.py +++ b/gui/builtinMarketBrowser/searchBox.py @@ -1,5 +1,5 @@ from gui.bitmapLoader import BitmapLoader -from pfSearchBox import PFSearchBox +from .pfSearchBox import PFSearchBox class SearchBox(PFSearchBox): diff --git a/gui/builtinPreferenceViews/dummyView.py b/gui/builtinPreferenceViews/dummyView.py index 5bded0d05..43fa34e12 100644 --- a/gui/builtinPreferenceViews/dummyView.py +++ b/gui/builtinPreferenceViews/dummyView.py @@ -50,7 +50,7 @@ class DummyView(PreferenceView): 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 = wx.StaticText(panel, wx.ID_ANY, "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) @@ -60,10 +60,10 @@ class DummyView(PreferenceView): 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) + self.m_checkBox2 = wx.CheckBox(panel, wx.ID_ANY, "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) + self.m_radioBtn2 = wx.RadioButton(panel, wx.ID_ANY, "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) @@ -82,12 +82,12 @@ class DummyView(PreferenceView): 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 = wx.Button(panel, wx.ID_ANY, "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) + self.btnApply = wx.Button(panel, wx.ID_ANY, "Apply", wx.DefaultPosition, wx.DefaultSize, 0) footerSizer.Add(self.btnApply, 0, wx.ALL, 5) return footerSizer diff --git a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py index 58ed81556..ab286458e 100644 --- a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py +++ b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py @@ -23,7 +23,7 @@ class PFContextMenuPref(PreferenceView): mainSizer.Add(self.stTitle, 0, wx.ALL, 5) self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, - u"Disabling context menus can improve responsiveness.", + "Disabling context menus can improve responsiveness.", wx.DefaultPosition, wx.DefaultSize, 0) self.stSubTitle.Wrap(-1) mainSizer.Add(self.stSubTitle, 0, wx.ALL, 5) diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py index 4a9208d0a..be427ff0d 100644 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ b/gui/builtinPreferenceViews/pyfaCrestPreferences.py @@ -36,7 +36,7 @@ class PFCrestPref(PreferenceView): 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.", + "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) @@ -60,7 +60,7 @@ class PFCrestPref(PreferenceView): timeoutSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stTimout = wx.StaticText(panel, wx.ID_ANY, u"Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0) + self.stTimout = wx.StaticText(panel, wx.ID_ANY, "Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0) self.stTimout.Wrap(-1) timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -84,7 +84,7 @@ class PFCrestPref(PreferenceView): 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 = wx.StaticText(panel, wx.ID_ANY, "Client ID:", wx.DefaultPosition, wx.DefaultSize, 0) self.stSetID.Wrap(-1) fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -93,7 +93,7 @@ class PFCrestPref(PreferenceView): 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 = wx.StaticText(panel, wx.ID_ANY, "Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0) self.stSetSecret.Wrap(-1) fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -103,7 +103,7 @@ class PFCrestPref(PreferenceView): 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, "Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0) self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) diff --git a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py index ab93d4655..1e644102a 100644 --- a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py +++ b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py @@ -24,7 +24,7 @@ class PFGeneralPref(PreferenceView): self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, u"(Cannot be changed while pyfa is running. Set via command line switches.)", + self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, "(Cannot be changed while pyfa is running. Set via command line switches.)", wx.DefaultPosition, wx.DefaultSize, 0) self.stSubTitle.Wrap(-1) mainSizer.Add(self.stSubTitle, 0, wx.ALL, 3) @@ -33,11 +33,11 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) # Save in Root - self.cbsaveInRoot = wx.CheckBox(panel, wx.ID_ANY, u"Using Executable Path for Saved Fit Database and Settings", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbsaveInRoot = wx.CheckBox(panel, wx.ID_ANY, "Using Executable Path for Saved Fit Database and Settings", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbsaveInRoot, 0, wx.ALL | wx.EXPAND, 5) # Database path - self.stSetUserPath = wx.StaticText(panel, wx.ID_ANY, u"pyfa User Path:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stSetUserPath = wx.StaticText(panel, wx.ID_ANY, "pyfa User Path:", wx.DefaultPosition, wx.DefaultSize, 0) self.stSetUserPath.Wrap(-1) mainSizer.Add(self.stSetUserPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.inputUserPath = wx.TextCtrl(panel, wx.ID_ANY, config.savePath, wx.DefaultPosition, wx.DefaultSize, 0) @@ -46,7 +46,7 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.inputUserPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) # Save DB - self.stFitDB = wx.StaticText(panel, wx.ID_ANY, u"Fitting Database:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stFitDB = wx.StaticText(panel, wx.ID_ANY, "Fitting Database:", wx.DefaultPosition, wx.DefaultSize, 0) self.stFitDB.Wrap(-1) mainSizer.Add(self.stFitDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -56,7 +56,7 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.inputFitDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) # Game Data DB - self.stGameDB = wx.StaticText(panel, wx.ID_ANY, u"Game Database:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stGameDB = wx.StaticText(panel, wx.ID_ANY, "Game Database:", wx.DefaultPosition, wx.DefaultSize, 0) self.stGameDB.Wrap(-1) mainSizer.Add(self.stGameDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -78,13 +78,13 @@ class PFGeneralPref(PreferenceView): btnSizer = wx.BoxSizer(wx.VERTICAL) btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - self.btnDeleteDamagePatterns = wx.Button(panel, wx.ID_ANY, u"Delete All Damage Pattern Profiles", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnDeleteDamagePatterns = wx.Button(panel, wx.ID_ANY, "Delete All Damage Pattern Profiles", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnDeleteDamagePatterns, 0, wx.ALL, 5) - self.btnDeleteTargetResists = wx.Button(panel, wx.ID_ANY, u"Delete All Target Resist Profiles", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnDeleteTargetResists = wx.Button(panel, wx.ID_ANY, "Delete All Target Resist Profiles", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnDeleteTargetResists, 0, wx.ALL, 5) - self.btnPrices = wx.Button(panel, wx.ID_ANY, u"Delete All Prices", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnPrices = wx.Button(panel, wx.ID_ANY, "Delete All Prices", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnPrices, 0, wx.ALL, 5) mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) @@ -97,17 +97,17 @@ class PFGeneralPref(PreferenceView): panel.Layout() def DeleteDamagePatterns(self, event): - question = u"This is a destructive action that will delete all damage pattern profiles.\nAre you sure you want to do this?" + question = "This is a destructive action that will delete all damage pattern profiles.\nAre you sure you want to do this?" if wxHelpers.YesNoDialog(question, "Confirm"): clearDamagePatterns() def DeleteTargetResists(self, event): - question = u"This is a destructive action that will delete all target resist profiles.\nAre you sure you want to do this?" + question = "This is a destructive action that will delete all target resist profiles.\nAre you sure you want to do this?" if wxHelpers.YesNoDialog(question, "Confirm"): clearTargetResists() def DeletePrices(self, event): - question = u"This is a destructive action that will delete all cached prices out of the database.\nAre you sure you want to do this?" + question = "This is a destructive action that will delete all cached prices out of the database.\nAre you sure you want to do this?" if wxHelpers.YesNoDialog(question, "Confirm"): clearPrices() diff --git a/gui/builtinPreferenceViews/pyfaEnginePreferences.py b/gui/builtinPreferenceViews/pyfaEnginePreferences.py index f9fba7612..641b48f8a 100644 --- a/gui/builtinPreferenceViews/pyfaEnginePreferences.py +++ b/gui/builtinPreferenceViews/pyfaEnginePreferences.py @@ -36,25 +36,25 @@ class PFFittingEnginePref(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.cbGlobalForceReload = wx.CheckBox(panel, wx.ID_ANY, u"Factor in reload time when calculating capacitor usage, damage, and tank.", + self.cbGlobalForceReload = wx.CheckBox(panel, wx.ID_ANY, "Factor in reload time when calculating capacitor usage, damage, and tank.", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbGlobalForceReload, 0, wx.ALL | wx.EXPAND, 5) self.cbStrictSkillLevels = wx.CheckBox(panel, wx.ID_ANY, - u"Enforce strict skill level requirements", + "Enforce strict skill level requirements", wx.DefaultPosition, wx.DefaultSize, 0) self.cbStrictSkillLevels.SetCursor(helpCursor) self.cbStrictSkillLevels.SetToolTip(wx.ToolTip( - u'When enabled, skills will check their dependencies\' requirements when their levels change and reset ' + - u'skills that no longer meet the requirement.\neg: Setting Drones from level V to IV will reset the Heavy ' + - u'Drone Operation skill, as that requires Drones V')) + 'When enabled, skills will check their dependencies\' requirements when their levels change and reset ' + + 'skills that no longer meet the requirement.\neg: Setting Drones from level V to IV will reset the Heavy ' + + 'Drone Operation skill, as that requires Drones V')) mainSizer.Add(self.cbStrictSkillLevels, 0, wx.ALL | wx.EXPAND, 5) self.cbUniversalAdaptiveArmorHardener = wx.CheckBox(panel, wx.ID_ANY, - u"When damage profile is Uniform, set Reactive Armor " + - u"Hardener to match (old behavior).", + "When damage profile is Uniform, set Reactive Armor " + + "Hardener to match (old behavior).", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbUniversalAdaptiveArmorHardener, 0, wx.ALL | wx.EXPAND, 5) diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index 5f6509dc0..557d79c4e 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -160,7 +160,7 @@ class PFGaugePref(PreferenceView): 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 = wx.StaticText(panel, wx.ID_ANY, "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) @@ -185,7 +185,7 @@ class PFGaugePref(PreferenceView): gSizer2 = wx.BoxSizer(wx.HORIZONTAL) - self.st100101 = wx.StaticText(panel, wx.ID_ANY, u"100 - 101", wx.DefaultPosition, wx.DefaultSize, + self.st100101 = wx.StaticText(panel, wx.ID_ANY, "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) @@ -211,7 +211,7 @@ class PFGaugePref(PreferenceView): gSizer3 = wx.BoxSizer(wx.HORIZONTAL) - self.st101103 = wx.StaticText(panel, wx.ID_ANY, u"101 - 103", wx.DefaultPosition, wx.DefaultSize, + self.st101103 = wx.StaticText(panel, wx.ID_ANY, "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) @@ -237,7 +237,7 @@ class PFGaugePref(PreferenceView): gSizer4 = wx.BoxSizer(wx.HORIZONTAL) - self.st103105 = wx.StaticText(panel, wx.ID_ANY, u"103 - 105", wx.DefaultPosition, wx.DefaultSize, + self.st103105 = wx.StaticText(panel, wx.ID_ANY, "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) @@ -284,20 +284,20 @@ class PFGaugePref(PreferenceView): buttonsSizer = wx.BoxSizer(wx.HORIZONTAL) - self.cbLink = wx.CheckBox(panel, wx.ID_ANY, u"Link Colors", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbLink = wx.CheckBox(panel, wx.ID_ANY, "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.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore Defaults", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnRestore = wx.Button(panel, wx.ID_ANY, "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) + self.btnDump = wx.Button(panel, wx.ID_ANY, "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) + self.btnOk = wx.Button(panel, wx.ID_ANY, "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) @@ -432,11 +432,11 @@ class PFGaugePref(PreferenceView): 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() diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index acdfb398b..caecd1cc9 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -34,70 +34,70 @@ 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, + self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, "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, + self.cbGlobalDmgPattern = wx.CheckBox(panel, wx.ID_ANY, "Use global damage pattern", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbGlobalDmgPattern, 0, wx.ALL | wx.EXPAND, 5) - self.cbCompactSkills = wx.CheckBox(panel, wx.ID_ANY, u"Compact skills needed tooltip", wx.DefaultPosition, + self.cbCompactSkills = wx.CheckBox(panel, wx.ID_ANY, "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, + self.cbFitColorSlots = wx.CheckBox(panel, wx.ID_ANY, "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, + self.cbReopenFits = wx.CheckBox(panel, wx.ID_ANY, "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) + self.cbRackSlots = wx.CheckBox(panel, wx.ID_ANY, "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) + self.cbRackLabels = wx.CheckBox(panel, wx.ID_ANY, "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) + self.cbShowTooltip = wx.CheckBox(panel, wx.ID_ANY, "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, + self.cbMarketShortcuts = wx.CheckBox(panel, wx.ID_ANY, "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) + self.cbGaugeAnimation = wx.CheckBox(panel, wx.ID_ANY, "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, + self.cbExportCharges = wx.CheckBox(panel, wx.ID_ANY, "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", + self.cbOpenFitInNew = wx.CheckBox(panel, wx.ID_ANY, "Open fittings in a new page by default", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbOpenFitInNew, 0, wx.ALL | wx.EXPAND, 5) - self.cbShowShipBrowserTooltip = wx.CheckBox(panel, wx.ID_ANY, u"Show ship browser tooltip", + self.cbShowShipBrowserTooltip = wx.CheckBox(panel, wx.ID_ANY, "Show ship browser tooltip", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbShowShipBrowserTooltip, 0, wx.ALL | wx.EXPAND, 5) priceSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stDefaultSystem = wx.StaticText(panel, wx.ID_ANY, u"Default Market Prices:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stDefaultSystem = wx.StaticText(panel, wx.ID_ANY, "Default Market Prices:", wx.DefaultPosition, wx.DefaultSize, 0) self.stDefaultSystem.Wrap(-1) priceSizer.Add(self.stDefaultSystem, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.chPriceSystem = wx.Choice(panel, choices=Price.systemsList.keys()) + self.chPriceSystem = wx.Choice(panel, choices=list(Price.systemsList.keys())) priceSizer.Add(self.chPriceSystem, 1, wx.ALL | wx.EXPAND, 5) mainSizer.Add(priceSizer, 0, wx.ALL | wx.EXPAND, 0) delayTimer = wx.BoxSizer(wx.HORIZONTAL) - self.stMarketDelay = wx.StaticText(panel, wx.ID_ANY, u"Market Search Delay (ms):", wx.DefaultPosition, wx.DefaultSize, 0) + self.stMarketDelay = wx.StaticText(panel, wx.ID_ANY, "Market Search Delay (ms):", wx.DefaultPosition, wx.DefaultSize, 0) self.stMarketDelay.Wrap(-1) self.stMarketDelay.SetCursor(helpCursor) self.stMarketDelay.SetToolTip( diff --git a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py index a60cc24df..1afdc7f90 100644 --- a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py +++ b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py @@ -38,7 +38,7 @@ class PFHTMLExportPref(PreferenceView): 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()), + '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) @@ -56,7 +56,7 @@ 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 format", wx.DefaultPosition, + self.exportMinimal = wx.CheckBox(panel, wx.ID_ANY, "Enable minimal format", wx.DefaultPosition, wx.DefaultSize, 0) self.exportMinimal.SetValue(self.HTMLExportSettings.getMinimalEnabled()) self.exportMinimal.Bind(wx.EVT_CHECKBOX, self.OnMinimalEnabledChange) @@ -67,7 +67,7 @@ class PFHTMLExportPref(PreferenceView): def setPathLinkCtrlValues(self, path): self.PathLinkCtrl.SetLabel(self.HTMLExportSettings.getPath()) - self.PathLinkCtrl.SetURL(u'file:///{}'.format(self.HTMLExportSettings.getPath())) + self.PathLinkCtrl.SetURL('file:///{}'.format(self.HTMLExportSettings.getPath())) self.PathLinkCtrl.SetSize(wx.DefaultSize) self.PathLinkCtrl.Refresh() diff --git a/gui/builtinPreferenceViews/pyfaLoggingPreferences.py b/gui/builtinPreferenceViews/pyfaLoggingPreferences.py index 5a7ffccc1..78490c2ad 100644 --- a/gui/builtinPreferenceViews/pyfaLoggingPreferences.py +++ b/gui/builtinPreferenceViews/pyfaLoggingPreferences.py @@ -25,7 +25,7 @@ class PFGeneralPref(PreferenceView): self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, u"(Cannot be changed while pyfa is running. Set via command line switches.)", + self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, "(Cannot be changed while pyfa is running. Set via command line switches.)", wx.DefaultPosition, wx.DefaultSize, 0) self.stSubTitle.Wrap(-1) mainSizer.Add(self.stSubTitle, 0, wx.ALL, 3) @@ -34,7 +34,7 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) # Database path - self.stLogPath = wx.StaticText(panel, wx.ID_ANY, u"Log file location:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stLogPath = wx.StaticText(panel, wx.ID_ANY, "Log file location:", wx.DefaultPosition, wx.DefaultSize, 0) self.stLogPath.Wrap(-1) mainSizer.Add(self.stLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.inputLogPath = wx.TextCtrl(panel, wx.ID_ANY, config.logPath, wx.DefaultPosition, wx.DefaultSize, 0) @@ -43,13 +43,13 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.inputLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) # Debug Logging - self.cbdebugLogging = wx.CheckBox(panel, wx.ID_ANY, u"Debug Logging Enabled", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbdebugLogging = wx.CheckBox(panel, wx.ID_ANY, "Debug Logging Enabled", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbdebugLogging, 0, wx.ALL | wx.EXPAND, 5) - self.stDumpLogs = wx.StaticText(panel, wx.ID_ANY, u"Pressing this button will cause all logs in memory to write to the log file:", + self.stDumpLogs = wx.StaticText(panel, wx.ID_ANY, "Pressing this button will cause all logs in memory to write to the log file:", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.stDumpLogs, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnDumpLogs = wx.Button(panel, wx.ID_ANY, u"Dump All Logs", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnDumpLogs = wx.Button(panel, wx.ID_ANY, "Dump All Logs", wx.DefaultPosition, wx.DefaultSize, 0) self.btnDumpLogs.Bind(wx.EVT_BUTTON, OnDumpLogs) mainSizer.Add(self.btnDumpLogs, 0, wx.ALIGN_LEFT, 5) diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index e20f8a01d..78b6dd32d 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -30,18 +30,18 @@ class PFNetworkPref(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.cbEnableNetwork = wx.CheckBox(panel, wx.ID_ANY, u"Enable Network", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbEnableNetwork = wx.CheckBox(panel, wx.ID_ANY, "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, + self.cbEve = wx.CheckBox(panel, wx.ID_ANY, "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) + self.cbPricing = wx.CheckBox(panel, wx.ID_ANY, "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) + self.cbPyfaUpdate = wx.CheckBox(panel, wx.ID_ANY, "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) @@ -80,11 +80,11 @@ class PFNetworkPref(PreferenceView): ptypeSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stPType = wx.StaticText(panel, wx.ID_ANY, u"Mode:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPType = wx.StaticText(panel, wx.ID_ANY, "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.chProxyTypeChoices = ["No proxy", "Auto-detected proxy settings", "Manual proxy settings"] self.chProxyType = wx.Choice(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, self.chProxyTypeChoices, 0) self.chProxyType.SetSelection(self.nMode) @@ -98,7 +98,7 @@ class PFNetworkPref(PreferenceView): 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 = wx.StaticText(panel, wx.ID_ANY, "Addr:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetAddr.Wrap(-1) fgAddrSizer.Add(self.stPSetAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -106,7 +106,7 @@ class PFNetworkPref(PreferenceView): fgAddrSizer.Add(self.editProxySettingsAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.stPSetPort = wx.StaticText(panel, wx.ID_ANY, u"Port:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetPort = wx.StaticText(panel, wx.ID_ANY, "Port:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetPort.Wrap(-1) fgAddrSizer.Add(self.stPSetPort, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -118,11 +118,11 @@ class PFNetworkPref(PreferenceView): 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) + self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, "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.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, u"Password:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, "Password:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetPassword.Wrap(-1) self.editProxySettingsPassword = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[1], wx.DefaultPosition, wx.DefaultSize, wx.TE_PASSWORD) @@ -133,7 +133,7 @@ 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, + self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, "Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSAutoDetected.Wrap(-1) mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -141,7 +141,7 @@ class PFNetworkPref(PreferenceView): 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, "Apply Proxy Settings", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnApply, 0, wx.ALL, 5) diff --git a/gui/builtinPreferenceViews/pyfaStatViewPreferences.py b/gui/builtinPreferenceViews/pyfaStatViewPreferences.py index 800b64851..eb7d424b8 100644 --- a/gui/builtinPreferenceViews/pyfaStatViewPreferences.py +++ b/gui/builtinPreferenceViews/pyfaStatViewPreferences.py @@ -27,7 +27,7 @@ class PFStatViewPref(PreferenceView): mainSizer.Add(self.stTitle, 0, wx.ALL, 5) self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, - u"Changes require restart of pyfa to take effect.", + "Changes require restart of pyfa to take effect.", wx.DefaultPosition, wx.DefaultSize, 0) self.stSubTitle.Wrap(-1) mainSizer.Add(self.stSubTitle, 0, wx.ALL, 3) diff --git a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py index cfc1bef11..b1158df94 100644 --- a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py +++ b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py @@ -33,7 +33,7 @@ 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, + self.suppressPrerelease = wx.CheckBox(panel, wx.ID_ANY, "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')) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index d5dde2967..bf51204b4 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -12,7 +12,7 @@ import gui.mainFrame import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.fonts as fonts -from events import * +from .events import * from gui.bitmapLoader import BitmapLoader from gui.builtinShipBrowser.pfBitmapFrame import PFBitmapFrame from service.fit import Fit @@ -154,8 +154,8 @@ class FitItem(SFItem.SFBrowserItem): if self.shipTrait and sFit.serviceFittingOptions["showShipBrowserTooltip"]: notes = "" if self.notes: - notes = u'─' * 20 + u"\nNotes: {}\n".format(self.notes[:197] + '...' if len(self.notes) > 200 else self.notes) - self.SetToolTip(wx.ToolTip(u'{}\n{}{}\n{}'.format(self.shipName, notes, u'─' * 20, self.shipTrait))) + notes = '─' * 20 + "\nNotes: {}\n".format(self.notes[:197] + '...' if len(self.notes) > 200 else self.notes) + self.SetToolTip(wx.ToolTip('{}\n{}{}\n{}'.format(self.shipName, notes, '─' * 20, self.shipTrait))) def OnKeyUp(self, event): if event.GetKeyCode() in (32, 13): # space and enter diff --git a/gui/builtinShipBrowser/navigationPanel.py b/gui/builtinShipBrowser/navigationPanel.py index be36fec01..1e0112f93 100644 --- a/gui/builtinShipBrowser/navigationPanel.py +++ b/gui/builtinShipBrowser/navigationPanel.py @@ -8,7 +8,7 @@ import gui.mainFrame import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.fonts as fonts -from events import * +from .events import * from gui.bitmapLoader import BitmapLoader from service.fit import Fit diff --git a/gui/builtinShipBrowser/pfListPane.py b/gui/builtinShipBrowser/pfListPane.py index e6689ce8d..bb0bf7bc4 100644 --- a/gui/builtinShipBrowser/pfListPane.py +++ b/gui/builtinShipBrowser/pfListPane.py @@ -127,7 +127,7 @@ class PFListPane(wx.ScrolledWindow): maxy = 0 selected = None - for i in xrange(len(self._wList)): + for i in range(len(self._wList)): iwidth, iheight = self._wList[i].GetSize() xa, ya = self.CalcScrolledPosition((0, maxy)) self._wList[i].SetPosition((xa, ya)) @@ -144,7 +144,7 @@ class PFListPane(wx.ScrolledWindow): elif doFocus: self.SetFocus() - for i in xrange(len(self._wList)): + for i in range(len(self._wList)): iwidth, iheight = self._wList[i].GetSize() self._wList[i].SetSize((cwidth, iheight)) if doRefresh is True: diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index d045eb8d2..5df6b772d 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -6,7 +6,7 @@ from logbook import Logger import gui.utils.animEffects as animEffects import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils -from events import * +from .events import * from gui.bitmapLoader import BitmapLoader pyfalog = Logger(__name__) diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py index f6b61bf18..1f7112d0a 100644 --- a/gui/builtinShipBrowser/shipItem.py +++ b/gui/builtinShipBrowser/shipItem.py @@ -8,7 +8,7 @@ import gui.mainFrame import gui.utils.colorUtils as colorUtils import gui.utils.drawUtils as drawUtils import gui.utils.fonts as fonts -from events import * +from .events import * from gui.bitmapLoader import BitmapLoader from gui.contextMenu import ContextMenu from service.fit import Fit diff --git a/gui/builtinStatsViews/capacitorViewFull.py b/gui/builtinStatsViews/capacitorViewFull.py index 49bb9d59b..1986d29f9 100644 --- a/gui/builtinStatsViews/capacitorViewFull.py +++ b/gui/builtinStatsViews/capacitorViewFull.py @@ -124,7 +124,7 @@ class CapacitorViewFull(StatsView): label = getattr(self, labelName % panel) value = value() if fit is not None else 0 value = value if value is not None else 0 - if isinstance(value, basestring): + if isinstance(value, str): label.SetLabel(value) label.SetToolTip(wx.ToolTip(value)) else: diff --git a/gui/builtinStatsViews/miningyieldViewFull.py b/gui/builtinStatsViews/miningyieldViewFull.py index be69be316..476a5644a 100644 --- a/gui/builtinStatsViews/miningyieldViewFull.py +++ b/gui/builtinStatsViews/miningyieldViewFull.py @@ -69,7 +69,7 @@ class MiningYieldViewFull(StatsView): hbox = wx.BoxSizer(wx.HORIZONTAL) box.Add(hbox, 1, wx.ALIGN_CENTER) - lbl = wx.StaticText(parent, wx.ID_ANY, u"0.0 m\u00B3/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0.0 m\u00B3/s") setattr(self, "label%sminingyield%s" % (panel.capitalize(), miningType.capitalize()), lbl) hbox.Add(lbl, 0, wx.ALIGN_CENTER) @@ -90,7 +90,7 @@ class MiningYieldViewFull(StatsView): hbox = wx.BoxSizer(wx.HORIZONTAL) box.Add(hbox, 1, wx.EXPAND) - lbl = wx.StaticText(parent, wx.ID_ANY, u"0.0 m\u00B3/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0.0 m\u00B3/s") setattr(self, "label%sminingyieldTotal" % panel.capitalize(), lbl) hbox.Add(lbl, 0, wx.ALIGN_LEFT) @@ -128,9 +128,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, 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)) + stats = (("labelFullminingyieldMiner", lambda: fit.minerYield, 3, 0, 0, "%s m\u00B3/s", None), + ("labelFullminingyieldDrone", lambda: fit.droneYield, 3, 0, 0, "%s m\u00B3/s", None), + ("labelFullminingyieldTotal", lambda: fit.totalYield, 3, 0, 0, "%s m\u00B3/s", None)) counter = 0 for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats: diff --git a/gui/builtinStatsViews/outgoingViewFull.py b/gui/builtinStatsViews/outgoingViewFull.py index 346aacc7a..da0c73829 100644 --- a/gui/builtinStatsViews/outgoingViewFull.py +++ b/gui/builtinStatsViews/outgoingViewFull.py @@ -63,9 +63,9 @@ class OutgoingViewFull(StatsView): baseBox.Add(BitmapLoader.getStaticBitmap("%s_big" % image, parent, "gui"), 0, wx.ALIGN_CENTER) if "Capacitor" in outgoingType: - lbl = wx.StaticText(parent, wx.ID_ANY, u"0 GJ/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0 GJ/s") else: - lbl = wx.StaticText(parent, wx.ID_ANY, u"0 HP/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0 HP/s") lbl.SetToolTip(wx.ToolTip(tooltip)) @@ -81,10 +81,10 @@ class OutgoingViewFull(StatsView): # If we did anything intresting, we'd update our labels to reflect the new fit's stats here stats = [ - ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, u"%s GJ/s", None), + ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, "%s GJ/s", None), ] counter = 0 diff --git a/gui/builtinStatsViews/outgoingViewMinimal.py b/gui/builtinStatsViews/outgoingViewMinimal.py index d2bf2c118..bb75f689d 100644 --- a/gui/builtinStatsViews/outgoingViewMinimal.py +++ b/gui/builtinStatsViews/outgoingViewMinimal.py @@ -62,9 +62,9 @@ class OutgoingViewMinimal(StatsView): baseBox.Add(wx.StaticText(contentPanel, wx.ID_ANY, label), 0, wx.ALIGN_CENTER) if "Capacitor" in outgoingType: - lbl = wx.StaticText(parent, wx.ID_ANY, u"0 GJ/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0 GJ/s") else: - lbl = wx.StaticText(parent, wx.ID_ANY, u"0 HP/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0 HP/s") lbl.SetToolTip(wx.ToolTip(tooltip)) @@ -80,10 +80,10 @@ class OutgoingViewMinimal(StatsView): # If we did anything intresting, we'd update our labels to reflect the new fit's stats here stats = [ - ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, u"%s GJ/s", None), + ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, "%s GJ/s", None), ] counter = 0 diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index 9946310ec..98b0c896f 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -93,7 +93,7 @@ class ResistancesViewFull(StatsView): self.stEHPs.Bind(wx.EVT_BUTTON, self.toggleEHP) - for i in xrange(4): + for i in range(4): sizerResistances.AddGrowableCol(i + 1) sizerResistances.Add(self.stEHPs, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index 3171aaa4b..298fed756 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -169,8 +169,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": " m\u00B3", + "fighterBay": " m\u00B3", "cargoBay": " m\u00B3"} lbl = wx.StaticText(parent, wx.ID_ANY, "%s" % units[type_]) absolute.Add(lbl, 0, wx.ALIGN_LEFT) @@ -275,7 +275,7 @@ class ResourcesViewFull(StatsView): totalCalibrationPoints = value labelTCP = label - if isinstance(value, basestring): + if isinstance(value, str): label.SetLabel(value) label.SetToolTip(wx.ToolTip(value)) else: diff --git a/gui/builtinStatsViews/targetingMiscViewFull.py b/gui/builtinStatsViews/targetingMiscViewFull.py index 1fa235ba0..4e7630fc9 100644 --- a/gui/builtinStatsViews/targetingMiscViewFull.py +++ b/gui/builtinStatsViews/targetingMiscViewFull.py @@ -87,7 +87,7 @@ class TargetingMiscViewFull(StatsView): ("Align time", "AlignTime", "s"), ("Signature", "SigRadius", "m"), ("Warp Speed", "WarpSpeed", "AU/s"), - ("Cargo", "Cargo", u"m\u00B3")) + ("Cargo", "Cargo", "m\u00B3")) for header, labelShort, unit in labels: gridMisc.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT) @@ -157,7 +157,7 @@ class TargetingMiscViewFull(StatsView): ("labelFullAlignTime", {"main": lambda: fit.alignTime}, 3, 0, 0, "s"), ("labelFullSigRadius", {"main": lambda: fit.ship.getModifiedItemAttr("signatureRadius")}, 3, 0, 9, ""), ("labelFullWarpSpeed", {"main": lambda: fit.warpSpeed}, 3, 0, 0, "AU/s"), - ("labelFullCargo", cargoValues, 4, 0, 9, u"m\u00B3")) + ("labelFullCargo", cargoValues, 4, 0, 9, "m\u00B3")) counter = 0 RADII = [("Pod", 25), ("Interceptor", 33), ("Frigate", 38), @@ -167,13 +167,13 @@ class TargetingMiscViewFull(StatsView): for labelName, valueDict, prec, lowest, highest, unit in stats: label = getattr(self, labelName) newValues = {} - for valueAlias, value in valueDict.items(): + for valueAlias, value in list(valueDict.items()): value = value() if fit is not None else 0 value = value if value is not None else 0 newValues[valueAlias] = value if self._cachedValues[counter] != newValues: mainValue = newValues["main"] - otherValues = dict((k, newValues[k]) for k in filter(lambda k: k != "main", newValues)) + otherValues = dict((k, newValues[k]) for k in [k for k in newValues if k != "main"]) if labelName == "labelFullCargo": # Get sum of all cargoholds except for maintenance bay additionalCargo = sum(otherValues.values()) @@ -210,11 +210,11 @@ class TargetingMiscViewFull(StatsView): 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 = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])] - for attrName, tipAlias in cargoNamesOrder.items(): + tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])] + for attrName, tipAlias in list(cargoNamesOrder.items()): if newValues[attrName] > 0: - tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) - label.SetToolTip(wx.ToolTip(u"\n".join(tipLines))) + tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) + label.SetToolTip(wx.ToolTip("\n".join(tipLines))) else: label.SetToolTip(wx.ToolTip("%.1f" % mainValue)) else: @@ -238,11 +238,11 @@ class TargetingMiscViewFull(StatsView): cachedCargo = self._cachedValues[counter] # 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 = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])] - for attrName, tipAlias in cargoNamesOrder.items(): + tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])] + for attrName, tipAlias in list(cargoNamesOrder.items()): if cachedCargo[attrName] > 0: - tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) - label.SetToolTip(wx.ToolTip(u"\n".join(tipLines))) + tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) + label.SetToolTip(wx.ToolTip("\n".join(tipLines))) else: label.SetToolTip(wx.ToolTip("")) diff --git a/gui/builtinStatsViews/targetingMiscViewMinimal.py b/gui/builtinStatsViews/targetingMiscViewMinimal.py index df0d9f70a..78664a2e2 100644 --- a/gui/builtinStatsViews/targetingMiscViewMinimal.py +++ b/gui/builtinStatsViews/targetingMiscViewMinimal.py @@ -87,7 +87,7 @@ class TargetingMiscViewMinimal(StatsView): ("Align time", "AlignTime", "s"), ("Signature", "SigRadius", "m"), ("Warp Speed", "WarpSpeed", "AU/s"), - ("Cargo", "Cargo", u"m\u00B3")) + ("Cargo", "Cargo", "m\u00B3")) for header, labelShort, unit in labels: gridMisc.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT) @@ -154,7 +154,7 @@ class TargetingMiscViewMinimal(StatsView): ("labelFullAlignTime", {"main": lambda: fit.alignTime}, 3, 0, 0, "s"), ("labelFullSigRadius", {"main": lambda: fit.ship.getModifiedItemAttr("signatureRadius")}, 3, 0, 9, ""), ("labelFullWarpSpeed", {"main": lambda: fit.warpSpeed}, 3, 0, 0, "AU/s"), - ("labelFullCargo", cargoValues, 4, 0, 9, u"m\u00B3")) + ("labelFullCargo", cargoValues, 4, 0, 9, "m\u00B3")) counter = 0 RADII = [("Pod", 25), ("Interceptor", 33), ("Frigate", 38), @@ -164,13 +164,13 @@ class TargetingMiscViewMinimal(StatsView): for labelName, valueDict, prec, lowest, highest, unit in stats: label = getattr(self, labelName) newValues = {} - for valueAlias, value in valueDict.items(): + for valueAlias, value in list(valueDict.items()): value = value() if fit is not None else 0 value = value if value is not None else 0 newValues[valueAlias] = value if self._cachedValues[counter] != newValues: mainValue = newValues["main"] - otherValues = dict((k, newValues[k]) for k in filter(lambda k: k != "main", newValues)) + otherValues = dict((k, newValues[k]) for k in [k for k in newValues if k != "main"]) if labelName == "labelFullCargo": # Get sum of all cargoholds except for maintenance bay additionalCargo = sum(otherValues.values()) @@ -204,11 +204,11 @@ class TargetingMiscViewMinimal(StatsView): 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 = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])] - for attrName, tipAlias in cargoNamesOrder.items(): + tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])] + for attrName, tipAlias in list(cargoNamesOrder.items()): if newValues[attrName] > 0: - tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) - label.SetToolTip(wx.ToolTip(u"\n".join(tipLines))) + tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) + label.SetToolTip(wx.ToolTip("\n".join(tipLines))) else: label.SetToolTip(wx.ToolTip("%.1f" % mainValue)) else: @@ -232,11 +232,11 @@ class TargetingMiscViewMinimal(StatsView): cachedCargo = self._cachedValues[counter] # 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 = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])] - for attrName, tipAlias in cargoNamesOrder.items(): + tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])] + for attrName, tipAlias in list(cargoNamesOrder.items()): if cachedCargo[attrName] > 0: - tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) - label.SetToolTip(wx.ToolTip(u"\n".join(tipLines))) + tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) + label.SetToolTip(wx.ToolTip("\n".join(tipLines))) else: label.SetToolTip(wx.ToolTip("")) diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index 855bfe2a1..0ccdcd96b 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -83,7 +83,7 @@ class AttributeDisplay(ViewColumn): if self.info.name == "volume": str_ = (formatAmount(attr, 3, 0, 3)) if hasattr(mod, "amount"): - str_ += u"m\u00B3 (%s m\u00B3)" % (formatAmount(attr * mod.amount, 3, 0, 3)) + str_ += "m\u00B3 (%s m\u00B3)" % (formatAmount(attr * mod.amount, 3, 0, 3)) attr = str_ if isinstance(attr, (float, int)): diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index 3a8bc6fa7..8ed1466ac 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -71,9 +71,9 @@ class BaseName(ViewColumn): elif isinstance(stuff, Rack): if FitSvc.getInstance().serviceFittingOptions["rackLabels"]: if stuff.slot == Slot.MODE: - return u'─ Tactical Mode ─' + return '─ Tactical Mode ─' else: - return u'─ {} Slots ─'.format(Slot.getName(stuff.slot).capitalize()) + return '─ {} Slots ─'.format(Slot.getName(stuff.slot).capitalize()) else: return "" elif isinstance(stuff, Module): @@ -91,7 +91,7 @@ class BaseName(ViewColumn): if marketShortcut: # use unicode subscript to display shortcut value - shortcut = unichr(marketShortcut + 8320) + u" " + shortcut = chr(marketShortcut + 8320) + " " del item.marketShortcut return shortcut + item.name diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index e4130f2fe..0c3971445 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -171,7 +171,7 @@ class Miscellanea(ViewColumn): "falloff range": falloffRangeBonus, "tracking speed": trackingSpeedBonus} - isTrackingDisruptor = any(map(lambda x: x is not None and x != 0, trackingDisruptorAttributes.values())) + isTrackingDisruptor = any([x is not None and x != 0 for x in list(trackingDisruptorAttributes.values())]) # Then get the attributes for guidance disruptors explosionVelocityBonus = stuff.getModifiedItemAttr("aoeVelocityBonus") @@ -186,7 +186,7 @@ class Miscellanea(ViewColumn): "flight time": flightTimeBonus, "missile velocity": missileVelocityBonus} - isGuidanceDisruptor = any(map(lambda x: x is not None and x != 0, guidanceDisruptorAttributes.values())) + isGuidanceDisruptor = any([x is not None and x != 0 for x in list(guidanceDisruptorAttributes.values())]) if isTrackingDisruptor: attributes = trackingDisruptorAttributes @@ -195,12 +195,12 @@ class Miscellanea(ViewColumn): else: return "", None - display = max(attributes.values(), key=lambda x: abs(x)) + display = max(list(attributes.values()), key=lambda x: abs(x)) text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) ttEntries = [] - for attributeName, attributeValue in attributes.items(): + for attributeName, attributeValue in list(attributes.items()): if attributeValue == display: ttEntries.append(attributeName) diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index 6bde57a11..5e717378f 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -42,7 +42,7 @@ class EntityEditor(wx.Panel): self.choices = [] self.choices.sort(key=lambda p: p.name) - self.entityChoices = wx.Choice(self, choices=map(lambda p: p.name, self.choices)) + self.entityChoices = wx.Choice(self, choices=[p.name for p in self.choices]) self.navSizer.Add(self.entityChoices, 1, wx.ALL, 5) buttons = (("new", wx.ART_NEW, self.OnNew), @@ -96,8 +96,8 @@ class EntityEditor(wx.Panel): def OnNew(self, event): dlg = TextEntryValidatedDialog(self, self.validator, - u"Enter a name for your new {}:".format(self.entityName), - u"New {}".format(self.entityName)) + "Enter a name for your new {}:".format(self.entityName), + "New {}".format(self.entityName)) dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: @@ -110,10 +110,10 @@ class EntityEditor(wx.Panel): def OnCopy(self, event): dlg = TextEntryValidatedDialog(self, self.validator, - u"Enter a name for your {} copy:".format(self.entityName), - u"Copy {}".format(self.entityName)) + "Enter a name for your {} copy:".format(self.entityName), + "Copy {}".format(self.entityName)) active = self.getActiveEntity() - dlg.SetValue(u"{} Copy".format(active.name)) + dlg.SetValue("{} Copy".format(active.name)) dlg.txtctrl.SetInsertionPointEnd() dlg.CenterOnParent() @@ -124,8 +124,8 @@ class EntityEditor(wx.Panel): def OnRename(self, event): dlg = TextEntryValidatedDialog(self, self.validator, - u"Enter a new name for your {}:".format(self.entityName), - u"Rename {}".format(self.entityName)) + "Enter a new name for your {}:".format(self.entityName), + "Rename {}".format(self.entityName)) active = self.getActiveEntity() dlg.SetValue(active.name) dlg.txtctrl.SetInsertionPointEnd() @@ -138,9 +138,9 @@ class EntityEditor(wx.Panel): def OnDelete(self, event): dlg = wx.MessageDialog(self, - u"Do you really want to delete the {} {}?".format(self.getActiveEntity().name, + "Do you really want to delete the {} {}?".format(self.getActiveEntity().name, self.entityName), - u"Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_YES: @@ -152,7 +152,7 @@ class EntityEditor(wx.Panel): self.choices = self.getEntitiesFromContext() self.entityChoices.Clear() - self.entityChoices.AppendItems(map(lambda p: p.name, self.choices)) + self.entityChoices.AppendItems([p.name for p in self.choices]) if selected: idx = self.choices.index(selected) self.entityChoices.SetSelection(idx) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index d2c4feaf4..1b9544e08 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -321,7 +321,7 @@ class FittingView(d.Display): fit = sFit.getFit(self.getActiveFit(), basic=True) bitmap = BitmapLoader.getImage("race_%s_small" % fit.ship.item.race, "gui") - text = u"%s: %s" % (fit.ship.item.name, fit.name) + text = "%s: %s" % (fit.ship.item.name, fit.name) pageIndex = self.parent.GetPageIndex(self) if pageIndex is not None: @@ -731,7 +731,7 @@ class FittingView(d.Display): break name = col.getText(st) - if not isinstance(name, basestring): + if not isinstance(name, str): name = "" nx, ny = tdc.GetTextExtent(name) @@ -761,7 +761,7 @@ class FittingView(d.Display): name = col.columnText imgId = col.imageId - if not isinstance(name, basestring): + if not isinstance(name, str): name = "" opts = wx.HeaderButtonParams() @@ -804,7 +804,7 @@ class FittingView(d.Display): name = col.columnText imgId = col.imageId - if not isinstance(name, basestring): + if not isinstance(name, str): name = "" opts = wx.HeaderButtonParams() @@ -838,7 +838,7 @@ class FittingView(d.Display): break name = col.getText(st) - if not isinstance(name, basestring): + if not isinstance(name, str): name = "" imgId = col.getImageId(st) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index ca3aacb70..bb8c52892 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx -from utils.floatspin import FloatSpin +from .utils.floatspin import FloatSpin # noinspection PyPackageRequirements import wx.lib.newevent # noinspection PyPackageRequirements @@ -57,9 +57,9 @@ class CharacterTextValidor(BaseValidator): raise ValueError("Character name already in use, please choose another.") return True - except ValueError, e: + except ValueError as e: pyfalog.error(e) - wx.MessageBox(u"{}".format(e), "Error") + wx.MessageBox("{}".format(e), "Error") textCtrl.SetFocus() return False @@ -128,7 +128,7 @@ 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, + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="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")) @@ -351,7 +351,7 @@ class SkillTreeView(wx.Panel): self.levelIds[idUnlearned] = "Not learned" self.levelChangeMenu.Append(idUnlearned, "Unlearn") - for level in xrange(6): + for level in range(6): id = wx.NewId() self.levelIds[id] = level self.levelChangeMenu.Append(id, "Level %d" % level) @@ -514,7 +514,7 @@ class SkillTreeView(wx.Panel): def _setTreeSkillLevel(treeItem, skillID): lvl, dirty = sChar.getSkillLevel(char.ID, skillID) self.skillTreeListCtrl.SetItemText(treeItem, - "Level {}".format(int(lvl)) if not isinstance(lvl, basestring) else lvl, + "Level {}".format(int(lvl)) if not isinstance(lvl, str) else lvl, 1) if not dirty: self.skillTreeListCtrl.SetItemTextColour(treeItem, None) @@ -611,16 +611,16 @@ class APIView(wx.Panel): self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - self.apiUrlCreatePredefined = u"https://community.eveonline.com/support/api-key/CreatePredefined?accessMask=8" - self.apiUrlKeyList = u"https://community.eveonline.com/support/api-key/" + self.apiUrlCreatePredefined = "https://community.eveonline.com/support/api-key/CreatePredefined?accessMask=8" + self.apiUrlKeyList = "https://community.eveonline.com/support/api-key/" pmainSizer = wx.BoxSizer(wx.VERTICAL) 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) + "You cannot add API Details for All 0 and All 5 characters.\n" + "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() @@ -632,21 +632,21 @@ class APIView(wx.Panel): fgSizerInput.SetFlexibleDirection(wx.BOTH) fgSizerInput.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - self.m_staticIDText = wx.StaticText(self, wx.ID_ANY, u"keyID:", wx.DefaultPosition, wx.DefaultSize, 0) + self.m_staticIDText = wx.StaticText(self, wx.ID_ANY, "keyID:", wx.DefaultPosition, wx.DefaultSize, 0) self.m_staticIDText.Wrap(-1) fgSizerInput.Add(self.m_staticIDText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5) self.inputID = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) fgSizerInput.Add(self.inputID, 1, wx.ALL | wx.EXPAND, 5) - self.m_staticKeyText = wx.StaticText(self, wx.ID_ANY, u"vCode:", wx.DefaultPosition, wx.DefaultSize, 0) + self.m_staticKeyText = wx.StaticText(self, wx.ID_ANY, "vCode:", wx.DefaultPosition, wx.DefaultSize, 0) self.m_staticKeyText.Wrap(-1) fgSizerInput.Add(self.m_staticKeyText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5) self.inputKey = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) fgSizerInput.Add(self.inputKey, 0, wx.ALL | wx.EXPAND, 5) - self.m_staticCharText = wx.StaticText(self, wx.ID_ANY, u"Character:", wx.DefaultPosition, wx.DefaultSize, 0) + self.m_staticCharText = wx.StaticText(self, wx.ID_ANY, "Character:", wx.DefaultPosition, wx.DefaultSize, 0) self.m_staticCharText.Wrap(-1) fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5) @@ -661,11 +661,11 @@ class APIView(wx.Panel): btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.AddStretchSpacer() - self.btnFetchCharList = wx.Button(self, wx.ID_ANY, u"Get Characters") + self.btnFetchCharList = wx.Button(self, wx.ID_ANY, "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") + self.btnFetchSkills = wx.Button(self, wx.ID_ANY, "Fetch Skills") btnSizer.Add(self.btnFetchSkills, 0, wx.ALL, 2) self.btnFetchSkills.Bind(wx.EVT_BUTTON, self.fetchSkills) self.btnFetchSkills.Enable(False) @@ -678,7 +678,7 @@ class APIView(wx.Panel): pmainSizer.AddStretchSpacer() self.stAPITip = wx.StaticText(self, wx.ID_ANY, - u"You can create a pre-defined key here (only CharacterSheet is required):", + "You can create a pre-defined key here (only CharacterSheet is required):", wx.DefaultPosition, wx.DefaultSize, 0) self.stAPITip.Wrap(-1) @@ -688,7 +688,7 @@ class APIView(wx.Panel): 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, + self.stAPITip2 = wx.StaticText(self, wx.ID_ANY, "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) @@ -747,15 +747,15 @@ class APIView(wx.Panel): try: activeChar = self.charEditor.entityEditor.getActiveEntity() list = sChar.apiCharList(activeChar.ID, self.inputID.GetLineText(0), self.inputKey.GetLineText(0)) - except AuthenticationError, e: + except AuthenticationError as e: msg = "Authentication failure. Please check keyID and vCode combination." pyfalog.info(msg) self.stStatus.SetLabel(msg) - except TimeoutError, e: + except TimeoutError as e: msg = "Request timed out. Please check network connectivity and/or proxy settings." pyfalog.info(msg) self.stStatus.SetLabel(msg) - except Exception, e: + except Exception as e: pyfalog.error(e) self.stStatus.SetLabel("Error:\n%s" % e.message) else: @@ -801,7 +801,7 @@ class SaveCharacterAs(wx.Dialog): bSizer1.Add(self.input, 1, wx.ALL, 5) self.input.Bind(wx.EVT_TEXT_ENTER, self.change) - self.button = wx.Button(self, wx.ID_OK, u"Save") + self.button = wx.Button(self, wx.ID_OK, "Save") bSizer1.Add(self.button, 0, wx.ALL, 5) self.SetSizer(bSizer1) @@ -828,7 +828,7 @@ class SecStatusDialog(wx.Dialog): bSizer1 = wx.BoxSizer(wx.VERTICAL) self.m_staticText1 = wx.StaticText(self, wx.ID_ANY, - u"Security Status is used in some CONCORD hull calculations; you can set the characters security status here", + "Security Status is used in some CONCORD hull calculations; you can set the characters security status here", wx.DefaultPosition, wx.DefaultSize, 0) self.m_staticText1.Wrap(-1) bSizer1.Add(self.m_staticText1, 1, wx.ALL | wx.EXPAND, 5) diff --git a/gui/characterSelection.py b/gui/characterSelection.py index 13df550e8..4b6947e4b 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -103,14 +103,14 @@ class CharacterSelection(wx.Panel): sChar = Character.getInstance() skillsMap = {} - for item, stuff in self.reqs.iteritems(): - for things in stuff.values(): + for item, stuff in self.reqs.items(): + for things in list(stuff.values()): if things[1] not in skillsMap: skillsMap[things[1]] = things[0] elif things[0] > skillsMap[things[1]]: skillsMap[things[1]] = things[0] - for skillID, level in skillsMap.iteritems(): + for skillID, level in skillsMap.items(): sChar.changeLevel(charID, skillID, level, ifHigher=True) self.refreshCharacterList() @@ -142,7 +142,7 @@ class CharacterSelection(wx.Panel): sFit = Fit.getInstance() sFit.changeChar(fitID, charID) - choice.Append(u"\u2015 Open Character Editor \u2015", -1) + choice.Append("\u2015 Open Character Editor \u2015", -1) self.charCache = self.charChoice.GetCurrentSelection() if event is not None: @@ -251,11 +251,11 @@ class CharacterSelection(wx.Panel): sCharacter = Character.getInstance() if tabulationLevel == 0: - for item, subReqs in reqs.iteritems(): + for item, subReqs in reqs.items(): tip += "%s:\n" % item.name tip += self._buildSkillsTooltip(subReqs, item.name, 1) else: - for name, info in reqs.iteritems(): + for name, info in reqs.items(): level, ID, more = info sCharacter.skillReqsDict['skills'].append({ 'item': currItem, @@ -277,11 +277,11 @@ class CharacterSelection(wx.Panel): sCharacter = Character.getInstance() if tabulationLevel == 0: - for item, subReqs in reqs.iteritems(): + for item, subReqs in reqs.items(): skillsMap = self._buildSkillsTooltipCondensed(subReqs, item.name, 1, skillsMap) sorted(skillsMap, key=skillsMap.get) else: - for name, info in reqs.iteritems(): + for name, info in reqs.items(): level, ID, more = info sCharacter.skillReqsDict['skills'].append({ 'item': currItem, diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 155a42787..a82a78025 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -72,7 +72,7 @@ class ContextMenu(object): display_amount += 1 texts = m.getText(itemContext, selection) - if isinstance(texts, basestring): + if isinstance(texts, str): texts = (texts,) bitmap = m.getBitmap(srcContext, selection) diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index 57ce48f47..d2e3a656c 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -31,21 +31,21 @@ 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), + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="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"] - copyFormatTooltips = {CopySelectDialog.copyFormatEft: u"EFT text format", - CopySelectDialog.copyFormatEftImps: u"EFT text format", - CopySelectDialog.copyFormatXml: u"EVE native XML format", - 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, + copyFormats = ["EFT", "EFT (Implants)", "XML", "DNA", "CREST", "MultiBuy"] + copyFormatTooltips = {CopySelectDialog.copyFormatEft: "EFT text format", + CopySelectDialog.copyFormatEftImps: "EFT text format", + CopySelectDialog.copyFormatXml: "EVE native XML format", + CopySelectDialog.copyFormatDna: "A one-line text format", + CopySelectDialog.copyFormatCrest: "A JSON format used for EVE CREST", + CopySelectDialog.copyFormatMultiBuy: "MultiBuy text format"} + selector = wx.RadioBox(self, wx.ID_ANY, label="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(): + for format, tooltip in copyFormatTooltips.items(): selector.SetItemToolTip(format, tooltip) self.copyFormat = CopySelectDialog.copyFormatEft diff --git a/gui/crestFittings.py b/gui/crestFittings.py index c02801268..7b2bc4802 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -45,7 +45,7 @@ class CrestFittings(wx.Frame): 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) + self.fetchBtn = wx.Button(self, wx.ID_ANY, "Fetch Fits", wx.DefaultPosition, wx.DefaultSize, 5) characterSelectSizer.Add(self.fetchBtn, 0, wx.ALL, 5) mainSizer.Add(characterSelectSizer, 0, wx.EXPAND, 5) @@ -64,8 +64,8 @@ class CrestFittings(wx.Frame): 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) + self.importBtn = wx.Button(self, wx.ID_ANY, "Import to pyfa", wx.DefaultPosition, wx.DefaultSize, 5) + self.deleteBtn = wx.Button(self, wx.ID_ANY, "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) @@ -207,7 +207,7 @@ class ExportToEve(wx.Frame): self.updateCharList() self.charChoice.SetSelection(0) - self.exportBtn = wx.Button(self, wx.ID_ANY, u"Export Fit", wx.DefaultPosition, wx.DefaultSize, 5) + self.exportBtn = wx.Button(self, wx.ID_ANY, "Export Fit", wx.DefaultPosition, wx.DefaultSize, 5) hSizer.Add(self.exportBtn, 0, wx.ALL, 5) mainSizer.Add(hSizer, 0, wx.EXPAND, 5) @@ -314,10 +314,10 @@ class CrestMgmt(wx.Dialog): btnSizer = wx.BoxSizer(wx.VERTICAL) - self.addBtn = wx.Button(self, wx.ID_ANY, u"Add Character", wx.DefaultPosition, wx.DefaultSize, 0) + self.addBtn = wx.Button(self, wx.ID_ANY, "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) + self.deleteBtn = wx.Button(self, wx.ID_ANY, "Revoke Character", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.deleteBtn, 0, wx.ALL | wx.EXPAND, 5) mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) @@ -397,7 +397,7 @@ class FittingsTreeView(wx.Panel): dict[fit['ship']['name']] = [] dict[fit['ship']['name']].append(fit) - for name, fits in dict.iteritems(): + for name, fits in dict.items(): shipID = tree.AppendItem(root, name) for fit in fits: fitId = tree.AppendItem(shipID, fit['name']) diff --git a/gui/display.py b/gui/display.py index 2ae83aa44..0462fdf24 100644 --- a/gui/display.py +++ b/gui/display.py @@ -55,7 +55,7 @@ class Display(wx.ListCtrl): name, type, defaultValue = param value = params[x] if len(params) > x else defaultValue value = value if value != "" else defaultValue - if type == bool and isinstance(value, basestring): + if type == bool and isinstance(value, str): value = bool(value) if value.lower() != "false" and value != "0" else False paramDict[name] = value col = colClass(self, paramDict) @@ -219,13 +219,13 @@ class Display(wx.ListCtrl): if listItemCount < stuffItemCount: for i in range(stuffItemCount - listItemCount): - self.InsertStringItem(sys.maxint, "") + self.InsertStringItem(sys.maxsize, "") if listItemCount > stuffItemCount: if listItemCount - stuffItemCount > 20 > stuffItemCount: self.DeleteAllItems() for i in range(stuffItemCount): - self.InsertStringItem(sys.maxint, "") + self.InsertStringItem(sys.maxsize, "") else: for i in range(listItemCount - stuffItemCount): self.DeleteItem(self.getLastItem()) @@ -247,7 +247,7 @@ class Display(wx.ListCtrl): newText = col.getText(st) if newText is False: col.delayedText(st, self, colItem) - newText = u"\u21bb" + newText = "\u21bb" newImageId = col.getImageId(st) diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 9c58a32b6..178e9c8d9 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -93,13 +93,13 @@ class GraphFrame(wx.Frame): graphFrame_enabled = True if int(mpl.__version__[0]) < 1: - print("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds") + print(("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds")) print("pyfa: Recommended minimum matplotlib version is 1.0.0") self.legendFix = True mplImported = True - wx.Frame.__init__(self, parent, title=u"pyfa: Graph Generator", style=style, size=(520, 390)) + wx.Frame.__init__(self, parent, title="pyfa: Graph Generator", style=style, size=(520, 390)) i = wx.IconFromBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) self.SetIcon(i) @@ -179,7 +179,7 @@ class GraphFrame(wx.Frame): def getValues(self): values = {} - for fieldName, field in self.fields.iteritems(): + for fieldName, field in self.fields.items(): values[fieldName] = field.GetValue() return values @@ -193,14 +193,14 @@ class GraphFrame(wx.Frame): self.fields.clear() # Setup textboxes - for field, defaultVal in view.getFields().iteritems(): + for field, defaultVal in view.getFields().items(): 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): + if not isinstance(defaultVal, str): defaultVal = ("%f" % defaultVal).rstrip("0") if defaultVal[-1:] == ".": defaultVal += "0" @@ -248,8 +248,8 @@ class GraphFrame(wx.Frame): self.subplot.plot(x, y) legend.append(fit.name) except: - pyfalog.warning(u"Invalid values in '{0}'", fit.name) - self.SetStatusText(u"Invalid values in '%s'" % fit.name) + pyfalog.warning("Invalid values in '{0}'", fit.name) + self.SetStatusText("Invalid values in '%s'" % fit.name) self.canvas.draw() return diff --git a/gui/itemStats.py b/gui/itemStats.py index 2c9ce4a5d..90f3172e4 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -113,7 +113,7 @@ class ItemStatsDialog(wx.Dialog): 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.closeBtn = wx.Button(self, wx.ID_ANY, "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) @@ -294,19 +294,19 @@ class ItemParams(wx.Panel): 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) + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", 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, + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "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, + self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, "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) + self.refreshBtn = wx.Button(self, wx.ID_ANY, "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) @@ -426,7 +426,7 @@ class ItemParams(wx.Panel): self.imageList = wx.ImageList(16, 16) self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) - names = list(self.attrValues.iterkeys()) + names = list(self.attrValues.keys()) names.sort() idNameMap = {} @@ -463,7 +463,7 @@ class ItemParams(wx.Panel): else: attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) - index = self.paramList.InsertImageStringItem(sys.maxint, attrName, attrIcon) + index = self.paramList.InsertImageStringItem(sys.maxsize, attrName, attrIcon) idNameMap[idCount] = attrName self.paramList.SetItemData(index, idCount) idCount += 1 @@ -510,7 +510,7 @@ class ItemParams(wx.Panel): "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"), + "Volume" : (lambda: value, "m\u00B3"), "Sizeclass" : (lambda: value, ""), "Absolute Percent" : (lambda: (value * 100), unitName), "Milliseconds" : (lambda: value / 1000.0, unitName), @@ -524,7 +524,7 @@ class ItemParams(wx.Panel): v = override[0]() if isinstance(v, str): fvalue = v - elif isinstance(v, (int, float, long)): + elif isinstance(v, (int, float)): fvalue = formatAmount(v, 3, 0, 0) else: fvalue = v @@ -558,12 +558,12 @@ class ItemCompare(wx.Panel): # get a dict of attrName: attrInfo of all unique attributes across all items for item in self.items: - for attr in item.attributes.keys(): + for attr in list(item.attributes.keys()): if item.attributes[attr].info.displayName: self.attrs[attr] = item.attributes[attr].info # Process attributes for items and find ones that differ - for attr in self.attrs.keys(): + for attr in list(self.attrs.keys()): value = None for item in self.items: @@ -588,14 +588,14 @@ class ItemCompare(wx.Panel): 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) + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", 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, + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle view mode", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, + self.refreshBtn = wx.Button(self, wx.ID_ANY, "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) @@ -649,7 +649,7 @@ class ItemCompare(wx.Panel): try: # Remember to reduce by 1, because the attrs array # starts at 0 while the list has the item name as column 0. - attr = str(self.attrs.keys()[sort - 1]) + attr = str(list(self.attrs.keys())[sort - 1]) func = lambda _val: _val.attributes[attr].value if attr in _val.attributes else None except IndexError: # Clicked on a column that's not part of our array (price most likely) @@ -670,7 +670,7 @@ class ItemCompare(wx.Panel): self.paramList.SetColumnWidth(len(self.attrs) + 1, 60) for item in self.items: - i = self.paramList.InsertStringItem(sys.maxint, item.name) + i = self.paramList.InsertStringItem(sys.maxsize, item.name) for x, attr in enumerate(self.attrs.keys()): if attr in item.attributes: info = self.attrs[attr] @@ -708,7 +708,7 @@ class ItemCompare(wx.Panel): "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"), + "Volume" : (lambda: value, "m\u00B3"), "Sizeclass" : (lambda: value, ""), "Absolute Percent" : (lambda: (value * 100), unitName), "Milliseconds" : (lambda: value / 1000.0, unitName), @@ -722,7 +722,7 @@ class ItemCompare(wx.Panel): v = override[0]() if isinstance(v, str): fvalue = v - elif isinstance(v, (int, float, long)): + elif isinstance(v, (int, float)): fvalue = formatAmount(v, 3, 0, 0) else: fvalue = v @@ -759,7 +759,7 @@ class ItemRequirements(wx.Panel): self.Layout() def getFullSkillTree(self, parentSkill, parent, sbIconId): - for skill, level in parentSkill.requiredSkills.iteritems(): + for skill, level in parentSkill.requiredSkills.items(): 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) @@ -794,7 +794,7 @@ class ItemDependents(wx.Panel): def getFullSkillTree(self, parentSkill, parent, sbIconId): levelToItems = {} - for item, level in parentSkill.requiredFor.iteritems(): + for item, level in parentSkill.requiredFor.items(): if level not in levelToItems: levelToItems[level] = [] levelToItems[level].append(item) @@ -853,11 +853,11 @@ class ItemEffects(wx.Panel): item = self.item effects = item.effects - names = list(effects.iterkeys()) + names = list(effects.keys()) names.sort() for name in names: - index = self.effectList.InsertStringItem(sys.maxint, name) + index = self.effectList.InsertStringItem(sys.maxsize, name) if effects[name].isImplemented: if effects[name].activeByDefault: @@ -962,17 +962,17 @@ class ItemAffectedBy(wx.Panel): 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) + self.toggleExpandBtn = wx.ToggleButton(self, wx.ID_ANY, "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) + self.toggleNameBtn = wx.ToggleButton(self, wx.ID_ANY, "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) + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "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) + self.refreshBtn = wx.Button(self, wx.ID_ANY, "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) @@ -995,7 +995,7 @@ class ItemAffectedBy(wx.Panel): stuff = self.affectedBy.GetPyData(item) # String is set as data when we are dealing with attributes, not stuff containers - if stuff is None or isinstance(stuff, basestring): + if stuff is None or isinstance(stuff, str): return contexts = [] @@ -1104,7 +1104,7 @@ class ItemAffectedBy(wx.Panel): if attributes[attrName] == (attributes.getOriginal(attrName, 0)): continue - for fit, afflictors in attributes.getAfflictions(attrName).iteritems(): + for fit, afflictors in attributes.getAfflictions(attrName).items(): for afflictor, modifier, amount, used in afflictors: if not used or afflictor.item is None: @@ -1135,7 +1135,7 @@ class ItemAffectedBy(wx.Panel): (type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) # Make sure projected fits are on top - rootOrder = container.keys() + rootOrder = list(container.keys()) rootOrder.sort(key=lambda x: self.ORDER.index(type(x))) # Now, we take our created dictionary and start adding stuff to our tree @@ -1149,7 +1149,7 @@ class ItemAffectedBy(wx.Panel): parent = child attributes = container[thing] - attrOrder = sorted(attributes.keys(), key=self.sortAttrDisplayName) + attrOrder = sorted(list(attributes.keys()), key=self.sortAttrDisplayName) for attrName in attrOrder: attrInfo = self.stuff.item.attributes.get(attrName) @@ -1233,7 +1233,7 @@ class ItemAffectedBy(wx.Panel): if attributes[attrName] == (attributes.getOriginal(attrName, 0)): continue - for fit, afflictors in attributes.getAfflictions(attrName).iteritems(): + for fit, afflictors in attributes.getAfflictions(attrName).items(): for afflictor, modifier, amount, used in afflictors: if not used or getattr(afflictor, 'item', None) is None: continue @@ -1269,7 +1269,7 @@ class ItemAffectedBy(wx.Panel): info[2].append((attrName, modifier, amount)) # Make sure projected fits are on top - rootOrder = container.keys() + rootOrder = list(container.keys()) rootOrder.sort(key=lambda x: self.ORDER.index(type(x))) # Now, we take our created dictionary and start adding stuff to our tree @@ -1283,7 +1283,7 @@ class ItemAffectedBy(wx.Panel): parent = child items = container[thing] - order = items.keys() + order = list(items.keys()) order.sort(key=lambda x: (self.ORDER.index(items[x][0]), x)) for itemName in order: @@ -1387,7 +1387,7 @@ class ItemProperties(wx.Panel): 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) + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT) @@ -1439,7 +1439,7 @@ class ItemProperties(wx.Panel): attrName = name.title() value = getattr(self.item, name) - index = self.paramList.InsertStringItem(sys.maxint, attrName) + index = self.paramList.InsertStringItem(sys.maxsize, attrName) # index = self.paramList.InsertImageStringItem(sys.maxint, attrName) idNameMap[idCount] = attrName self.paramList.SetItemData(index, idCount) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index b5d859149..a7eafadcd 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -93,7 +93,7 @@ try: from gui.propertyEditor import AttributeEditor except ImportError as e: AttributeEditor = None - print("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message) + print(("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message)) disableOverrideEditor = True pyfalog = Logger(__name__) @@ -413,7 +413,7 @@ class MainFrame(wx.Frame, IPortUser): """ Export active fit """ sFit = Fit.getInstance() fit = sFit.getFit(self.getActiveFit()) - defaultFile = u"%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", @@ -427,7 +427,7 @@ class MainFrame(wx.Frame, IPortUser): if '.' not in os.path.basename(path): path += ".xml" else: - print("oops, invalid fit format %d" % format_) + print(("oops, invalid fit format %d" % format_)) try: dlg.Destroy() except PyDeadObjectError: diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index b67950503..f097f4a09 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -65,7 +65,7 @@ class MarketBrowser(wx.Panel): vbox.Add(p, 0, wx.EXPAND) self.metaButtons = [] btn = None - for name in self.sMkt.META_MAP.keys(): + for name in list(self.sMkt.META_MAP.keys()): btn = MetaButton(p, wx.ID_ANY, name.capitalize(), style=wx.BU_EXACTFIT) setattr(self, name, btn) box.Add(btn, 1, wx.ALIGN_CENTER) diff --git a/gui/patternEditor.py b/gui/patternEditor.py index 879683373..4fe15077e 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -51,7 +51,7 @@ class DmgPatternTextValidor(BaseValidator): return True except ValueError as e: pyfalog.error(e) - wx.MessageBox(u"{}".format(e), "Error") + wx.MessageBox("{}".format(e), "Error") textCtrl.SetFocus() return False @@ -89,7 +89,7 @@ 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="Damage Pattern Editor", size=wx.Size(400, 240)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -128,7 +128,7 @@ class DmgPatternEditorDlg(wx.Dialog): # 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%")) + setattr(self, "%sPerc" % type_, wx.StaticText(self, wx.ID_ANY, "0%")) editObj = getattr(self, "%sEdit" % type_) dmgeditSizer.Add(bmp, 0, style, border) @@ -147,7 +147,7 @@ class DmgPatternEditorDlg(wx.Dialog): footerSizer = wx.BoxSizer(wx.HORIZONTAL) perSizer = wx.BoxSizer(wx.VERTICAL) - self.stNotice = wx.StaticText(self, wx.ID_ANY, u"") + self.stNotice = wx.StaticText(self, wx.ID_ANY, "") self.stNotice.Wrap(-1) perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) @@ -160,7 +160,7 @@ 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) + self.closeBtn = wx.Button(self, wx.ID_ANY, "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) @@ -200,7 +200,7 @@ class DmgPatternEditorDlg(wx.Dialog): return p = self.entityEditor.getActiveEntity() - total = sum(map(lambda attr: getattr(self, "%sEdit" % attr).GetValue(), self.DAMAGE_TYPES)) + total = sum([getattr(self, "%sEdit" % attr).GetValue() for attr in self.DAMAGE_TYPES]) for type_ in self.DAMAGE_TYPES: editObj = getattr(self, "%sEdit" % type_) percObj = getattr(self, "%sPerc" % type_) diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index 2ed0f829a..d51ee2515 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -47,7 +47,7 @@ class PreferenceDialog(wx.Dialog): 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) + self.btnOK = wx.Button(self, wx.ID_ANY, "OK", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnOK, 0, wx.ALL, 5) mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) self.SetSizer(mainSizer) diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index b6f4ab1a1..658e0bc21 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -70,7 +70,7 @@ 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, + self.btnRemoveOverrides = wx.Button(panel, wx.ID_ANY, "Remove Overides for Item", wx.DefaultPosition, wx.DefaultSize, 0) self.pg = AttributeGrid(panel) rightSizer.Add(self.pg, 1, wx.ALL | wx.EXPAND, 5) @@ -126,7 +126,7 @@ class AttributeEditor(wx.Frame): with open(path, 'wb') as csvfile: writer = csv.writer(csvfile) for item in items: - for key, override in item.overrides.iteritems(): + for key, override in item.overrides.items(): writer.writerow([item.ID, override.attrID, override.value]) def OnClear(self, event): @@ -145,7 +145,7 @@ class AttributeEditor(wx.Frame): # them due to the eve/user database disconnect. We must loop through # all items that have overrides and remove them for item in items: - for _, x in item.overrides.items(): + for _, x in list(item.overrides.items()): item.deleteOverride(x.attr) self.itemView.updateItems(True) self.pg.Clear() @@ -247,7 +247,7 @@ class AttributeGrid(wxpg.PropertyGrid): if self.item is None: return - for x in self.item.overrides.values(): + for x in list(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 cdd4398bb..ae653e11b 100644 --- a/gui/pyfatogglepanel.py +++ b/gui/pyfatogglepanel.py @@ -74,7 +74,7 @@ class TogglePanel(wx.Panel): hbmpSizer.Add(self.headerBmp, 0, 0, 5) - self.headerLabel = wx.StaticText(self.headerPanel, wx.ID_ANY, u"PYFA", wx.DefaultPosition, wx.DefaultSize, 0) + self.headerLabel = wx.StaticText(self.headerPanel, wx.ID_ANY, "PYFA", wx.DefaultPosition, wx.DefaultSize, 0) hlblSizer.Add(self.headerLabel, 0, wx.EXPAND, 5) headerSizer.Add(hbmpSizer, 0, wx.RIGHT, 5) diff --git a/gui/pygauge.py b/gui/pygauge.py index dbb0c4531..f670c1427 100644 --- a/gui/pygauge.py +++ b/gui/pygauge.py @@ -379,7 +379,7 @@ class PyGauge(wx.PyWindow): r.left += 1 r.top += 1 if self._range == 0.01 and self._value > 0: - formatStr = u'\u221e' + formatStr = '\u221e' dc.SetTextForeground(wx.Colour(80, 80, 80)) dc.DrawLabel(formatStr, r, wx.ALIGN_CENTER) diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index c8bab84bb..76ea7551a 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -49,7 +49,7 @@ class TargetResistsTextValidor(BaseValidator): return True except ValueError as e: pyfalog.error(e) - wx.MessageBox(u"{}".format(e), "Error") + wx.MessageBox("{}".format(e), "Error") textCtrl.SetFocus() return False @@ -87,7 +87,7 @@ 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="Target Resists Editor", size=wx.Size(350, 240)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -124,7 +124,7 @@ 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, + resistEditSizer.Add(wx.StaticText(self, wx.ID_ANY, "%", wx.DefaultPosition, wx.DefaultSize, 0), 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated) @@ -138,7 +138,7 @@ class ResistsEditorDlg(wx.Dialog): footerSizer = wx.BoxSizer(wx.HORIZONTAL) perSizer = wx.BoxSizer(wx.VERTICAL) - self.stNotice = wx.StaticText(self, wx.ID_ANY, u"") + self.stNotice = wx.StaticText(self, wx.ID_ANY, "") self.stNotice.Wrap(-1) perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) @@ -151,7 +151,7 @@ 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) + self.closeBtn = wx.Button(self, wx.ID_ANY, "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) diff --git a/gui/setEditor.py b/gui/setEditor.py index d199c1cc2..1a9295dea 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -50,7 +50,7 @@ class ImplantTextValidor(BaseValidator): return True except ValueError as e: pyfalog.error(e) - wx.MessageBox(u"{}".format(e), "Error") + wx.MessageBox("{}".format(e), "Error") textCtrl.SetFocus() return False @@ -114,7 +114,7 @@ class ImplantSetEditor(BaseImplantEditorView): 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="Implant Set Editor", size=wx.Size(640, 600)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -135,12 +135,12 @@ class ImplantSetEditorDlg(wx.Dialog): footerSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stNotice = wx.StaticText(self, wx.ID_ANY, u"") + self.stNotice = wx.StaticText(self, wx.ID_ANY, "") self.stNotice.Wrap(-1) 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) + self.closeBtn = wx.Button(self, wx.ID_ANY, "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) diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 2caa74f11..5f21f489a 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -214,12 +214,12 @@ 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.items(): if race in racesList: subRacesFilter[race] = self.racesFilter[race] override = True - for race, state in subRacesFilter.iteritems(): + for race, state in subRacesFilter.items(): if state: override = False break @@ -382,7 +382,7 @@ class ShipBrowser(wx.Panel): self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID)) if len(ships) == 0 and len(fitList) == 0: - self.lpane.AddWidget(PFStaticText(self.lpane, label=u"No matching results.")) + self.lpane.AddWidget(PFStaticText(self.lpane, label="No matching results.")) self.lpane.RefreshList(doFocus=False) self.lpane.Thaw() diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index ba075a35f..984505021 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -67,7 +67,7 @@ class exportHtmlThread(threading.Thread): 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: @@ -222,7 +222,7 @@ class exportHtmlThread(threading.Thread): return try: dnaFit = Port.exportDna(getFit(fit[0])) - print dnaFit + print(dnaFit) HTMLship += '
  • ' + fit[ 1] + '
  • \n' except: diff --git a/gui/utils/floatspin.py b/gui/utils/floatspin.py index b2e21f178..5be9d7bf3 100644 --- a/gui/utils/floatspin.py +++ b/gui/utils/floatspin.py @@ -410,7 +410,7 @@ class FloatSpin(wx.PyControl): height = best_size.GetHeight() self._validkeycode = [43, 44, 45, 46, 69, 101, 127, 314] - self._validkeycode.extend(range(48, 58)) + self._validkeycode.extend(list(range(48, 58))) self._validkeycode.extend([wx.WXK_RETURN, wx.WXK_TAB, wx.WXK_BACK, wx.WXK_LEFT, wx.WXK_RIGHT]) @@ -1282,8 +1282,8 @@ class FixedPoint(object): self.n = n return - if isinstance(value, type(42)) or isinstance(value, type(42L)): - self.n = long(value) * _tento(p) + if isinstance(value, type(42)) or isinstance(value, type(42)): + self.n = int(value) * _tento(p) return if isinstance(value, FixedPoint): @@ -1303,7 +1303,7 @@ class FixedPoint(object): # up all bits in 2 iterations for all known binary double- # precision formats, and small enough to fit in an int. CHUNK = 28 - top = 0L + top = 0 # invariant: |value| = (top + f) * 2**e exactly while f: f = math.ldexp(f, CHUNK) @@ -1321,7 +1321,7 @@ class FixedPoint(object): if e >= 0: n = top << e else: - n = _roundquotient(top, 1L << -e) + n = _roundquotient(top, 1 << -e) if value < 0: n = -n self.n = n @@ -1329,7 +1329,7 @@ class FixedPoint(object): if isinstance(value, type(42 - 42j)): raise TypeError("can't convert complex to FixedPoint: " + - `value`) + repr(value)) # can we coerce to a float? yes = 1 @@ -1344,14 +1344,14 @@ class FixedPoint(object): # similarly for long yes = 1 try: - aslong = long(value) + aslong = int(value) except: yes = 0 if yes: self.__init__(aslong, p) return - raise TypeError("can't convert to FixedPoint: " + `value`) + raise TypeError("can't convert to FixedPoint: " + repr(value)) def get_precision(self): """ @@ -1378,9 +1378,9 @@ class FixedPoint(object): p = int(precision) except: raise TypeError("precision not convertable to int: " + - `precision`) + repr(precision)) if p < 0: - raise ValueError("precision must be >= 0: " + `precision`) + raise ValueError("precision must be >= 0: " + repr(precision)) if p > self.p: self.n = self.n * _tento(p - self.p) @@ -1403,7 +1403,7 @@ class FixedPoint(object): def __repr__(self): - return "FixedPoint" + `(str(self), self.p)` + return "FixedPoint" + repr((str(self), self.p)) def copy(self): """ Create a copy of the current :class:`FixedPoint`. """ @@ -1434,7 +1434,7 @@ class FixedPoint(object): # a float, their hashes may differ. This is a teensy bit Bad. return hash(n) ^ hash(p) - def __nonzero__(self): + def __bool__(self): return self.n != 0 def __neg__(self): @@ -1531,7 +1531,7 @@ class FixedPoint(object): """ - return self - long(self) + return self - int(self) # return n, p s.t. self == n/10**p and n % 10 != 0 def __reduce(self): @@ -1550,7 +1550,7 @@ def _tento(n, cache={}): try: return cache[n] except KeyError: - answer = cache[n] = 10L ** n + answer = cache[n] = 10 ** n return answer @@ -1632,7 +1632,7 @@ del re def _string2exact(s): m = _parser(s) if m is None: - raise ValueError("can't parse as number: " + `s`) + raise ValueError("can't parse as number: " + repr(s)) exp = m.group('exp') if exp is None: @@ -1651,7 +1651,7 @@ def _string2exact(s): assert intpart assert fracpart - i, f = long(intpart), long(fracpart) + i, f = int(intpart), int(fracpart) nfrac = len(fracpart) i = i * _tento(nfrac) + f exp = exp - nfrac diff --git a/gui/utils/helpers_wxPython.py b/gui/utils/helpers_wxPython.py index 79f8ed206..e0f9d0ae0 100644 --- a/gui/utils/helpers_wxPython.py +++ b/gui/utils/helpers_wxPython.py @@ -1,7 +1,7 @@ import wx -def YesNoDialog(question=u'Are you sure you want to do this?', caption=u'Yes or no?'): +def YesNoDialog(question='Are you sure you want to do this?', caption='Yes or no?'): dlg = wx.MessageDialog(None, question, caption, wx.YES_NO | wx.ICON_QUESTION) result = dlg.ShowModal() == wx.ID_YES dlg.Destroy() diff --git a/gui/utils/listFormatter.py b/gui/utils/listFormatter.py index 28612d0ab..4f059f36e 100644 --- a/gui/utils/listFormatter.py +++ b/gui/utils/listFormatter.py @@ -5,5 +5,5 @@ def formatList(words): if len(words) == 1: return words[0] last = words[-1:][0] - beginning = u", ".join(words[:-1]) - return u"{0} and {1}".format(beginning, last) + beginning = ", ".join(words[:-1]) + return "{0} and {1}".format(beginning, last) diff --git a/gui/utils/numberFormatter.py b/gui/utils/numberFormatter.py index 46aaf7d5a..c79c9e50b 100644 --- a/gui/utils/numberFormatter.py +++ b/gui/utils/numberFormatter.py @@ -17,13 +17,13 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal return "" # Define suffix maps posSuffixMap = {3: "k", 6: "M", 9: "B" if currency is True else "G"} - negSuffixMap = {-6: u'\u03bc', -3: "m"} + negSuffixMap = {-6: '\u03bc', -3: "m"} # Define tuple of the map keys # As we're going to go from the biggest order of abs(key), sort # them differently due to one set of values being negative # and other positive - posOrders = tuple(sorted(posSuffixMap.iterkeys(), reverse=True)) - negOrders = tuple(sorted(negSuffixMap.iterkeys(), reverse=False)) + posOrders = tuple(sorted(iter(posSuffixMap.keys()), reverse=True)) + negOrders = tuple(sorted(iter(negSuffixMap.keys()), reverse=False)) # Find the least abs(key) posLowest = min(posOrders) negHighest = max(negOrders) @@ -89,7 +89,7 @@ def formatAmount(val, prec=3, lowest=0, highest=0, currency=False, forceSign=Fal mantissa = roundToPrec(mantissa, prec) sign = "+" if forceSign is True and mantissa > 0 else "" # Round mantissa and add suffix - result = u"{0}{1}{2}".format(sign, mantissa, suffix) + result = "{0}{1}{2}".format(sign, mantissa, suffix) return result diff --git a/pyfa.py b/pyfa.py index 4faa1eddf..fd205f6df 100755 --- a/pyfa.py +++ b/pyfa.py @@ -137,9 +137,9 @@ def handleGUIException(exc_type, exc_value, exc_traceback): pyfalog.info("Imported Python Modules:") for imported_module in module_list: module_details = sys.modules[imported_module] - print(str(imported_module) + ": " + str(getattr(module_details, '__version__', ''))) + print((str(imported_module) + ": " + str(getattr(module_details, '__version__', '')))) - print("Exception in main thread: " + str(exc_value.message)) + print(("Exception in main thread: " + str(exc_value.message))) traceback.print_tb(exc_traceback) if wx and ErrorFrame: @@ -200,7 +200,7 @@ if __name__ == "__main__": # convert to unicode if it is set if options.savepath is not None: - options.savepath = unicode(options.savepath) + options.savepath = str(options.savepath) config.defPaths(options.savepath) # Basic logging initialization diff --git a/scripts/conversion.py b/scripts/conversion.py index 78c2f6ad4..65d3cc66d 100644 --- a/scripts/conversion.py +++ b/scripts/conversion.py @@ -7,7 +7,7 @@ import sqlite3 import sys # Add eos root path to sys.path so we can import ourselves -path = os.path.dirname(unicode(__file__, sys.getfilesystemencoding())) +path = os.path.dirname(str(__file__, sys.getfilesystemencoding())) sys.path.append(os.path.realpath(os.path.join(path, ".."))) # change to correct conversion @@ -275,14 +275,14 @@ def main(old, new): c = x.split(rename_phrase) container = renames else: - print "Unknown format: {}".format(x) + print("Unknown format: {}".format(x)) sys.exit() old_name, new_name = c[0], c[1] old_item, new_item = None, None if "Blueprint" in old_name or "Blueprint" in new_name: - print "Blueprint: Skipping this line: %s"%x + print("Blueprint: Skipping this line: %s"%x) continue # gather item info @@ -297,9 +297,9 @@ def main(old, new): break if not old_item: - print "Error finding old item in {} -> {}".format(old_name, new_name) + print("Error finding old item in {} -> {}".format(old_name, new_name)) if not new_item: - print "Error finding new item in {} -> {}".format(old_name, new_name) + print("Error finding new item in {} -> {}".format(old_name, new_name)) if not container.get((new_item,new_name), None): container[(new_item,new_name)] = [] @@ -307,31 +307,31 @@ def main(old, new): container[(new_item,new_name)].append((old_item, old_name)) - print " # Renamed items" + print(" # Renamed items") - for new, old in renames.iteritems(): + for new, old in renames.items(): if len(old) != 1: - print "Incorrect length, key: {}, value: {}".format(new, old) + print("Incorrect length, key: {}, value: {}".format(new, old)) sys.exit() old = old[0] - print " \"{}\": \"{}\",".format(old[1], new[1]) + print(" \"{}\": \"{}\",".format(old[1], new[1])) # Convert modules - print "\n # Converted items" + print("\n # Converted items") - for new, olds in conversions.iteritems(): + for new, olds in conversions.items(): for old in olds: - print " \"{}\": \"{}\",".format(old[1], new[1]) + print(" \"{}\": \"{}\",".format(old[1], new[1])) - print - print + print() + print() - for new, old in conversions.iteritems(): - print " {}: ( # {}".format(new[0], new[1]) + for new, old in conversions.items(): + print(" {}: ( # {}".format(new[0], new[1])) for item in old: - print " {}, # {}".format(item[0], item[1]) - print " )," + print(" {}, # {}".format(item[0], item[1])) + print(" ),") if __name__ == "__main__": diff --git a/scripts/dist.py b/scripts/dist.py index 588418a39..07b4ec473 100755 --- a/scripts/dist.py +++ b/scripts/dist.py @@ -87,7 +87,7 @@ if __name__ == "__main__": options, args = parser.parse_args() if options.skeleton is None or options.base is None or options.destination is None: - print "Need --skeleton argument as well as --base and --destination argument" + print("Need --skeleton argument as well as --base and --destination argument") parser.print_help() sys.exit() @@ -100,20 +100,20 @@ if __name__ == "__main__": if skel not in options.platforms: continue - print "\n======== %s ========"%skel + print("\n======== %s ========"%skel) info = {} config = {} setup = {} skeleton = os.path.expanduser(os.path.join(options.skeleton, skel)) - execfile(os.path.join(options.base, "config.py"), config) - execfile(os.path.join(skeleton, "info.py"), info) - execfile(os.path.join(options.base, "setup.py"), setup) + exec(compile(open(os.path.join(options.base, "config.py")).read(), os.path.join(options.base, "config.py"), 'exec'), config) + exec(compile(open(os.path.join(skeleton, "info.py")).read(), os.path.join(skeleton, "info.py"), 'exec'), info) + exec(compile(open(os.path.join(options.base, "setup.py")).read(), os.path.join(options.base, "setup.py"), 'exec'), setup) destination = os.path.expanduser(options.destination) if not os.path.isdir(destination) or not os.access(destination, os.W_OK | os.X_OK): - print "Destination directory does not exist or is not writable: {}".format(destination) + print("Destination directory does not exist or is not writable: {}".format(destination)) sys.exit() dirName = info["arcname"] @@ -143,9 +143,9 @@ if __name__ == "__main__": tmpFile = os.path.join(os.getcwd(), archiveName) try: - print "Copying skeleton to ", tmpDir + print("Copying skeleton to ", tmpDir) shutil.copytree(skeleton, tmpDir, ignore=loginfo) - print + print() source = os.path.expanduser(options.base) root = os.path.join(tmpDir, info["base"]) @@ -154,7 +154,7 @@ if __name__ == "__main__": os.chdir(source) if info["library"]: - print "Injecting files into", info["library"] + print("Injecting files into", info["library"]) libraryFile = os.path.join(root, info["library"]) with zipfile.ZipFile(libraryFile, 'a') as library: @@ -163,7 +163,7 @@ if __name__ == "__main__": library.write('pyfa.py', 'pyfa__main__.py') library.write('config.py') else: # platforms where we don't have a packaged library - print "Copying modules into", root + print("Copying modules into", root) for dir in setup['packages']: copyanything(dir, os.path.join(root, dir)) @@ -175,27 +175,27 @@ if __name__ == "__main__": if skel in ('src', 'mac-deprecated'): setup['include_files'] += ['pyfa.py', 'config.py'] - print - print "Copying included files:", + print() + print("Copying included files:", end=' ') for file in setup['include_files']: - if isinstance(file, basestring): - print file, + if isinstance(file, str): + print(file, end=' ') copyanything(file, os.path.join(root, file)) - print - print "Creating images zipfile:", + print() + print("Creating images zipfile:", end=' ') os.chdir('imgs') imagesFile = os.path.join(root, "imgs.zip") with zipfile.ZipFile(imagesFile, 'w') as images: for dir in setup['icon_dirs']: - print dir, + print(dir, end=' ') zipdir(dir, images) os.chdir(oldcwd) - print - print "Creating archive" + print() + print("Creating archive") if options.zip: archive = zipfile.ZipFile(tmpFile, 'w', compression=zipfile.ZIP_DEFLATED) zipdir(dirName, archive) @@ -205,11 +205,11 @@ if __name__ == "__main__": archive.add(tmpDir, arcname=info["arcname"]) archive.close() - print "Moving archive to ", destination + print("Moving archive to ", destination) shutil.move(tmpFile, destination) if "win" in skel and options.winexe: - print "Compiling EXE" + print("Compiling EXE") if config['tag'].lower() == "git": if git: # if git repo info available, use git commit @@ -230,13 +230,13 @@ if __name__ == "__main__": "/dMyOutputDir=%s"%destination, "/dMyOutputFile=%s"%fileName]) #stdout=devnull, stderr=devnull - print "EXE completed" + print("EXE completed") except Exception as e: - print "Encountered an error: \n\t", e + print("Encountered an error: \n\t", e) raise finally: - print "Deleting tmp files\n" + print("Deleting tmp files\n") try: shutil.rmtree("dist") # Inno dir except: @@ -252,6 +252,6 @@ if __name__ == "__main__": sys.stdout = oldstd if os.path.isdir(destination): - print os.path.join(destination, os.path.split(tmpFile)[1]) + print(os.path.join(destination, os.path.split(tmpFile)[1])) else: - print destination + print(destination) diff --git a/scripts/effectUsedBy.py b/scripts/effectUsedBy.py index c7fd67eb2..3e045105b 100755 --- a/scripts/effectUsedBy.py +++ b/scripts/effectUsedBy.py @@ -285,7 +285,7 @@ for marketgroupid in INITIALMARKETGROUPIDS: (globalmap_marketgroupid_typeid[marketgroupid]) else: break # Now, make a reverse map -for marketgroupid, typeidset in globalmap_marketgroupid_typeid.items(): +for marketgroupid, typeidset in list(globalmap_marketgroupid_typeid.items()): for typeid in typeidset: if not typeid in globalmap_typeid_marketgroupid: globalmap_typeid_marketgroupid[typeid] = set() @@ -310,7 +310,7 @@ for marketgroupid in globalmap_marketgroupid_typeidwithvariations: (typestoadd) # Make reverse map using simple way too for marketgroupid, typeidwithvariationsset in \ -globalmap_marketgroupid_typeidwithvariations.items(): +list(globalmap_marketgroupid_typeidwithvariations.items()): for typeid in typeidwithvariationsset: if not typeid in globalmap_typeidwithvariations_marketgroupid: globalmap_typeidwithvariations_marketgroupid[typeid] = set() @@ -422,12 +422,12 @@ for effect_name in effect_list: effectids = globalmap_effectnameeos_effectid[effect_name] else: if options.remove2: - print("Warning: effect file " + effect_name + - " exists but is not in database, removing") + print(("Warning: effect file " + effect_name + + " exists but is not in database, removing")) os.remove(os.path.join(effects_path, effect_file)) else: - print("Warning: effect file " + effect_name + - " exists but is not in database") + print(("Warning: effect file " + effect_name + + " exists but is not in database")) continue for effectid in effectids: cursor.execute(QUERY_EFFECTID_TYPEID, (effectid,)) @@ -497,8 +497,8 @@ for effect_name in effect_list: stopdebugprints = False if DEBUG_LEVEL >= 1: - print("\nEffect:", effect_name) - print("Total items affected: {0}".format(pereffect_totalaffected)) + print(("\nEffect:", effect_name)) + print(("Total items affected: {0}".format(pereffect_totalaffected))) # Stage 2.2 # This set holds all ids of already described items @@ -553,18 +553,18 @@ for effect_name in effect_list: if DEBUG_LEVEL == 1: printstr = "Group: {0}: {1}/{2} ({3:.3}%, inner \ score: {4:.3})" - print(printstr.format(groupName, + print((printstr.format(groupName, affected_undescribed, total, coverage, - groupscore[groupid])) + groupscore[groupid]))) # If it's 2, print results for each # iteration, so we need to include number # of already described items if DEBUG_LEVEL == 2: printstr = "Group: {0}: {1}+{2}/{3} ({4:.3}%, \ inner score: {5:.3})" - print(printstr.format(groupName, + print((printstr.format(groupName, affected_undescribed, affected_decribed, - total, coverage, groupscore[groupid])) + total, coverage, groupscore[groupid]))) # Calculate outer score for this grouping type groupouterscore = calc_outerscore(groupscore, pereffect_totalaffected, @@ -572,7 +572,7 @@ inner score: {5:.3})" # Debug print for outer data if DEBUG_LEVEL >= 1 and not stopdebugprints: printstr = "Groups outer score: {0:.3}" - print(printstr.format(groupouterscore)) + print((printstr.format(groupouterscore))) categoryscore = {} for categoryid in effectmap_categoryid_typeid: @@ -597,21 +597,21 @@ inner score: {5:.3})" if DEBUG_LEVEL == 1: printstr = "Category: {0}: {1}/{2} ({3:.3}%, \ inner score: {4:.3})" - print(printstr.format(categoryname, + print((printstr.format(categoryname, affected_undescribed, total, coverage, - categoryscore[categoryid])) + categoryscore[categoryid]))) if DEBUG_LEVEL == 2: printstr = "Category: {0}: {1}+{2}/{3} ({4:.3}%, \ inner score: {5:.3})" - print(printstr.format(categoryname, + print((printstr.format(categoryname, affected_undescribed, affected_decribed, - total, coverage, categoryscore[categoryid])) + total, coverage, categoryscore[categoryid]))) categoryouterscore = calc_outerscore(categoryscore, pereffect_totalaffected, CATEGORY_WEIGHT) if DEBUG_LEVEL >= 1 and not stopdebugprints: printstr = "Category outer score: {0:.3}" - print(printstr.format(categoryouterscore)) + print((printstr.format(categoryouterscore))) basetypescore = {} for basetypeid in effectmap_basetypeid_typeid: @@ -635,22 +635,22 @@ inner score: {5:.3})" if DEBUG_LEVEL == 1: printstr = "Base item: {0}: {1}/{2} ({3:.3}%, \ inner score: {4:.3})" - print(printstr.format(basetypename, + print((printstr.format(basetypename, affected_undescribed, total, coverage, - basetypescore[basetypeid])) + basetypescore[basetypeid]))) if DEBUG_LEVEL == 2: printstr = "Base item: {0}: {1}+{2}/{3} ({4:.3}%, \ inner score: {5:.3})" - print(printstr.format(basetypename, + print((printstr.format(basetypename, affected_undescribed, affected_decribed, - total, coverage, basetypescore[basetypeid])) + total, coverage, basetypescore[basetypeid]))) basetypeouterscore = calc_outerscore(basetypescore, pereffect_totalaffected, BASETYPE_WEIGHT) #Print outer data if DEBUG_LEVEL >= 1 and not stopdebugprints: printstr = "Base item outer score: {0:.3}" - print(printstr.format(basetypeouterscore)) + print((printstr.format(basetypeouterscore))) marketgroupwithvarsscore = {} for marketgroupid in effectmap_marketgroupid_typeidwithvars: @@ -698,22 +698,22 @@ inner score: {5:.3})" if DEBUG_LEVEL == 1: printstr = "Market group with variations: {0}: \ {1}/{2} ({3:.3}%, inner score: {4:.3})" - print(printstr.format(marketgroupname, + print((printstr.format(marketgroupname, affected_undescribed, total, coverage, - marketgroupwithvarsscore[marketgroupid])) + marketgroupwithvarsscore[marketgroupid]))) if DEBUG_LEVEL == 2: printstr = "Market group with variations: {0}: \ {1}+{2}/{3} ({4:.3}%, inner score: {5:.3})" - print(printstr.format(marketgroupname, + print((printstr.format(marketgroupname, affected_undescribed, affected_decribed, total, coverage, - marketgroupwithvarsscore[marketgroupid])) + marketgroupwithvarsscore[marketgroupid]))) marketgroupwithvarsouterscore = calc_outerscore\ (marketgroupwithvarsscore, pereffect_totalaffected, MARKETGROUPWITHVARS_WEIGHT) if DEBUG_LEVEL >= 1 and not stopdebugprints: printstr = "Market group outer score: {0:.3}" - print(printstr.format(marketgroupwithvarsouterscore)) + print((printstr.format(marketgroupwithvarsouterscore))) typenamecombscore = {} for typenamecombtuple in effectmap_typenamecombtuple_typeid: @@ -755,22 +755,22 @@ inner score: {5:.3})" if DEBUG_LEVEL == 1: printstr = "Type name combination: \"{0}\": \ {1}/{2} ({3:.3}%, inner score: {4:.3})" - print(printstr.format(typenamecombprintable, + print((printstr.format(typenamecombprintable, affected_undescribed, total, coverage, - typenamecombscore[typenamecombtuple])) + typenamecombscore[typenamecombtuple]))) if DEBUG_LEVEL == 2: printstr = "Type name combination: \"{0}\": \ {1}+{2}/{3} ({4:.3}%, inner score: {5:.3})" - print(printstr.format(typenamecombprintable, + print((printstr.format(typenamecombprintable, affected_undescribed, affected_decribed, total, coverage, - typenamecombscore[typenamecombtuple])) + typenamecombscore[typenamecombtuple]))) typenamecombouterscore = calc_outerscore(typenamecombscore, pereffect_totalaffected, TYPENAMECOMBS_WEIGHT) if DEBUG_LEVEL >= 1 and not stopdebugprints: printstr = "Type name combination outer score: {0:.3}" - print(printstr.format(typenamecombouterscore)) + print((printstr.format(typenamecombouterscore))) # Don't print anything after 1st iteration at 1st debugging # level @@ -804,7 +804,7 @@ inner score: {5:.3})" effectmap_categoryid_typeid[categorywinner][1] = True if DEBUG_LEVEL >= 2: printstr = "Category winner: {0}" - print(printstr.format(categorywinner)) + print((printstr.format(categorywinner))) elif maxouterscore == groupouterscore: groupwinner = max(groupscore, key=groupscore.get) describedbygroup.append(groupwinner) @@ -813,7 +813,7 @@ inner score: {5:.3})" effectmap_groupid_typeid[groupwinner][1] = True if DEBUG_LEVEL >= 2: printstr = "Group winner: {0}" - print(printstr.format(groupwinner)) + print((printstr.format(groupwinner))) elif maxouterscore == typenamecombouterscore: typenamecombwinner = max(typenamecombscore, key=typenamecombscore.get) @@ -824,7 +824,7 @@ inner score: {5:.3})" [1] = True if DEBUG_LEVEL >= 2: printstr = "Named like winner: {0}" - print(printstr.format(typenamecombwinner)) + print((printstr.format(typenamecombwinner))) elif maxouterscore == marketgroupwithvarsouterscore: marketgroupwithvarswinner = max(marketgroupwithvarsscore, key=marketgroupwithvarsscore.get) @@ -837,7 +837,7 @@ inner score: {5:.3})" [marketgroupwithvarswinner][1] = True if DEBUG_LEVEL >= 2: printstr = "Market group with variations winner: {0}" - print(printstr.format(marketgroupwithvarswinner)) + print((printstr.format(marketgroupwithvarswinner))) elif maxouterscore == basetypeouterscore: basetypewinner = max(basetypescore, key=basetypescore.get) describedbybasetype.append(basetypewinner) @@ -846,7 +846,7 @@ inner score: {5:.3})" effectmap_basetypeid_typeid[basetypewinner][1] = True if DEBUG_LEVEL >= 2: printstr = "Base item winner: {0}" - print(printstr.format(basetypewinner)) + print((printstr.format(basetypewinner))) # Stop if we have score less than some critical value, # all undescribed items will be provided as plain list else: @@ -864,13 +864,13 @@ inner score: {5:.3})" (effect_describedtypes) if DEBUG_LEVEL >= 1: print("Effect will be described by:") - print("Single item IDs:", singleitems) - print("Group IDs:", describedbygroup) - print("Category IDs:", describedbycategory) - print("Base item IDs:", describedbybasetype) - print("Market group with variations IDs:", - describedbymarketgroupwithvars) - print("Type name combinations:", describedbytypenamecomb) + print(("Single item IDs:", singleitems)) + print(("Group IDs:", describedbygroup)) + print(("Category IDs:", describedbycategory)) + print(("Base item IDs:", describedbybasetype)) + print(("Market group with variations IDs:", + describedbymarketgroupwithvars)) + print(("Type name combinations:", describedbytypenamecomb)) # Stage 2.1 # Read effect file and split it into lines @@ -1068,13 +1068,13 @@ inner score: {5:.3})" else: commentlines = ["# Not used by any item"] if options.remove: - print("Warning: effect file " + effect_name + - " is not used by any item, removing") + print(("Warning: effect file " + effect_name + + " is not used by any item, removing")) os.remove(os.path.join(effects_path, effect_file)) continue else: - print("Warning: effect file " + effect_name + - " is not used by any item") + print(("Warning: effect file " + effect_name + + " is not used by any item")) # Combine "used by" comment lines and actual effect lines outputlines = commentlines + effectLines # Combine all lines into single string @@ -1088,4 +1088,4 @@ inner score: {5:.3})" effectfile.close() elif DEBUG_LEVEL >= 2: print("Comment to write to file:") - print("\n".join(commentlines)) + print(("\n".join(commentlines))) diff --git a/scripts/findNonMarket.py b/scripts/findNonMarket.py index 9568b5ba2..d6a7520be 100755 --- a/scripts/findNonMarket.py +++ b/scripts/findNonMarket.py @@ -5,7 +5,7 @@ import os.path import re import sqlite3 -script_dir = os.path.dirname(unicode(__file__, sys.getfilesystemencoding())) +script_dir = os.path.dirname(str(__file__, sys.getfilesystemencoding())) # Connect to database and set up cursor db = sqlite3.connect(os.path.join(script_dir, "..", "eve.db")) @@ -156,7 +156,7 @@ for marketgroupid in INITIALMARKETGROUPIDS: (globalmap_marketgroupid_typeid[marketgroupid]) else: break # Now, make a reverse map -for marketgroupid, typeidset in globalmap_marketgroupid_typeid.items(): +for marketgroupid, typeidset in list(globalmap_marketgroupid_typeid.items()): for typeid in typeidset: if not typeid in globalmap_typeid_marketgroupid: globalmap_typeid_marketgroupid[typeid] = set() @@ -181,7 +181,7 @@ for marketgroupid in globalmap_marketgroupid_typeidwithvariations: (typestoadd) # Make reverse map using simple way too for marketgroupid, typeidwithvariationsset in \ -globalmap_marketgroupid_typeidwithvariations.items(): +list(globalmap_marketgroupid_typeidwithvariations.items()): for typeid in typeidwithvariationsset: if not typeid in globalmap_typeidwithvariations_marketgroupid: globalmap_typeidwithvariations_marketgroupid[typeid] = set() @@ -310,7 +310,7 @@ def suggestMktGrp(typeid, mode="grp"): similarity_factor *= 0.01 mktgrps_w_cos[marketgroupid] += similarity_factor if mktgrps_w_cos: - winner = max(mktgrps_w_cos.keys(), key=lambda k: mktgrps_w_cos[k]) + winner = max(list(mktgrps_w_cos.keys()), key=lambda k: mktgrps_w_cos[k]) else: winner = None return winner @@ -429,4 +429,4 @@ for typeid in nonmarket: #print("---\nItem: {0}\nGroup: {1}\nSuggested market group: {2} ({3})\nMeta group: {4}".format(typename, grpname, marketgroupname, mkt, metagroupname)) #print("\n\nmap = {{ {0} }}".format(", ".join("{0}: ({1}, {2})".format(key, map_typeid_stuff[key][0], map_typeid_stuff[key][1]) for key in sorted(map_typeid_stuff)))) -print("---\n{0}".format("\n".join("\"{0}\": {1}, # {2}".format(key, map_typeid_stuff2[key][0], map_typeid_stuff2[key][1]) for key in sorted(map_typeid_stuff2)))) +print(("---\n{0}".format("\n".join("\"{0}\": {1}, # {2}".format(key, map_typeid_stuff2[key][0], map_typeid_stuff2[key][1]) for key in sorted(map_typeid_stuff2))))) diff --git a/scripts/icons_update.py b/scripts/icons_update.py index 261ee7e89..e6a4050a3 100644 --- a/scripts/icons_update.py +++ b/scripts/icons_update.py @@ -153,7 +153,7 @@ for fname in os.listdir(icons_dir): fname = strip_path(fname) # Get rid of "icon" prefix as well #fname = re.sub('^icon', '', fname) - print fname,"exists" + print(fname,"exists") existing.add(fname) # Get a list of all the icons currently available in export @@ -232,12 +232,12 @@ if toremove: print('Some icons are not used and will be removed:') for fname in sorted(toremove): fullname = '{}.png'.format(fname) - print(' {}'.format(fullname)) + print((' {}'.format(fullname))) fullpath = os.path.join(icons_dir, fullname) os.remove(fullpath) if toupdate: - print('Updating {} icons...'.format(len(toupdate))) + print(('Updating {} icons...'.format(len(toupdate)))) missing = set() for fname in sorted(toupdate): icon = get_icon_file(fname) @@ -248,12 +248,12 @@ if toupdate: fullpath = os.path.join(icons_dir, fullname) icon.save(fullpath, 'png') if missing: - print(' {} icons are missing in export:'.format(len(missing))) + print((' {} icons are missing in export:'.format(len(missing)))) for fname in sorted(missing): - print(' {}'.format(fname)) + print((' {}'.format(fname))) if toadd: - print('Adding {} icons...'.format(len(toadd))) + print(('Adding {} icons...'.format(len(toadd)))) missing = set() for fname in sorted(toadd): icon = get_icon_file(fname) @@ -264,6 +264,6 @@ if toadd: fullpath = os.path.join(icons_dir, fullname) icon.save(fullpath, 'png') if missing: - print(' {} icons are missing in export:'.format(len(missing))) + print((' {} icons are missing in export:'.format(len(missing)))) for fname in sorted(missing): - print(' {}'.format(fname)) + print((' {}'.format(fname))) diff --git a/scripts/itemDiff.py b/scripts/itemDiff.py index c25312bf8..ea3d57d76 100755 --- a/scripts/itemDiff.py +++ b/scripts/itemDiff.py @@ -88,7 +88,7 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True): name = re.sub(stripspec, "", name) dictionary[id] = name - for id in set(old_namedata.keys()).intersection(new_namedata.keys()): + for id in set(old_namedata.keys()).intersection(list(new_namedata.keys())): oldname = old_namedata[id] newname = new_namedata[id] if oldname != newname: @@ -97,13 +97,13 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True): def printrenames(ren_dict, title, implementedtag=False): if len(ren_dict) > 0: - print('\nRenamed ' + title + ':') + print(('\nRenamed ' + title + ':')) for id in sorted(ren_dict): couple = ren_dict[id] if implementedtag: - print("\n[{0}] \"{1}\"\n[{2}] \"{3}\"".format(geteffst(couple[0]), couple[0], geteffst(couple[1]), couple[1])) + print(("\n[{0}] \"{1}\"\n[{2}] \"{3}\"".format(geteffst(couple[0]), couple[0], geteffst(couple[1]), couple[1]))) else: - print(" \"{0}\": \"{1}\",".format(couple[0].encode('utf-8'), couple[1].encode('utf-8'))) + print((" \"{0}\": \"{1}\",".format(couple[0].encode('utf-8'), couple[1].encode('utf-8')))) groupcats = {} def getgroupcat(grp): @@ -347,7 +347,7 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True): if attributes: oldattrs = old_itmdata[item][2] newattrs = new_itmdata[item][2] - for attr in set(oldattrs.keys()).union(newattrs.keys()): + for attr in set(oldattrs.keys()).union(list(newattrs.keys())): # NULL will mean there's no such attribute in db oldattr = oldattrs.get(attr, "NULL") newattr = newattrs.get(attr, "NULL") @@ -410,7 +410,7 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True): except: pass # Print jobs - print("Comparing databases:\n{0} -> {1}\n".format(old_meta.get("client_build"), new_meta.get("client_build"))) + print(("Comparing databases:\n{0} -> {1}\n".format(old_meta.get("client_build"), new_meta.get("client_build")))) if renames: title = 'effects' @@ -438,7 +438,7 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True): grpleg = "(x => y) - group changes\n" if groups else "" attreffleg = " [+] - effect or attribute has been added to item\n [-] - effect or attribute has been removed from item\n" if attributes or effects else "" effleg = " [y] - effect is implemented\n [n] - effect is not implemented\n" if effects else "" - print("{0}{1}{2}{3}\nItems:".format(genleg, grpleg, attreffleg, effleg)) + print(("{0}{1}{2}{3}\nItems:".format(genleg, grpleg, attreffleg, effleg))) # Make sure our states are sorted stateorder = sorted(global_itmdata) @@ -463,7 +463,7 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True): for item in itemorder: groupdata = items[item][0] groupstr = " ({0} => {1})".format(getgroupname(groupdata[1]), getgroupname(groupdata[2])) if groupdata[0] == S["changed"] else "" - print("\n[{0}] {1}{2}".format(TG[itmstate], getitemname(item).encode('utf-8'), groupstr)) + print(("\n[{0}] {1}{2}".format(TG[itmstate], getitemname(item).encode('utf-8'), groupstr))) effdata = items[item][1] for effstate in stateorder: @@ -476,7 +476,7 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True): for eff in efforder: # Take tag from item if item was added or removed tag = TG[effstate] if itmstate not in (S["removed"], S["added"]) else TG[itmstate] - print(" [{0}|{1}] {2}".format(tag, "y" if geteffst(geteffectname(eff)) else "n", geteffectname(eff))) + print((" [{0}|{1}] {2}".format(tag, "y" if geteffst(geteffectname(eff)) else "n", geteffectname(eff)))) attrdata = items[item][2] for attrstate in stateorder: @@ -493,7 +493,7 @@ def main(old, new, groups=True, effects=True, attributes=True, renames=True): valline = "{0}".format(attrs[attr][0] or 0) else: valline = "{0} => {1}".format(attrs[attr][0] or 0, attrs[attr][1] or 0) - print(" [{0}] {1}: {2}".format(TG[attrstate], getattrname(attr), valline)) + print((" [{0}] {1}: {2}".format(TG[attrstate], getattrname(attr), valline))) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Compare two databases generated from eve dump to find eos-related differences") diff --git a/scripts/jsonToSql.py b/scripts/jsonToSql.py index ad263df4b..d6d37213e 100755 --- a/scripts/jsonToSql.py +++ b/scripts/jsonToSql.py @@ -23,7 +23,7 @@ import sys import re # Add eos root path to sys.path so we can import ourselves -path = os.path.dirname(unicode(__file__, sys.getfilesystemencoding())) +path = os.path.dirname(str(__file__, sys.getfilesystemencoding())) sys.path.append(os.path.realpath(os.path.join(path, ".."))) import json @@ -105,7 +105,7 @@ def main(db, json_path): def convertIcons(data): new = [] - for k, v in data.items(): + for k, v in list(data.items()): v["iconID"] = k new.append(v) return new @@ -127,13 +127,13 @@ def main(db, json_path): def convertSection(sectionData): sectionLines = [] - headerText = u"{}".format(sectionData["header"]) + headerText = "{}".format(sectionData["header"]) sectionLines.append(headerText) for bonusData in sectionData["bonuses"]: - prefix = u"{} ".format(bonusData["number"]) if "number" in bonusData else "" - bonusText = u"{}{}".format(prefix, bonusData["text"].replace(u"\u00B7", u"\u2022 ")) + prefix = "{} ".format(bonusData["number"]) if "number" in bonusData else "" + bonusText = "{}{}".format(prefix, bonusData["text"].replace("\u00B7", "\u2022 ")) sectionLines.append(bonusText) - sectionLine = u"
    \n".join(sectionLines) + sectionLine = "
    \n".join(sectionLines) return sectionLine newData = [] @@ -147,7 +147,7 @@ def main(db, json_path): typeLines.append(convertSection(traitData["role"])) if "misc" in traitData: typeLines.append(convertSection(traitData["misc"])) - traitLine = u"
    \n
    \n".join(typeLines) + traitLine = "
    \n
    \n".join(typeLines) newRow = {"typeID": typeId, "traitText": traitLine} newData.append(newRow) return newData @@ -159,7 +159,7 @@ def main(db, json_path): factionMap = {} with open(os.path.join(jsonPath, "fsdTypeOverrides.json")) as f: overridesData = json.load(f) - for typeID, typeData in overridesData.items(): + for typeID, typeData in list(overridesData.items()): factionID = typeData.get("factionID") if factionID is not None: factionMap[int(typeID)] = factionID @@ -170,7 +170,7 @@ def main(db, json_path): data = {} # Dump all data to memory so we can easely cross check ignored rows - for jsonName, cls in tables.iteritems(): + for jsonName, cls in tables.items(): with open(os.path.join(jsonPath, "{}.json".format(jsonName))) as f: tableData = json.load(f) if jsonName in rowsInValues: @@ -205,11 +205,11 @@ def main(db, json_path): return False # Loop through each json file and write it away, checking ignored rows - for jsonName, table in data.iteritems(): + for jsonName, table in data.items(): fieldMap = fieldMapping.get(jsonName, {}) tmp = [] - print "processing {}".format(jsonName) + print("processing {}".format(jsonName)) for row in table: # We don't care about some kind of rows, filter it out if so @@ -234,8 +234,8 @@ def main(db, json_path): eos.db.gamedata_session.add(cloneParent) tmp.append(row['alphaCloneID']) - for k, v in row.iteritems(): - if (isinstance(v, basestring)): + for k, v in row.items(): + if (isinstance(v, str)): v = v.strip() setattr(instance, fieldMap.get(k, k), v) diff --git a/scripts/prep_data.py b/scripts/prep_data.py index 7de1cfdc3..d8907aa58 100644 --- a/scripts/prep_data.py +++ b/scripts/prep_data.py @@ -25,23 +25,23 @@ parser.add_argument("-s", "--singularity", action="store_true", help="Singularit parser.add_argument("-j", "--nojson", dest="nojson", action="store_true", help="Skip Phobos JSON data dump.") args = parser.parse_args() -eve_path = os.path.expanduser(unicode(args.eve_path, sys.getfilesystemencoding())) -cache_path = os.path.expanduser(unicode(args.cache_path, sys.getfilesystemencoding())) if args.cache_path else None -res_path = os.path.expanduser(unicode(args.res_path, sys.getfilesystemencoding())) if args.res_path else None -dump_path = os.path.expanduser(unicode(args.dump_path, sys.getfilesystemencoding())) -script_path = os.path.dirname(unicode(__file__, sys.getfilesystemencoding())) +eve_path = os.path.expanduser(str(args.eve_path, sys.getfilesystemencoding())) +cache_path = os.path.expanduser(str(args.cache_path, sys.getfilesystemencoding())) if args.cache_path else None +res_path = os.path.expanduser(str(args.res_path, sys.getfilesystemencoding())) if args.res_path else None +dump_path = os.path.expanduser(str(args.dump_path, sys.getfilesystemencoding())) +script_path = os.path.dirname(str(__file__, sys.getfilesystemencoding())) ### Append Phobos to path -sys.path.append(os.path.expanduser(unicode(args.phb_path, sys.getfilesystemencoding()))) +sys.path.append(os.path.expanduser(str(args.phb_path, sys.getfilesystemencoding()))) def header(text, subtext=None): - print - print "* "*30 - print text.center(60) + print() + print("* "*30) + print(text.center(60)) if subtext: - print subtext.center(60) - print "* "*30 - print + print(subtext.center(60)) + print("* "*30) + print() ### Data dump if not args.nojson: @@ -54,10 +54,10 @@ if not args.nojson: from writer import * rvr = reverence.blue.EVE(eve_path, cachepath=args.cache_path, sharedcachepath=res_path, server="singularity" if args.singularity else "tranquility") - print "EVE Directory: {}".format(rvr.paths.root) - print "Cache Directory: {}".format(rvr.paths.cache) - print "Shared Resource Directory: {}".format(rvr.paths.sharedcache) - print + print("EVE Directory: {}".format(rvr.paths.root)) + print("Cache Directory: {}".format(rvr.paths.cache)) + print("Shared Resource Directory: {}".format(rvr.paths.sharedcache)) + print() pickle_miner = ResourcePickleMiner(rvr) trans = Translator(pickle_miner) @@ -106,4 +106,4 @@ sys.stdout = open(diff_file, 'w') itemDiff.main(old=old_db, new=db_file) sys.stdout = old_stdout -print "\nAll done." +print("\nAll done.") diff --git a/scripts/renders_update.py b/scripts/renders_update.py index 8cb734720..f73b37b05 100644 --- a/scripts/renders_update.py +++ b/scripts/renders_update.py @@ -90,12 +90,12 @@ if toremove: print('Some renders are not used and will be removed:') for type_id in sorted(toremove): fullname = '{}.png'.format(type_id) - print(' {}'.format(fullname)) + print((' {}'.format(fullname))) fullpath = os.path.join(icons_dir, fullname) os.remove(fullpath) if toupdate: - print('Updating {} renders...'.format(len(toupdate))) + print(('Updating {} renders...'.format(len(toupdate)))) missing = toupdate.difference(export) toupdate.intersection_update(export) for type_id in sorted(toupdate): @@ -104,12 +104,12 @@ if toupdate: fullpath = os.path.join(icons_dir, fname) render.save(fullpath, 'png') if missing: - print(' {} renders are missing in export:'.format(len(missing))) + print((' {} renders are missing in export:'.format(len(missing)))) for type_id in sorted(missing): - print(' {}.png'.format(type_id)) + print((' {}.png'.format(type_id))) if toadd: - print('Adding {} renders...'.format(len(toadd))) + print(('Adding {} renders...'.format(len(toadd)))) missing = toadd.difference(export) toadd.intersection_update(export) for type_id in sorted(toadd): @@ -118,6 +118,6 @@ if toadd: fullpath = os.path.join(icons_dir, fname) render.save(fullpath, 'png') if missing: - print(' {} renders are missing in export:'.format(len(missing))) + print((' {} renders are missing in export:'.format(len(missing)))) for type_id in sorted(missing): - print(' {}.png'.format(type_id)) + print((' {}.png'.format(type_id))) diff --git a/scripts/sdeReadIcons.py b/scripts/sdeReadIcons.py index c1b9910c4..885c255eb 100644 --- a/scripts/sdeReadIcons.py +++ b/scripts/sdeReadIcons.py @@ -13,10 +13,10 @@ stream = open(r"C:\Users\Ryan\Sync\Git\blitzmann\Pyfa\scripts\iconIDs.yaml", "r" docs = yaml.load_all(stream) for doc in docs: - for k,v in doc.items(): + for k,v in list(doc.items()): iconDict[str(k)] = {"iconFile": v['iconFile']} with open('icons.json', 'w') as outfile: json.dump(iconDict, outfile) -print "done" \ No newline at end of file +print("done") \ No newline at end of file diff --git a/service/attribute.py b/service/attribute.py index 5b010bcc5..e33cbef4b 100644 --- a/service/attribute.py +++ b/service/attribute.py @@ -32,7 +32,7 @@ class Attribute(object): @staticmethod def getAttributeInfo(identity): - if isinstance(identity, (int, basestring)): + if isinstance(identity, (int, str)): info = eos.db.getAttributeInfo(identity, eager=("icon", "unit")) elif isinstance(identity, (int, float)): id_ = int(identity) diff --git a/service/character.py b/service/character.py index 2075ffdaa..cf2233fa9 100644 --- a/service/character.py +++ b/service/character.py @@ -95,7 +95,7 @@ class CharacterImportThread(threading.Thread): ) char = sCharacter.new(name + " (EVEMon)") sCharacter.apiUpdateCharSheet(char.ID, skills, securitystatus) - except Exception, e: + except Exception as e: pyfalog.error("Exception on character import:") pyfalog.error(e) continue @@ -147,18 +147,18 @@ class Character(object): self.all5() def exportText(self): - data = u"Pyfa exported plan for \"" + self.skillReqsDict['charname'] + "\"\n" - data += u"=" * 79 + u"\n" - data += u"\n" - item = u"" + data = "Pyfa exported plan for \"" + self.skillReqsDict['charname'] + "\"\n" + data += "=" * 79 + "\n" + data += "\n" + item = "" try: for s in self.skillReqsDict['skills']: if item == "" or not item == s["item"]: item = s["item"] - data += u"-" * 79 + "\n" - data += u"Skills required for {}:\n".format(item) - data += u"{}{}: {}\n".format(" " * s["indent"], s["skill"], int(s["level"])) - data += u"-" * 79 + "\n" + data += "-" * 79 + "\n" + data += "Skills required for {}:\n".format(item) + data += "{}{}: {}\n".format(" " * s["indent"], s["skill"], int(s["level"])) + data += "-" * 79 + "\n" except Exception: pass @@ -365,7 +365,7 @@ class Character(object): api = EVEAPIConnection() auth = api.auth(keyID=userID, vCode=apiKey) apiResult = auth.account.Characters() - charList = map(lambda c: unicode(c.name), apiResult.characters) + charList = [str(c.name) for c in apiResult.characters] char.chars = json.dumps(charList) return charList @@ -392,7 +392,7 @@ class Character(object): if ifHigher and level < skill.level: return - if isinstance(level, basestring) or level > 5 or level < 0: + if isinstance(level, str) or level > 5 or level < 0: skill.setLevel(None, persist) else: skill.setLevel(level, persist) @@ -456,7 +456,7 @@ class Character(object): return reqs def _checkRequirements(self, fit, char, subThing, reqs): - for req, level in subThing.requiredSkills.iteritems(): + for req, level in subThing.requiredSkills.items(): name = req.name ID = req.ID info = reqs.get(name) diff --git a/service/damagePattern.py b/service/damagePattern.py index 5a169734c..b52dee7e5 100644 --- a/service/damagePattern.py +++ b/service/damagePattern.py @@ -94,7 +94,7 @@ class DamagePattern(object): def exportPatterns(self): patterns = self.getDamagePatternList() - for i in xrange(len(patterns) - 1, -1, -1): + for i in range(len(patterns) - 1, -1, -1): if patterns[i].name in ("Uniform", "Selected Ammo"): del patterns[i] diff --git a/service/eveapi.py b/service/eveapi.py index c4ac681a0..e64315bd6 100644 --- a/service/eveapi.py +++ b/service/eveapi.py @@ -159,7 +159,7 @@ # ----------------------------------------------------------------------------- -import urlparse +import urllib.parse import copy from xml.parsers import expat @@ -200,7 +200,7 @@ class Error(Exception): self.args = (message.rstrip("."),) def __unicode__(self): - return u'%s [code=%s]' % (self.args[0], self.code) + return '%s [code=%s]' % (self.args[0], self.code) class RequestError(Error): @@ -255,7 +255,7 @@ def EVEAPIConnection(url="api.eveonline.com", cacheHandler=None, proxy=None, pro if not url.startswith("http"): url = "https://" + url - p = urlparse.urlparse(url, "https") + p = urllib.parse.urlparse(url, "https") if p.path and p.path[-1] == "/": p.path = p.path[:-1] ctx = _RootContext(None, p.path, {}, {}) @@ -279,7 +279,7 @@ def _ParseXML(response, fromContext, storeFunc): if fromContext and isinstance(response, Element): obj = response - elif type(response) in (str, unicode): + elif type(response) in (str, str): obj = _Parser().Parse(response, False) elif hasattr(response, "read"): obj = _Parser().Parse(response, True) @@ -349,7 +349,7 @@ class _Context(object): def __call__(self, **kw): if kw: # specified keywords override contextual ones - for k, v in self.parameters.iteritems(): + for k, v in self.parameters.items(): if k not in kw: kw[k] = v else: @@ -383,7 +383,7 @@ class _RootContext(_Context): def __call__(self, path, **kw): # convert list type arguments to something the API likes - for k, v in kw.iteritems(): + for k, v in kw.items(): if isinstance(v, _listtypes): kw[k] = ','.join(map(str, list(v))) @@ -567,7 +567,7 @@ class _Parser(object): # 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)] + [_castfunc(attributes[i], attributes[i + 1]) for i in range(0, len(attributes), 2)] ) # @@ -658,7 +658,7 @@ class _Parser(object): e = Element() e._name = this._name setattr(self.container, this._name, e) - for i in xrange(0, len(attributes), 2): + for i in range(0, len(attributes), 2): setattr(e, attributes[i], attributes[i + 1]) else: # tag of the form: , treat as empty string. @@ -671,7 +671,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 range(0, len(attributes), 2)] row.extend([getattr(this, col) for col in attributes2]) sibling.append(row) elif isinstance(sibling, Element): @@ -680,13 +680,13 @@ 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)] + \ + row = [_castfunc(attributes[i], attributes[i + 1]) for i in range(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)] + \ + row = [getattr(sibling, attributes[i]) for i in range(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 range(0, len(attributes), 2)] + [col for col in attributes2] setattr(self.container, this._name, rs) else: # something else must have set this attribute already. @@ -694,7 +694,7 @@ class _Parser(object): pass # Now fix up the attributes and be done with it. - for i in xrange(0, len(attributes), 2): + for i in range(0, len(attributes), 2): this.__dict__[attributes[i]] = _castfunc(attributes[i], attributes[i + 1]) return @@ -719,7 +719,7 @@ class Element(object): return "" % self._name -_fmt = u"%s:%s".__mod__ +_fmt = "%s:%s".__mod__ class Row(object): @@ -734,7 +734,7 @@ class Row(object): self._cols = cols or [] self._row = row or [] - def __nonzero__(self): + def __bool__(self): return True def __ne__(self, other): @@ -770,7 +770,7 @@ class Row(object): return self._row[self._cols.index(this)] def __str__(self): - return "Row(" + ','.join(map(_fmt, zip(self._cols, self._row))) + ")" + return "Row(" + ','.join(map(_fmt, list(zip(self._cols, self._row)))) + ")" class Rowset(object): @@ -829,7 +829,7 @@ class Rowset(object): for line in self._rows: yield line[i] else: - i = map(self._cols.index, columns) + i = list(map(self._cols.index, columns)) if options.get("row", False): for line in self._rows: yield line, [line[x] for x in i] @@ -857,7 +857,7 @@ class Rowset(object): self._rows += other._rows raise TypeError("rowset instance expected") - def __nonzero__(self): + def __bool__(self): return not not self._rows def __len__(self): diff --git a/service/fit.py b/service/fit.py index 706fc41cc..307aa7141 100644 --- a/service/fit.py +++ b/service/fit.py @@ -141,7 +141,7 @@ class Fit(object): except ValueError: ship = es_Citadel(eos.db.getItem(shipID)) fit = FitType(ship) - fit.name = name if name is not None else u"New %s" % fit.ship.item.name + fit.name = name if name is not None else "New %s" % fit.ship.item.name fit.damagePattern = self.pattern fit.targetResists = self.targetResists fit.character = self.character @@ -177,11 +177,11 @@ class Fit(object): # it will be refreshed first during the projected loop and throw an # error during the command loop refreshFits = set() - for projection in fit.projectedOnto.values(): + for projection in list(fit.projectedOnto.values()): if projection.victim_fit != fit and projection.victim_fit in eos.db.saveddata_session: # GH issue #359 refreshFits.add(projection.victim_fit) - for booster in fit.boostedOnto.values(): + for booster in list(fit.boostedOnto.values()): if booster.boosted_fit != fit and booster.boosted_fit in eos.db.saveddata_session: # GH issue #359 refreshFits.add(booster.boosted_fit) @@ -779,7 +779,7 @@ class Fit(object): total = fit.getNumSlots(fighter.slot) standardAttackActive = False for ability in fighter.abilities: - if ability.effect.isImplemented and ability.effect.handlerName == u'fighterabilityattackm': + if ability.effect.isImplemented and ability.effect.handlerName == 'fighterabilityattackm': # Activate "standard attack" if available ability.active = True standardAttackActive = True @@ -787,8 +787,8 @@ class Fit(object): # 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': + ability.effect.handlerName != 'fighterabilitymicrowarpdrive' and \ + ability.effect.handlerName != 'fighterabilityevasivemaneuvers': ability.active = True if used >= total: @@ -1194,7 +1194,7 @@ class Fit(object): def recalc(self, fit): start_time = time() - pyfalog.info(u"=" * 10 + u"recalc: {0}" + u"=" * 10, fit.name) + pyfalog.info("=" * 10 + "recalc: {0}" + "=" * 10, fit.name) if fit.factorReload is not self.serviceFittingOptions["useGlobalForceReload"]: fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"] fit.clear() diff --git a/service/market.py b/service/market.py index 49f0d88d2..131201ac0 100644 --- a/service/market.py +++ b/service/market.py @@ -20,7 +20,7 @@ import re import threading from logbook import Logger -import Queue +import queue # noinspection PyPackageRequirements import wx @@ -53,7 +53,7 @@ class ShipBrowserWorkerThread(threading.Thread): self.name = "ShipBrowser" def run(self): - self.queue = Queue.Queue() + self.queue = queue.Queue() self.cache = {} # Wait for full market initialization (otherwise there's high risky # this thread will attempt to init Market which is already being inited) @@ -261,7 +261,7 @@ class Market(object): } # Parent type name: set(item names) self.ITEMS_FORCEDMETAGROUP_R = {} - for item, value in self.ITEMS_FORCEDMETAGROUP.items(): + for item, value in list(self.ITEMS_FORCEDMETAGROUP.items()): parent = value[1] if parent not in self.ITEMS_FORCEDMETAGROUP_R: self.ITEMS_FORCEDMETAGROUP_R[parent] = set() @@ -372,7 +372,7 @@ class Market(object): def __makeRevDict(orig): """Creates reverse dictionary""" rev = {} - for item, value in orig.items(): + for item, value in list(orig.items()): if value not in rev: rev[value] = set() rev[value].add(item) @@ -386,7 +386,7 @@ class Market(object): item = identity elif isinstance(identity, int): item = eos.db.getItem(identity, *args, **kwargs) - elif isinstance(identity, basestring): + elif isinstance(identity, str): # We normally lookup with string when we are using import/export # features. Check against overrides identity = conversions.all.get(identity, identity) @@ -407,7 +407,7 @@ class Market(object): """Get group by its ID or name""" if isinstance(identity, types_Group): return identity - elif isinstance(identity, (int, float, basestring)): + elif isinstance(identity, (int, float, str)): if isinstance(identity, float): identity = int(identity) # Check custom groups @@ -426,7 +426,7 @@ class Market(object): """Get category by its ID or name""" if isinstance(identity, types_Category): category = identity - elif isinstance(identity, (int, basestring)): + elif isinstance(identity, (int, str)): category = eos.db.getCategory(identity, *args, **kwargs) elif isinstance(identity, float): id_ = int(identity) @@ -440,7 +440,7 @@ class Market(object): """Get meta group by its ID or name""" if isinstance(identity, types_MetaGroup): metaGroup = identity - elif isinstance(identity, (int, basestring)): + elif isinstance(identity, (int, str)): metaGroup = eos.db.getMetaGroup(identity, *args, **kwargs) elif isinstance(identity, float): id_ = int(identity) @@ -606,7 +606,7 @@ class Market(object): def getGroupsByCategory(self, cat): """Get groups from given category""" - groups = set(filter(lambda grp: self.getPublicityByGroup(grp), cat.groups)) + groups = set([grp for grp in cat.groups if self.getPublicityByGroup(grp)]) return groups @@ -626,7 +626,7 @@ class Market(object): if hasattr(group, 'addItems'): groupItems.update(group.addItems) items = set( - filter(lambda item: self.getPublicityByItem(item) and self.getGroupByItem(item) == group, groupItems)) + [item for item in groupItems if self.getPublicityByItem(item) and self.getGroupByItem(item) == group]) return items def getItemsByMarketGroup(self, mg, vars_=True): @@ -656,7 +656,7 @@ class Market(object): else: result = baseitms # Get rid of unpublished items - result = set(filter(lambda item_: self.getPublicityByItem(item_), result)) + result = set([item_ for item_ in result if self.getPublicityByItem(item_)]) return result def marketGroupHasTypesCheck(self, mg): @@ -783,11 +783,11 @@ class Market(object): @staticmethod def directAttrRequest(items, attribs): try: - itemIDs = tuple(map(lambda i: i.ID, items)) + itemIDs = tuple([i.ID for i in items]) except TypeError: itemIDs = (items.ID,) try: - attrIDs = tuple(map(lambda i: i.ID, attribs)) + attrIDs = tuple([i.ID for i in attribs]) except TypeError: attrIDs = (attribs.ID,) info = {} @@ -803,7 +803,7 @@ class Market(object): def filterItemsByMeta(self, items, metas): """Filter items by meta lvl""" - filtered = set(filter(lambda item: self.getMetaGroupIdByItem(item) in metas, items)) + filtered = set([item for item in items if self.getMetaGroupIdByItem(item) in metas]) return filtered def getSystemWideEffects(self): diff --git a/service/network.py b/service/network.py index a990fee23..0099300f9 100644 --- a/service/network.py +++ b/service/network.py @@ -18,8 +18,8 @@ # ============================================================================= -import urllib2 -import urllib +import urllib.request, urllib.error, urllib.parse +import urllib.request, urllib.parse, urllib.error import socket from logbook import Logger @@ -33,24 +33,24 @@ timeout = 3 socket.setdefaulttimeout(timeout) -class Error(StandardError): +class Error(Exception): def __init__(self, msg=None): self.message = msg -class RequestError(StandardError): +class RequestError(Exception): pass -class AuthenticationError(StandardError): +class AuthenticationError(Exception): pass -class ServerError(StandardError): +class ServerError(Exception): pass -class TimeoutError(StandardError): +class TimeoutError(Exception): pass @@ -94,29 +94,29 @@ class Network(object): # proxy_auth is a tuple of (login, password) or None if proxy_auth is not None: # add login:password@ in front of proxy address - proxy_handler = urllib2.ProxyHandler({ + proxy_handler = urllib.request.ProxyHandler({ 'https': '{0}:{1}@{2}:{3}'.format( proxy_auth[0], proxy_auth[1], proxy[0], proxy[1]) }) else: # build proxy handler with no login/pass info - proxy_handler = urllib2.ProxyHandler({'https': "{0}:{1}".format(proxy[0], proxy[1])}) - opener = urllib2.build_opener(proxy_handler) - urllib2.install_opener(opener) + proxy_handler = urllib.request.ProxyHandler({'https': "{0}:{1}".format(proxy[0], proxy[1])}) + opener = urllib.request.build_opener(proxy_handler) + urllib.request.install_opener(opener) else: # This is a bug fix, explicitly disable possibly previously installed # opener with proxy, by urllib2.install_opener() a few lines above in code. # Now this explicitly disables proxy handler, "uninstalling" opener. # This is used in case when user had proxy enabled, so proxy_handler was already # installed globally, and then user had disabled the proxy, so we should clear that opener - urllib2.install_opener(None) + urllib.request.install_opener(None) # another option could be installing a default opener: # urllib2.install_opener(urllib2.build_opener()) - request = urllib2.Request(url, headers=headers, data=urllib.urlencode(data) if data else None) + request = urllib.request.Request(url, headers=headers, data=urllib.parse.urlencode(data) if data else None) try: - return urllib2.urlopen(request) - except urllib2.HTTPError as error: + return urllib.request.urlopen(request) + except urllib.error.HTTPError as error: pyfalog.warning("HTTPError:") pyfalog.warning(error) if error.code == 404: @@ -125,7 +125,7 @@ class Network(object): raise AuthenticationError() elif error.code >= 500: raise ServerError() - except urllib2.URLError as error: + except urllib.error.URLError as error: pyfalog.warning("Timed out or other URL error:") pyfalog.warning(error) if "timed out" in error.reason: diff --git a/service/port.py b/service/port.py index dad55aac9..857a0ff5c 100644 --- a/service/port.py +++ b/service/port.py @@ -169,9 +169,7 @@ class UserCancelException(Exception): pass -class IPortUser: - - __metaclass__ = ABCMeta +class IPortUser(metaclass=ABCMeta): ID_PULSE = 1 # Pulse the progress bar @@ -316,7 +314,7 @@ class Port(object): for page in attempt_codecs: try: pyfalog.info("Attempting to decode file {0} using {1} page.", path, page) - srcString = unicode(srcString, page) + srcString = str(srcString, page) codec_found = page pyfalog.info("File {0} decoded using {1} page.", path, page) except UnicodeDecodeError: @@ -325,7 +323,7 @@ class Port(object): break else: pyfalog.info("Unicode BOM detected in {0}, using {1} page.", path, codec_found) - srcString = unicode(srcString[len(savebom):], codec_found) + srcString = str(srcString[len(savebom):], codec_found) else: # nasty hack to detect other transparent utf-16 loading @@ -453,7 +451,7 @@ class Port(object): item['type']['name'] = '' fit['items'].append(item) - for chargeID, amount in charges.items(): + for chargeID, amount in list(charges.items()): item = nested_dict() item['flag'] = INV_FLAG_CARGOBAY item['quantity'] = amount @@ -588,7 +586,7 @@ class Port(object): def importDna(string): sMkt = Market.getInstance() - ids = map(int, re.findall(r'\d+', string)) + ids = list(map(int, re.findall(r'\d+', string))) for id_ in ids: try: try: @@ -642,7 +640,7 @@ class Port(object): c.amount = int(amount) f.cargo.append(c) else: - for i in xrange(int(amount)): + for i in range(int(amount)): try: m = Module(item) except: @@ -834,7 +832,7 @@ class Port(object): # If client didn't take care of encoding file contents into Unicode, # do it using fallback encoding ourselves if isinstance(contents, str): - contents = unicode(contents, locale.getpreferredencoding()) + contents = str(contents, locale.getpreferredencoding()) fits = [] # List for fits fitIndices = [] # List for starting line numbers for each fit @@ -1114,7 +1112,7 @@ class Port(object): also, it's OK to arrange modules randomly? """ offineSuffix = " /OFFLINE" - export = u"[%s, %s]\n" % (fit.ship.item.name, fit.name) + export = "[%s, %s]\n" % (fit.ship.item.name, fit.name) stuff = {} sFit = svcFit.getInstance() for module in fit.modules: @@ -1324,7 +1322,7 @@ class Port(object): charges[cargo.item.name] = 0 charges[cargo.item.name] += cargo.amount - for name, qty in charges.items(): + for name, qty in list(charges.items()): hardware = doc.createElement("hardware") hardware.setAttribute("qty", "%d" % qty) hardware.setAttribute("slot", "cargo") diff --git a/service/price.py b/service/price.py index 9c6da235a..4476f99fd 100644 --- a/service/price.py +++ b/service/price.py @@ -20,7 +20,7 @@ import time import threading -import Queue +import queue from xml.dom import minidom from logbook import Logger @@ -134,7 +134,7 @@ class Price(object): except TimeoutError: # Timeout error deserves special treatment pyfalog.warning("Price fetch timout") - for typeID in priceMap.keys(): + for typeID in list(priceMap.keys()): priceobj = priceMap[typeID] priceobj.time = time.time() + TIMEOUT priceobj.failed = True @@ -146,7 +146,7 @@ class Price(object): pass # if we get to this point, then we've got an error. Set to REREQUEST delay - for typeID in priceMap.keys(): + for typeID in list(priceMap.keys()): priceobj = priceMap[typeID] priceobj.time = time.time() + REREQUEST priceobj.failed = True @@ -215,7 +215,7 @@ class PriceWorkerThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.name = "PriceWorker" - self.queue = Queue.Queue() + self.queue = queue.Queue() self.wait = {} pyfalog.debug("Initialize PriceWorkerThread.") diff --git a/service/pycrest/compat.py b/service/pycrest/compat.py index 7484b41dd..8f4a35e0b 100644 --- a/service/pycrest/compat.py +++ b/service/pycrest/compat.py @@ -7,8 +7,8 @@ if PY3: # pragma: no cover text_type = str binary_type = bytes else: # pragma: no cover - string_types = basestring, - text_type = unicode + string_types = str, + text_type = str binary_type = str diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index 6a4799d74..9ea88ce23 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -12,13 +12,13 @@ import config from service.pycrest.compat import bytes_, text_ from service.pycrest.errors import APIException -from urlparse import urlparse, urlunparse, parse_qsl +from urllib.parse import urlparse, urlunparse, parse_qsl try: import pickle except ImportError: # pragma: no cover # noinspection PyPep8Naming - import cPickle as pickle + import pickle as pickle pyfalog = Logger(__name__) cache_re = re.compile(r'max-age=([0-9]+)') @@ -136,7 +136,7 @@ class APIConnection(object): prms[key] = params[key] # check cache - key = (resource, frozenset(self._session.headers.items()), frozenset(prms.items())) + key = (resource, frozenset(list(self._session.headers.items())), frozenset(list(prms.items()))) cached = self.cache.get(key) if cached and cached['cached_until'] > time.time(): pyfalog.debug('Cache hit for resource {0} (params={1})', resource, prms) @@ -280,7 +280,7 @@ class APIObject(object): def __init__(self, parent, connection): self._dict = {} self.connection = connection - for k, v in parent.items(): + for k, v in list(parent.items()): if type(v) is dict: self._dict[k] = APIObject(v, connection) elif type(v) is list: diff --git a/service/server.py b/service/server.py index 4d6566de9..3f28c5cf2 100644 --- a/service/server.py +++ b/service/server.py @@ -1,5 +1,5 @@ -import BaseHTTPServer -import urlparse +import http.server +import urllib.parse import socket import threading from logbook import Logger @@ -68,13 +68,13 @@ if (window.location.href.indexOf('step=2') == -1) {{ # https://github.com/fuzzysteve/CREST-Market-Downloader/ -class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class AuthHandler(http.server.BaseHTTPRequestHandler): def do_GET(self): if self.path == "/favicon.ico": return - parsed_path = urlparse.urlparse(self.path) - parts = urlparse.parse_qs(parsed_path.query) + parsed_path = urllib.parse.urlparse(self.path) + parts = urllib.parse.parse_qs(parsed_path.query) msg = "" step2 = 'step' in parts @@ -88,7 +88,7 @@ class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler): # For implicit mode, we have to serve up the page which will take the hash and redirect useing a querystring pyfalog.info("Processing response from EVE Online.") msg = "Processing response from EVE Online" - except Exception, ex: + except Exception as ex: pyfalog.error("Error in CREST AuthHandler") pyfalog.error(ex) msg = "

    Error

    \n

    {}

    ".format(ex.message) @@ -106,9 +106,9 @@ class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler): # http://code.activestate.com/recipes/425210-simple-stoppable-server-using-socket-timeout/ -class StoppableHTTPServer(BaseHTTPServer.HTTPServer): +class StoppableHTTPServer(http.server.HTTPServer): def server_bind(self): - BaseHTTPServer.HTTPServer.server_bind(self) + http.server.HTTPServer.server_bind(self) self.settings = CRESTSettings.getInstance() # Allow listening for x seconds @@ -156,5 +156,5 @@ class StoppableHTTPServer(BaseHTTPServer.HTTPServer): if __name__ == "__main__": httpd = StoppableHTTPServer(('', 6461), AuthHandler) t = threading.Thread(target=httpd.serve) - raw_input("Press to stop server\n") + input("Press to stop server\n") httpd.stop() diff --git a/service/settings.py b/service/settings.py index 0197564c1..f69761bc6 100644 --- a/service/settings.py +++ b/service/settings.py @@ -17,9 +17,9 @@ # along with pyfa. If not, see . # ============================================================================= -import cPickle +import pickle import os.path -import urllib2 +import urllib.request, urllib.error, urllib.parse import config import eos.config @@ -91,7 +91,7 @@ class SettingsProvider(object): else: try: with open(canonical_path, "rb") as f: - info = cPickle.load(f) + info = pickle.load(f) for item in defaults: if item not in info: info[item] = defaults[item] @@ -103,7 +103,7 @@ class SettingsProvider(object): return settings_obj def saveAll(self): - for settings in self.settings.itervalues(): + for settings in self.settings.values(): settings.save() @@ -124,7 +124,7 @@ class Settings(object): return # NOTE: with + open -> file handle auto close with open(self.location, "wb") as f: - cPickle.dump(self.info, f, cPickle.HIGHEST_PROTOCOL) + pickle.dump(self.info, f, pickle.HIGHEST_PROTOCOL) def __getitem__(self, k): try: @@ -140,22 +140,22 @@ class Settings(object): return self.info.__iter__() def iterkeys(self): - return self.info.iterkeys() + return iter(self.info.keys()) def itervalues(self): - return self.info.itervalues() + return iter(self.info.values()) def iteritems(self): - return self.info.iteritems() + return iter(self.info.items()) def keys(self): - return self.info.keys() + return list(self.info.keys()) def values(self): - return self.info.values() + return list(self.info.values()) def items(self): - return self.info.items() + return list(self.info.items()) class NetworkSettings(object): @@ -235,7 +235,7 @@ class NetworkSettings(object): def autodetect(): proxy = None - proxydict = urllib2.ProxyHandler().proxies + proxydict = urllib.request.ProxyHandler().proxies validPrefixes = ("http", "https") diff --git a/testFits.py b/testFits.py new file mode 100644 index 000000000..b2c31df5c --- /dev/null +++ b/testFits.py @@ -0,0 +1,79 @@ +fits = ["306", "317", "318", "320", "321", "322", "323", "330", "334", "337", "338", "342", "348", "349", "350", + "374", "377", "379", "382", + "387", "389", "390", "393", "394", "395", "397", "399", "420", "423", "424", "427", "431", "432", "436", + "437", "438", "439", "440", "444", + "446", "450", "453", "457", "458", "459", "461", "464", "471", "472", "473", "474", "478", "483", "484", + "485", "486", "487", "494", "495", + "496", "501", "504", "505", "508", "509", "513", "515", "517", "518", "519", "522", "523", "526", "527", + "528", "529", "530", "531", "532", + "534", "535", "536", "537", "539", "540", "541", "542", "543", "546", "547", "548", "550", "551", "554", + "555", "556", "558", "559", "560", + "563", "565", "566", "568", "569", "570", "571", "572", "574", "576", "578", "581", "582", "583", "585", + "586", "587", "589", "593", "594", + "598", "602", "603", "606", "610", "611", "612", "613", "614", "618", "620", "621", "624", "626", "627", + "628", "629", "630", "631", "632", + "633", "634", "635", "637", "638", "640", "644", "645", "648", "649", "654", "655", "656", "657", "665", + "666", "667", "668", "669", "671", + "674", "677", "678", "685", "686", "690", "691", "692", "693", "694", "695", "696", "698", "699", "702", + "704", "712", "713", "714", "716", + "718", "722", "723", "726", "727", "728", "730", "731", "733", "735", "736", "737", "738", "741", "745", + "746", "749", "750", "751", "756", + "757", "758", "759", "766", "769", "778", "779", "780", "782", "783", "786", "788", "789", "791", "794", + "796", "798", "801", "808", "817", + "818", "819", "822", "823", "827", "831", "837", "838", "839", "846", "847", "856", "857", "866", "867", + "868", "869", "871", "877", "880", + "881", "885", "886", "889", "892", "893", "894", "895", "897", "905", "906", "908", "910", "911", "912", + "915", "916", "917", "922", "924", + "926", "931", "932", "933", "934", "936", "939", "942", "947", "949", "951", "952", "955", "957", "958", + "959", "961", "962", "968", "969", + "971", "974", "975", "977", "978", "979", "980", "982", "984", "985", "987", "988", "989", "990", "991", + "992", "997", "999", "1004", "1005", + "1008", "1009", "1015", "1016", "1025", "1026", "1027", "1036", "1037", "1043", "1044", "1045", "1046", + "1047", "1048", "1049", "1051", + "1060", "1061", "1062", "1063", "1065", "1066", "1067", "1069", "1073", "1074", "1075", "1076", "1078", + "1083", "1084", "1085", "1086", + "1088", "1089", "1090", "1091", "1092", "1094", "1096", "1099", "1100", "1101", "1102", "1104", "1105", + "1112", "1114", "1115", "1117", + "1119", "1121", "1122", "1124", "1128", "1132", "1134", "1139", "1140", "1142", "1143", "1144", "1145", + "1146", "1150", "1151", "1152", + "1155", "1156", "1157", "1161", "1163", "1164", "1166", "1167", "1170", "1172", "1173", "1174", "1176", + "1180", "1181", "1184", "1185", + "1187", "1194", "1196", "1199", "1200", "1201", "1202", "1203", "1206", "1207", "1214", "1216", "1218", + "1220", "1225", "1228", "1230", + "1235", "1236", "1237", "1239", "1243", "1244", "1245", "1249", "1250", "1251", "1252", "1255", "1261", + "1262", "1264", "1265", "1266", + "1267", "1269", "1270", "1272", "1273", "1275", "1276", "1281", "1284", "1285", "1286", "1291", "1292", + "1293", "1297", "1299", "1300", + "1301", "1302", "1304", "1305", "1306", "1310", "1311", "1312", "1315", "1322", "1331", "1332", "1338", + "1341", "1345", "1346", "1358", + "1359", "1360", "1362", "1364", "1370", "1373", "1374", "1387", "1391", "1393", "1394", "1395", "1400", + "1403", "1404", "1406", "1408", + "1411", "1412", "1413", "1414", "1415", "1419", "1420", "1421", "1422", "1423", "1424", "1426", "1427", + "1428", "1429", "1430", "1440", + "1441", "1443", "1445", "1447", "1450", "1451", "1454", "1455", "1456", "1457", "1458", "1459", "1462", + "1463", "1465", "1466", "1468", + "1469", "1471", "1476", "1479", "1481", "1482", "1483", "1484", "1485", "1486", "1487", "1489", "1490", + "1491", "1493", "1495", "1499", + "1503", "1505", "1506", "1508", "1511", "1517", "1528", "1529", "1532", "1534", "1535", "1537", "1542", + "1543", "1544", "1546", "1547", + "1548", "1549", "1550", ] +import time + +for fitID in fits[:100]: + time.sleep(.200) + print(fitID) + wx.PostEvent(self, FitSelected(fitID=int(fitID), startup=2)) + +from service.fit import Fit +sFit = Fit.getInstance() + +for x in fits: + try: + print(x) + fit = sFit.getFit(int(x)) + fit.clear() + fit.calculateModifiedAttributes() + except Exception as e: + print(("failed on ",x)) + print(e) + raise e diff --git a/tests/test_locale/file_dialog.py b/tests/test_locale/file_dialog.py index 6ef38f12a..357037265 100644 --- a/tests/test_locale/file_dialog.py +++ b/tests/test_locale/file_dialog.py @@ -60,7 +60,7 @@ class MyForm(wx.Frame): print(path) print("Type:") - print(type(path)) + print((type(path))) print("OS Walk: No Codec:") print(os_walk_without_codec) diff --git a/tests/test_locale/test_os_walk.py b/tests/test_locale/test_os_walk.py index 4ed377acc..616feffcd 100644 --- a/tests/test_locale/test_os_walk.py +++ b/tests/test_locale/test_os_walk.py @@ -8,7 +8,7 @@ sys.path.append(os.path.realpath(os.path.join(script_dir, '..', '..'))) from _development.helpers_locale import GetPath def test_os_walk(): - current_directory = os.path.dirname(os.path.abspath(unicode(__file__))) + current_directory = os.path.dirname(os.path.abspath(str(__file__))) subfolders = os.listdir(current_directory) subfolders = [e for e in subfolders if not (e.endswith(".py") or e.endswith(".pyc") or e.endswith(".md"))] diff --git a/tests/test_modules/test_eos/test_modifiedAttributeDict.py b/tests/test_modules/test_eos/test_modifiedAttributeDict.py index 55b68fb14..649cfaee8 100644 --- a/tests/test_modules/test_eos/test_modifiedAttributeDict.py +++ b/tests/test_modules/test_eos/test_modifiedAttributeDict.py @@ -5,7 +5,7 @@ import os import sys script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.realpath(os.path.join(script_dir, '..', '..', '..')) -print script_dir +print(script_dir) sys.path.append(script_dir) # noinspection PyPackageRequirements diff --git a/tests/test_modules/test_service/test_attribute.py b/tests/test_modules/test_service/test_attribute.py index 0f9622885..b4d349377 100644 --- a/tests/test_modules/test_service/test_attribute.py +++ b/tests/test_modules/test_service/test_attribute.py @@ -20,19 +20,19 @@ def test_attribute(): assert info.attributeID == 54 assert type(info.attributeID) is int assert info.attributeName == 'maxRange' - assert type(info.attributeName) is unicode + assert type(info.attributeName) is str assert info.defaultValue == 0.0 assert type(info.defaultValue) is float assert info.description == 'Distance below which range does not affect the to-hit equation.' - assert type(info.description) is unicode + assert type(info.description) is str assert info.displayName == 'Optimal Range' - assert type(info.displayName) is unicode + assert type(info.displayName) is str assert info.highIsGood is True assert type(info.highIsGood) is bool assert info.iconID == 1391 assert type(info.iconID) is int assert info.name == 'maxRange' - assert type(info.name) is unicode + assert type(info.name) is str assert info.published is True assert type(info.published) is bool assert info.unitID == 1 @@ -40,10 +40,10 @@ def test_attribute(): assert info.unit.ID == 1 assert type(info.unit.ID) is int assert info.unit.displayName == 'm' - assert type(info.unit.displayName) is unicode + assert type(info.unit.displayName) is str assert info.unit.name == 'Length' - assert type(info.unit.name) is unicode + assert type(info.unit.name) is str assert info.unit.unitID == 1 assert type(info.unit.unitID) is int assert info.unit.unitName == 'Length' - assert type(info.unit.unitName) is unicode + assert type(info.unit.unitName) is str diff --git a/tests/test_unread_desc.py b/tests/test_unread_desc.py index 0f1a0d52b..5fc54fd14 100644 --- a/tests/test_unread_desc.py +++ b/tests/test_unread_desc.py @@ -62,11 +62,11 @@ class PortUser(IPortUser): def print_db_info(): # Output debug info import eos - print - print "------------ data base connection info ------------" - print(eos.db.saveddata_engine) - print(eos.db.gamedata_engine) - print + print() + print("------------ data base connection info ------------") + print((eos.db.saveddata_engine)) + print((eos.db.gamedata_engine)) + print() # noinspection PyUnusedLocal @@ -78,7 +78,7 @@ def test_import_xml(print_db_info): fits = None with open(os.path.join(script_dir, xml_file), "r") as file_: srcString = file_.read() - srcString = unicode(srcString, "utf-8") + srcString = str(srcString, "utf-8") # (basestring, IPortUser, basestring) -> list[eos.saveddata.fit.Fit] usr.on_port_process_start() #stpw.reset() diff --git a/utils/compat.py b/utils/compat.py index 8dcf8a4d9..7507c7c96 100644 --- a/utils/compat.py +++ b/utils/compat.py @@ -2,9 +2,9 @@ # Passes Python2.7's test suite and incorporates all the latest updates. try: - from thread import get_ident as _get_ident + from _thread import get_ident as _get_ident except ImportError: - from dummy_thread import get_ident as _get_ident + from _dummy_thread import get_ident as _get_ident try: from _abcoll import KeysView, ValuesView, ItemsView @@ -79,7 +79,7 @@ class OrderedDict(dict): def clear(self): """od.clear() -> None. Remove all items from od.""" try: - for node in self.__map.itervalues(): + for node in self.__map.values(): del node[:] root = self.__root root[:] = [root, root, None] @@ -162,12 +162,12 @@ class OrderedDict(dict): for key in other: self[key] = other[key] elif hasattr(other, 'keys'): - for key in other.keys(): + for key in list(other.keys()): self[key] = other[key] else: for key, value in other: self[key] = value - for key, value in kwds.items(): + for key, value in list(kwds.items()): self[key] = value __update = update # let subclasses override update without breaking __init__ @@ -205,7 +205,7 @@ class OrderedDict(dict): try: if not self: return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) + return '%s(%r)' % (self.__class__.__name__, list(self.items())) finally: del _repr_running[call_key] @@ -240,7 +240,7 @@ class OrderedDict(dict): """ if isinstance(other, OrderedDict): - return len(self) == len(other) and self.items() == other.items() + return len(self) == len(other) and list(self.items()) == list(other.items()) return dict.__eq__(self, other) def __ne__(self, other): diff --git a/utils/stopwatch.py b/utils/stopwatch.py index adf7e5832..cb5f0b9ea 100644 --- a/utils/stopwatch.py +++ b/utils/stopwatch.py @@ -82,9 +82,9 @@ m_re_sub(stpw, 1000, 100000, "asdfadsasdaasdfadsasda") def checkpoint(self, name=''): span = self.elapsed self.__update_stat(span) - text = u'Stopwatch("{tname}") - {checkpoint} - {last:.6f}ms ({elapsed:.12f}ms elapsed)'.format( + text = 'Stopwatch("{tname}") - {checkpoint} - {last:.6f}ms ({elapsed:.12f}ms elapsed)'.format( tname=self.name, - checkpoint=unicode(name, "utf-8"), + checkpoint=str(name, "utf-8"), last=self.last, elapsed=span ).strip() diff --git a/utils/strfunctions.py b/utils/strfunctions.py index 26bf5f59a..8532d9c18 100644 --- a/utils/strfunctions.py +++ b/utils/strfunctions.py @@ -12,7 +12,7 @@ def sequential_rep(text_, *args): :return: if text_ length was zero or invalid parameters then no manipulation to text_ """ arg_len = len(args) - if arg_len % 2 == 0 and isinstance(text_, basestring) and len(text_) > 0: + if arg_len % 2 == 0 and isinstance(text_, str) and len(text_) > 0: i = 0 while i < arg_len: text_ = re.sub(args[i], args[i + 1], text_) @@ -27,4 +27,4 @@ def replace_ltgt(text_): :param text_: string content of fit name from exported by EVE client. :return: if text_ is not instance of basestring then no manipulation to text_. """ - return text_.replace("<", "<").replace(">", ">") if isinstance(text_, basestring) else text_ + return text_.replace("<", "<").replace(">", ">") if isinstance(text_, str) else text_ diff --git a/utils/timer.py b/utils/timer.py index 8139f9101..9b7daf4dc 100644 --- a/utils/timer.py +++ b/utils/timer.py @@ -17,9 +17,9 @@ class Timer(object): return (time.time() - self.__last) * 1000 def checkpoint(self, name=''): - text = u'Timer - {timer} - {checkpoint} - {last:.2f}ms ({elapsed:.2f}ms elapsed)'.format( + text = 'Timer - {timer} - {checkpoint} - {last:.2f}ms ({elapsed:.2f}ms elapsed)'.format( timer=self.name, - checkpoint=unicode(name, "utf-8"), + checkpoint=str(name, "utf-8"), last=self.last, elapsed=self.elapsed ).strip() diff --git a/wxthing.py b/wxthing.py new file mode 100644 index 000000000..bdcbaf616 --- /dev/null +++ b/wxthing.py @@ -0,0 +1,159 @@ +# A very simple Drag and Drop Example +# provided with no warranty whatsoever for any purpose, ever + +# A very simple Drag and Drop Example +# provided with no warranty whatsoever for any purpose, ever + +# This code creates a Text Control from which Text can be dragged, +# a Text Control to which Text can be dragged (from the first Text Control or from other applications), +# and a Text Control to which Files can be dragged from outside this application. +# While the later two windows can receive data from outside the application, the first window +# does not appear to be able to provide text to other applications. Please feel free to fix +# this if you know how, as I think that would be more useful as an example. + +# It is designed to demonstrate the fundamentals of very simple drag-and-drop operations. + +""" This mini-app is designed to demonstrate simple Drag and Drop functioning in wx.Python """ + +__author__ = 'David Woods, Wisconsin Center for Education Research ' + +# Import wx.Python +import wx + +# Declare GUI Constants +MENU_FILE_EXIT = wx.NewId() +DRAG_SOURCE = wx.NewId() + +# Define Text Drop Target class +class TextDropTarget(wx.TextDropTarget): + """ This object implements Drop Target functionality for Text """ + def __init__(self, obj): + """ Initialize the Drop Target, passing in the Object Reference to + indicate what should receive the dropped text """ + # Initialize the wx.TextDropTarget Object + wx.TextDropTarget.__init__(self) + # Store the Object Reference for dropped text + self.obj = obj + + def OnDropText(self, x, y, data): + """ Implement Text Drop """ + # When text is dropped, write it into the object specified + self.obj.WriteText(data + '\n\n') + +# Define File Drop Target class +class FileDropTarget(wx.FileDropTarget): + """ This object implements Drop Target functionality for Files """ + def __init__(self, obj): + """ Initialize the Drop Target, passing in the Object Reference to + indicate what should receive the dropped files """ + # Initialize the wxFileDropTarget Object + wx.FileDropTarget.__init__(self) + # Store the Object Reference for dropped files + self.obj = obj + + def OnDropFiles(self, x, y, filenames): + """ Implement File Drop """ + # For Demo purposes, this function appends a list of the files dropped at the end of the widget's text + # Move Insertion Point to the end of the widget's text + self.obj.SetInsertionPointEnd() + # append a list of the file names dropped + self.obj.WriteText("%d file(s) dropped at %d, %d:\n" % (len(filenames), x, y)) + for file in filenames: + self.obj.WriteText(file + '\n') + self.obj.WriteText('\n') + + + +class MainWindow(wx.Frame): + """ This window displays the GUI Widgets. """ + def __init__(self,parent,id,title): + wx.Frame.__init__(self,parent, wx.ID_ANY, title, size = (800,600), style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE) + self.SetBackgroundColour(wx.WHITE) + + # Menu Bar + # Create a MenuBar + menuBar = wx.MenuBar() + # Build a Menu Object to go into the Menu Bar + menu1 = wx.Menu() + menu1.Append(MENU_FILE_EXIT, "E&xit", "Quit Application") + # Place the Menu Item in the Menu Bar + menuBar.Append(menu1, "&File") + # Place the Menu Bar on the ap + self.SetMenuBar(menuBar) + #Define Events for the Menu Items + wx.EVT_MENU(self, MENU_FILE_EXIT, self.CloseWindow) + + # GUI Widgets + # Define a Text Control from which Text can be dragged for dropping + # Label the control + wx.StaticText(self, -1, "Text Drag Source (left-click to select, right-click to drag)", (10, 1)) + # Create a Text Control + self.text = wx.TextCtrl(self, DRAG_SOURCE, "", pos=(10,15), size=(350,500), style = wx.TE_MULTILINE|wx.HSCROLL) + # Make this control a Text Drop Target + # Create a Text Drop Target object + dt1 = TextDropTarget(self.text) + # Link the Drop Target Object to the Text Control + self.text.SetDropTarget(dt1) + # Put some text in the control as a starting place to have something to copy + for x in range(20): + self.text.WriteText("This is line %d of some text to drag.\n" % x) + # Define Right-Click as start of Drag + wx.EVT_RIGHT_DOWN(self.text, self.OnDragInit) + + # Define a Text Control to recieve Dropped Text + # Label the control + wx.StaticText(self, -1, "Text Drop Target", (370, 1)) + # Create a read-only Text Control + self.text2 = wx.TextCtrl(self, -1, "", pos=(370,15), size=(410,235), style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY) + # Make this control a Text Drop Target + # Create a Text Drop Target object + dt2 = TextDropTarget(self.text2) + # Link the Drop Target Object to the Text Control + self.text2.SetDropTarget(dt2) + + # Define a Text Control to receive Dropped Files + # Label the control + wx.StaticText(self, -1, "File Drop Target (from off application only)", (370, 261)) + # Create a read-only Text Control + self.text3 = wx.TextCtrl(self, -1, "", pos=(370,275), size=(410,235), style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY) + # Make this control a File Drop Target + # Create a File Drop Target object + dt3 = FileDropTarget(self.text3) + # Link the Drop Target Object to the Text Control + self.text3.SetDropTarget(dt3) + + # Display the Window + self.Show(True) + + + def CloseWindow(self, event): + """ Close the Window """ + self.Close() + + def OnDragInit(self, event): + """ Begin a Drag Operation """ + # Create a Text Data Object, which holds the text that is to be dragged + tdo = wx.PyTextDataObject(self.text.GetStringSelection()) + # Create a Drop Source Object, which enables the Drag operation + tds = wx.DropSource(self.text) + # Associate the Data to be dragged with the Drop Source Object + tds.SetData(tdo) + # Initiate the Drag Operation + tds.DoDragDrop(True) + + + +class MyApp(wx.App): + """ Define the Drag and Drop Example Application """ + def OnInit(self): + """ Initialize the Application """ + # Declare the Main Application Window + frame = MainWindow(None, -1, "Drag and Drop Example") + # Show the Application as the top window + self.SetTopWindow(frame) + return True + + +# Declare the Application and start the Main Loop +app = MyApp(0) +app.MainLoop() \ No newline at end of file From 7f392006d15a6c70a30686b0aa5e2df0eb8c963d Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 16:37:15 -0400 Subject: [PATCH 002/212] comment out most of the fluff in pyfa.py. Need to come back to this and get it all working --- config.py | 2 +- eos/config.py | 6 +- pyfa.py | 294 +++++++++++++++++++++++++------------------------- 3 files changed, 149 insertions(+), 153 deletions(-) diff --git a/config.py b/config.py index 7162f68d0..c9bd69842 100644 --- a/config.py +++ b/config.py @@ -47,7 +47,7 @@ def __createDirs(path): 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 = str(root, sys.getfilesystemencoding()) + root = root return root diff --git a/eos/config.py b/eos/config.py index 2e34df838..2c1dd7e71 100644 --- a/eos/config.py +++ b/eos/config.py @@ -11,14 +11,14 @@ debug = False gamedataCache = True saveddataCache = True gamedata_version = "" -gamedata_connectionstring = 'sqlite:///' + str(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding()) +gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "eve.db")) pyfalog.debug("Gamedata connection string: {0}", gamedata_connectionstring) if istravis is True or hasattr(sys, '_called_from_test'): # Running in Travis. Run saveddata database in memory. saveddata_connectionstring = 'sqlite:///:memory:' else: - saveddata_connectionstring = 'sqlite:///' + str(realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding()) + saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")) pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring) @@ -28,4 +28,4 @@ settings = { } # Autodetect path, only change if the autodetection bugs out. -path = dirname(str(__file__, sys.getfilesystemencoding())) +path = dirname(__file__) diff --git a/pyfa.py b/pyfa.py index fd205f6df..7f0b99140 100755 --- a/pyfa.py +++ b/pyfa.py @@ -31,11 +31,6 @@ from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, import config -try: - import wxversion -except ImportError: - wxversion = None - try: import sqlalchemy except ImportError: @@ -59,108 +54,108 @@ class PassThroughOptionParser(OptionParser): pyfalog.error("Bad startup option passed.") largs.append(e.opt_str) - -class LoggerWriter(object): - def __init__(self, level): - # self.level is really like using log.debug(message) - # at least in my case - self.level = level - - def write(self, message): - # if statement reduces the amount of newlines that are - # printed to the logger - if message not in {'\n', ' '}: - self.level(message.replace("\n", "")) - - def flush(self): - # create a flush method so things can be flushed when - # the system wants to. Not sure if simply 'printing' - # sys.stderr is the correct way to do it, but it seemed - # to work properly for me. - self.level(sys.stderr) - - -class PreCheckException(Exception): - def __init__(self, msg): - try: - ln = sys.exc_info()[-1].tb_lineno - except AttributeError: - ln = inspect.currentframe().f_back.f_lineno - self.message = "{0.__name__} (line {1}): {2}".format(type(self), ln, msg) - self.args = self.message, - - -def handleGUIException(exc_type, exc_value, exc_traceback): - try: - # Try and import wx in case it's missing. - # noinspection PyPackageRequirements - import wx - from gui.errorDialog import ErrorFrame - except: - # noinspection PyShadowingNames - wx = None - # noinspection PyShadowingNames - ErrorFrame = None - - tb = traceback.format_tb(exc_traceback) - - try: - - # Try and output to our log handler - with logging_setup.threadbound(): - module_list = list(set(sys.modules) & set(globals())) - if module_list: - pyfalog.info("Imported Python Modules:") - for imported_module in module_list: - module_details = sys.modules[imported_module] - pyfalog.info("{0}: {1}", imported_module, getattr(module_details, '__version__', '')) - - pyfalog.critical("Exception in main thread: {0}", exc_value.message) - # Print the base level traceback - traceback.print_tb(exc_traceback) - - if wx and ErrorFrame: - pyfa_gui = wx.App(False) - if exc_type == PreCheckException: - msgbox = wx.MessageBox(exc_value.message, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) - msgbox.ShowModal() - else: - ErrorFrame(exc_value, tb) - - pyfa_gui.MainLoop() - - pyfalog.info("Exiting.") - except: - # Most likely logging isn't available. Try and output to the console - module_list = list(set(sys.modules) & set(globals())) - if module_list: - pyfalog.info("Imported Python Modules:") - for imported_module in module_list: - module_details = sys.modules[imported_module] - print((str(imported_module) + ": " + str(getattr(module_details, '__version__', '')))) - - print(("Exception in main thread: " + str(exc_value.message))) - traceback.print_tb(exc_traceback) - - if wx and ErrorFrame: - pyfa_gui = wx.App(False) - if exc_type == PreCheckException: - msgbox = wx.MessageBox(exc_value.message, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) - msgbox.ShowModal() - else: - ErrorFrame(exc_value, tb) - - pyfa_gui.MainLoop() - - print("Exiting.") - - finally: - # TODO: Add cleanup when exiting here. - sys.exit() - - -# Replace the uncaught exception handler with our own handler. -sys.excepthook = handleGUIException +# +# class LoggerWriter(object): +# def __init__(self, level): +# # self.level is really like using log.debug(message) +# # at least in my case +# self.level = level +# +# def write(self, message): +# # if statement reduces the amount of newlines that are +# # printed to the logger +# if message not in {'\n', ' '}: +# self.level(message.replace("\n", "")) +# +# def flush(self): +# # create a flush method so things can be flushed when +# # the system wants to. Not sure if simply 'printing' +# # sys.stderr is the correct way to do it, but it seemed +# # to work properly for me. +# self.level(sys.stderr) +# +# +# class PreCheckException(Exception): +# def __init__(self, msg): +# try: +# ln = sys.exc_info()[-1].tb_lineno +# except AttributeError: +# ln = inspect.currentframe().f_back.f_lineno +# self.message = "{0.__name__} (line {1}): {2}".format(type(self), ln, msg) +# self.args = self.message, +# +# +# def handleGUIException(exc_type, exc_value, exc_traceback): +# try: +# # Try and import wx in case it's missing. +# # noinspection PyPackageRequirements +# import wx +# from gui.errorDialog import ErrorFrame +# except: +# # noinspection PyShadowingNames +# wx = None +# # noinspection PyShadowingNames +# ErrorFrame = None +# +# tb = traceback.format_tb(exc_traceback) +# +# try: +# +# # Try and output to our log handler +# with logging_setup.threadbound(): +# module_list = list(set(sys.modules) & set(globals())) +# if module_list: +# pyfalog.info("Imported Python Modules:") +# for imported_module in module_list: +# module_details = sys.modules[imported_module] +# pyfalog.info("{0}: {1}", imported_module, getattr(module_details, '__version__', '')) +# +# pyfalog.critical("Exception in main thread: {0}", exc_value.message) +# # Print the base level traceback +# traceback.print_tb(exc_traceback) +# +# if wx and ErrorFrame: +# pyfa_gui = wx.App(False) +# if exc_type == PreCheckException: +# msgbox = wx.MessageBox(exc_value.message, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) +# msgbox.ShowModal() +# else: +# ErrorFrame(exc_value, tb) +# +# pyfa_gui.MainLoop() +# +# pyfalog.info("Exiting.") +# except: +# # Most likely logging isn't available. Try and output to the console +# module_list = list(set(sys.modules) & set(globals())) +# if module_list: +# pyfalog.info("Imported Python Modules:") +# for imported_module in module_list: +# module_details = sys.modules[imported_module] +# print((str(imported_module) + ": " + str(getattr(module_details, '__version__', '')))) +# +# print(("Exception in main thread: " + str(exc_value.message))) +# traceback.print_tb(exc_traceback) +# +# if wx and ErrorFrame: +# pyfa_gui = wx.App(False) +# if exc_type == PreCheckException: +# msgbox = wx.MessageBox(exc_value.message, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) +# msgbox.ShowModal() +# else: +# ErrorFrame(exc_value, tb) +# +# pyfa_gui.MainLoop() +# +# print("Exiting.") +# +# finally: +# # TODO: Add cleanup when exiting here. +# sys.exit() +# +# +# # Replace the uncaught exception handler with our own handler. +# sys.excepthook = handleGUIException # Parse command line options usage = "usage: %prog [--root]" @@ -173,19 +168,19 @@ parser.add_option("-s", "--savepath", action="store", dest="savepath", help="Set parser.add_option("-l", "--logginglevel", action="store", dest="logginglevel", help="Set desired logging level [Critical|Error|Warning|Info|Debug]", default="Error") (options, args) = parser.parse_args() - -if options.logginglevel == "Critical": - options.logginglevel = CRITICAL -elif options.logginglevel == "Error": - options.logginglevel = ERROR -elif options.logginglevel == "Warning": - options.logginglevel = WARNING -elif options.logginglevel == "Info": - options.logginglevel = INFO -elif options.logginglevel == "Debug": - options.logginglevel = DEBUG -else: - options.logginglevel = ERROR +# +# if options.logginglevel == "Critical": +# options.logginglevel = CRITICAL +# elif options.logginglevel == "Error": +# options.logginglevel = ERROR +# elif options.logginglevel == "Warning": +# options.logginglevel = WARNING +# elif options.logginglevel == "Info": +# options.logginglevel = INFO +# elif options.logginglevel == "Debug": +# options.logginglevel = DEBUG +# else: +# options.logginglevel = ERROR if __name__ == "__main__": # Configure paths @@ -284,55 +279,55 @@ if __name__ == "__main__": pyfalog.info("Writing log file to: {0}", config.logPath) # Output all stdout (print) messages as warnings - try: - sys.stdout = LoggerWriter(pyfalog.warning) - except: - pyfalog.critical("Cannot redirect. Continuing without writing stdout to log.") + # try: + # sys.stdout = LoggerWriter(pyfalog.warning) + # except: + # pyfalog.critical("Cannot redirect. Continuing without writing stdout to log.") # Output all stderr (stacktrace) messages as critical - try: - sys.stderr = LoggerWriter(pyfalog.critical) - except: - pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.") + # try: + # sys.stderr = LoggerWriter(pyfalog.critical) + # except: + # pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.") pyfalog.info("OS version: {0}", platform.platform()) pyfalog.info("Python version: {0}", sys.version) - if sys.version_info < (2, 7) or sys.version_info > (3, 0): - exit_message = "Pyfa requires python 2.x branch ( >= 2.7 )." - raise PreCheckException(exit_message) + # if sys.version_info < (2, 7) or sys.version_info > (3, 0): + # exit_message = "Pyfa requires python 2.x branch ( >= 2.7 )." + # raise PreCheckException(exit_message) if hasattr(sys, 'frozen'): pyfalog.info("Running in a frozen state.") else: pyfalog.info("Running in a thawed state.") - if not hasattr(sys, 'frozen') and wxversion: - try: - if options.force28 is True: - pyfalog.info("Selecting wx version: 2.8. (Forced)") - wxversion.select('2.8') - else: - pyfalog.info("Selecting wx versions: 3.0, 2.8") - wxversion.select(['3.0', '2.8']) - except: - pyfalog.warning("Unable to select wx version. Attempting to import wx without specifying the version.") - else: - if not wxversion: - pyfalog.warning("wxVersion not found. Attempting to import wx without specifying the version.") + # if not hasattr(sys, 'frozen') and wxversion: + # try: + # if options.force28 is True: + # pyfalog.info("Selecting wx version: 2.8. (Forced)") + # wxversion.select('2.8') + # else: + # pyfalog.info("Selecting wx versions: 3.0, 2.8") + # wxversion.select(['3.0', '2.8']) + # except: + # pyfalog.warning("Unable to select wx version. Attempting to import wx without specifying the version.") + # else: + # if not wxversion: + # pyfalog.warning("wxVersion not found. Attempting to import wx without specifying the version.") try: # noinspection PyPackageRequirements import wx except: exit_message = "Cannot import wxPython. You can download wxPython (2.8+) from http://www.wxpython.org/" - raise PreCheckException(exit_message) + # raise PreCheckException(exit_message) pyfalog.info("wxPython version: {0}.", str(wx.VERSION_STRING)) if sqlalchemy is None: exit_message = "\nCannot find sqlalchemy.\nYou can download sqlalchemy (0.6+) from http://www.sqlalchemy.org/" - raise PreCheckException(exit_message) + # raise PreCheckException(exit_message) else: saVersion = sqlalchemy.__version__ saMatch = re.match("([0-9]+).([0-9]+)([b\.])([0-9]+)", saVersion) @@ -352,15 +347,16 @@ if __name__ == "__main__": pyfalog.warning("Unknown sqlalchemy version string format, skipping check. Version: {0}", sqlalchemy.__version__) logVersion = logbook_version.split('.') - if int(logVersion[0]) == 0 and int(logVersion[1]) < 10: - raise PreCheckException("Logbook version >= 0.10.0 is required.") + # if int(logVersion[0]) == 0 and int(logVersion[1]) < 10: + # raise PreCheckException("Logbook version >= 0.10.0 is required.") if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): try: import requests config.requestsVersion = requests.__version__ except ImportError: - raise PreCheckException("Cannot import requests. You can download requests from https://pypi.python.org/pypi/requests.") + pass + # raise PreCheckException("Cannot import requests. You can download requests from https://pypi.python.org/pypi/requests.") import eos.db From 8e4db5a8c394111e00d75248069417df75e857ec Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 18:08:07 -0400 Subject: [PATCH 003/212] Do some renaming of some files --- gui/additionsPane.py | 4 ++-- gui/{bitmapLoader.py => bitmap_loader.py} | 0 gui/builtinContextMenus/changeAffectingSkills.py | 2 +- gui/builtinContextMenus/damagePattern.py | 2 +- gui/builtinContextMenus/factorReload.py | 2 +- gui/builtinContextMenus/moduleAmmoPicker.py | 2 +- gui/builtinContextMenus/targetResists.py | 2 +- gui/builtinGraphs/fitDps.py | 2 +- gui/builtinMarketBrowser/pfSearchBox.py | 4 ++-- gui/builtinMarketBrowser/searchBox.py | 2 +- .../pyfaContextMenuPreferences.py | 2 +- .../pyfaCrestPreferences.py | 2 +- .../pyfaDatabasePreferences.py | 2 +- .../pyfaEnginePreferences.py | 2 +- .../pyfaGaugePreferences.py | 16 ++++++++-------- .../pyfaGeneralPreferences.py | 2 +- .../pyfaHTMLExportPreferences.py | 2 +- .../pyfaLoggingPreferences.py | 2 +- .../pyfaNetworkPreferences.py | 2 +- .../pyfaStatViewPreferences.py | 2 +- .../pyfaUpdatePreferences.py | 2 +- gui/builtinShipBrowser/categoryItem.py | 6 +++--- gui/builtinShipBrowser/fitItem.py | 6 +++--- gui/builtinShipBrowser/navigationPanel.py | 6 +++--- gui/builtinShipBrowser/pfWidgetContainer.py | 2 +- gui/builtinShipBrowser/raceSelector.py | 8 ++++---- gui/builtinShipBrowser/sfBrowserItem.py | 2 +- gui/builtinShipBrowser/shipItem.py | 6 +++--- gui/builtinStatsViews/capacitorViewFull.py | 2 +- gui/builtinStatsViews/firepowerViewFull.py | 2 +- gui/builtinStatsViews/miningyieldViewFull.py | 2 +- gui/builtinStatsViews/outgoingViewFull.py | 2 +- gui/builtinStatsViews/priceViewFull.py | 2 +- gui/builtinStatsViews/priceViewMinimal.py | 2 +- gui/builtinStatsViews/rechargeViewFull.py | 2 +- gui/builtinStatsViews/resistancesViewFull.py | 2 +- gui/builtinStatsViews/resourcesViewFull.py | 4 ++-- gui/builtinViewColumns/ammo.py | 2 +- gui/builtinViewColumns/attributeDisplay.py | 2 +- gui/builtinViewColumns/capacitorUse.py | 2 +- gui/builtinViewColumns/maxRange.py | 2 +- gui/builtinViewColumns/misc.py | 2 +- gui/builtinViewColumns/price.py | 2 +- gui/builtinViews/emptyView.py | 2 +- gui/builtinViews/entityEditor.py | 2 +- gui/builtinViews/fittingView.py | 4 ++-- gui/builtinViews/implantEditor.py | 2 +- gui/cachingImageList.py | 2 +- gui/characterEditor.py | 2 +- gui/characterSelection.py | 2 +- gui/{chromeTabs.py => chrome_tabs.py} | 6 +++--- gui/graphFrame.py | 2 +- gui/itemStats.py | 2 +- gui/mainFrame.py | 4 ++-- gui/mainMenuBar.py | 2 +- gui/multiSwitch.py | 2 +- gui/patternEditor.py | 2 +- gui/preferenceDialog.py | 2 +- gui/propertyEditor.py | 2 +- gui/pyfatogglepanel.py | 2 +- gui/pygauge.py | 16 ++++++++-------- gui/resistsEditor.py | 2 +- gui/utils/{animUtils.py => anim.py} | 2 +- gui/utils/{animEffects.py => anim_effects.py} | 0 gui/utils/{colorUtils.py => color.py} | 0 gui/utils/{drawUtils.py => draw.py} | 2 +- 66 files changed, 95 insertions(+), 95 deletions(-) rename gui/{bitmapLoader.py => bitmap_loader.py} (100%) rename gui/{chromeTabs.py => chrome_tabs.py} (99%) rename gui/utils/{animUtils.py => anim.py} (98%) rename gui/utils/{animEffects.py => anim_effects.py} (100%) rename gui/utils/{colorUtils.py => color.py} (100%) rename gui/utils/{drawUtils.py => draw.py} (98%) diff --git a/gui/additionsPane.py b/gui/additionsPane.py index b12fe5036..00a126a30 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.builtinAdditionPanes.boosterView import BoosterView from gui.builtinAdditionPanes.cargoView import CargoView from gui.builtinAdditionPanes.commandView import CommandView @@ -29,7 +29,7 @@ from gui.builtinAdditionPanes.fighterView import FighterView from gui.builtinAdditionPanes.implantView import ImplantView from gui.builtinAdditionPanes.notesView import NotesView from gui.builtinAdditionPanes.projectedView import ProjectedView -from gui.chromeTabs import PFNotebook +from gui.chrome_tabs import PFNotebook from gui.pyfatogglepanel import TogglePanel diff --git a/gui/bitmapLoader.py b/gui/bitmap_loader.py similarity index 100% rename from gui/bitmapLoader.py rename to gui/bitmap_loader.py diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index 1f16b8cb8..523557379 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -3,7 +3,7 @@ from gui.contextMenu import ContextMenu import gui.mainFrame # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from eos.saveddata.character import Skill import gui.globalEvents as GE from service.fit import Fit diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index 86899aeab..710447a50 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -3,7 +3,7 @@ import gui.mainFrame import gui.globalEvents as GE # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.fit import Fit from service.damagePattern import DamagePattern as import_DamagePattern from service.settings import ContextMenuSettings diff --git a/gui/builtinContextMenus/factorReload.py b/gui/builtinContextMenus/factorReload.py index 6770e64af..64e246413 100644 --- a/gui/builtinContextMenus/factorReload.py +++ b/gui/builtinContextMenus/factorReload.py @@ -3,7 +3,7 @@ import gui.mainFrame import gui.globalEvents as GE # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.fit import Fit from service.settings import ContextMenuSettings diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 79cd790f0..7b1f86169 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -9,7 +9,7 @@ from eos.saveddata.module import Hardpoint import gui.mainFrame import gui.globalEvents as GE from gui.contextMenu import ContextMenu -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.settings import ContextMenuSettings diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index ca59e22f1..677605f31 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -3,7 +3,7 @@ import gui.mainFrame import gui.globalEvents as GE # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.targetResists import TargetResists as svc_TargetResists from service.fit import Fit from service.settings import ContextMenuSettings diff --git a/gui/builtinGraphs/fitDps.py b/gui/builtinGraphs/fitDps.py index 9a3048a24..d848d3f9c 100644 --- a/gui/builtinGraphs/fitDps.py +++ b/gui/builtinGraphs/fitDps.py @@ -18,7 +18,7 @@ # ============================================================================= from gui.graph import Graph -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from eos.graph.fitDps import FitDpsGraph as FitDps from eos.graph import Data import gui.mainFrame diff --git a/gui/builtinMarketBrowser/pfSearchBox.py b/gui/builtinMarketBrowser/pfSearchBox.py index f5f8d8c6d..12a069e85 100644 --- a/gui/builtinMarketBrowser/pfSearchBox.py +++ b/gui/builtinMarketBrowser/pfSearchBox.py @@ -1,7 +1,7 @@ # noinspection PyPackageRequirements import wx -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils SearchButton, EVT_SEARCH_BTN = wx.lib.newevent.NewEvent() CancelButton, EVT_CANCEL_BTN = wx.lib.newevent.NewEvent() diff --git a/gui/builtinMarketBrowser/searchBox.py b/gui/builtinMarketBrowser/searchBox.py index fb86bbb2e..43dd9acf6 100644 --- a/gui/builtinMarketBrowser/searchBox.py +++ b/gui/builtinMarketBrowser/searchBox.py @@ -1,4 +1,4 @@ -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from .pfSearchBox import PFSearchBox diff --git a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py index ab286458e..458424dce 100644 --- a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py +++ b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py @@ -1,7 +1,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.mainFrame from service.settings import ContextMenuSettings diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py index be427ff0d..2e82cb7bc 100644 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ b/gui/builtinPreferenceViews/pyfaCrestPreferences.py @@ -2,7 +2,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.mainFrame diff --git a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py index 1e644102a..200cbb597 100644 --- a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py +++ b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py @@ -1,7 +1,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils import helpers_wxPython as wxHelpers import config from eos.db.saveddata.queries import clearPrices, clearDamagePatterns, clearTargetResists diff --git a/gui/builtinPreferenceViews/pyfaEnginePreferences.py b/gui/builtinPreferenceViews/pyfaEnginePreferences.py index 641b48f8a..c4d8c7f46 100644 --- a/gui/builtinPreferenceViews/pyfaEnginePreferences.py +++ b/gui/builtinPreferenceViews/pyfaEnginePreferences.py @@ -3,7 +3,7 @@ import logging import wx from service.fit import Fit -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.preferenceView import PreferenceView from service.settings import EOSSettings diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index 557d79c4e..c17002590 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -7,9 +7,9 @@ import wx import copy from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader -from gui.utils import colorUtils -import gui.utils.drawUtils as drawUtils +from gui.bitmap_loader import BitmapLoader +from gui.utils import color +import gui.utils.draw as drawUtils ########################################################################### @@ -122,13 +122,13 @@ class PFGaugePreview(wx.Window): r = copy.copy(rect) r.width = w - color = colorUtils.CalculateTransitionColor(self.colorS, self.colorE, float(value) / 100) + color = color.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 = color.BrightenColor(color, float(self.gradientStart) / 100) + gMid = color.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 = color.DarkenColor(color, float(-self.gradientStart) / 100) + gMid = color.DarkenColor(color, float(-self.gradientStart / 2) / 100) gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) dc.DrawBitmap(gBmp, 0, 0) diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index caecd1cc9..23a1d67a4 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -3,7 +3,7 @@ import wx from wx.lib.intctrl import IntCtrl from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.mainFrame import gui.globalEvents as GE diff --git a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py index 1afdc7f90..2165ce114 100644 --- a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py +++ b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py @@ -3,7 +3,7 @@ import wx import os from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.mainFrame diff --git a/gui/builtinPreferenceViews/pyfaLoggingPreferences.py b/gui/builtinPreferenceViews/pyfaLoggingPreferences.py index 78490c2ad..e83fcd799 100644 --- a/gui/builtinPreferenceViews/pyfaLoggingPreferences.py +++ b/gui/builtinPreferenceViews/pyfaLoggingPreferences.py @@ -1,7 +1,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import config from logbook import Logger diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index 78b6dd32d..711d332c4 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -2,7 +2,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.mainFrame from service.settings import NetworkSettings diff --git a/gui/builtinPreferenceViews/pyfaStatViewPreferences.py b/gui/builtinPreferenceViews/pyfaStatViewPreferences.py index eb7d424b8..e9e0d8cb0 100644 --- a/gui/builtinPreferenceViews/pyfaStatViewPreferences.py +++ b/gui/builtinPreferenceViews/pyfaStatViewPreferences.py @@ -2,7 +2,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.settings import StatViewSettings diff --git a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py index b1158df94..8bb6faad2 100644 --- a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py +++ b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py @@ -2,7 +2,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.settings import UpdateSettings diff --git a/gui/builtinShipBrowser/categoryItem.py b/gui/builtinShipBrowser/categoryItem.py index 86ce08f75..7788aeef9 100644 --- a/gui/builtinShipBrowser/categoryItem.py +++ b/gui/builtinShipBrowser/categoryItem.py @@ -4,10 +4,10 @@ import wx from logbook import Logger from gui.builtinShipBrowser.sfBrowserItem import SFBrowserItem -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from .events import * pyfalog = Logger(__name__) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index bf51204b4..5e6b089c3 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -9,11 +9,11 @@ from logbook import Logger import gui.builtinShipBrowser.sfBrowserItem as SFItem import gui.globalEvents as GE import gui.mainFrame -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils import gui.utils.fonts as fonts from .events import * -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.builtinShipBrowser.pfBitmapFrame import PFBitmapFrame from service.fit import Fit diff --git a/gui/builtinShipBrowser/navigationPanel.py b/gui/builtinShipBrowser/navigationPanel.py index 1e0112f93..7f802a71b 100644 --- a/gui/builtinShipBrowser/navigationPanel.py +++ b/gui/builtinShipBrowser/navigationPanel.py @@ -5,11 +5,11 @@ from logbook import Logger import gui.builtinShipBrowser.sfBrowserItem as SFItem import gui.mainFrame -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils import gui.utils.fonts as fonts from .events import * -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.fit import Fit pyfalog = Logger(__name__) diff --git a/gui/builtinShipBrowser/pfWidgetContainer.py b/gui/builtinShipBrowser/pfWidgetContainer.py index 305bd6385..e3219530a 100644 --- a/gui/builtinShipBrowser/pfWidgetContainer.py +++ b/gui/builtinShipBrowser/pfWidgetContainer.py @@ -1,6 +1,6 @@ from gui.builtinShipBrowser.pfListPane import PFListPane import gui.mainFrame -import gui.utils.animUtils as animUtils +import gui.utils.anim as animUtils class PFWidgetsContainer(PFListPane): diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index 5df6b772d..5b3b40601 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -3,11 +3,11 @@ import wx from logbook import Logger -import gui.utils.animEffects as animEffects -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.anim_effects as animEffects +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils from .events import * -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader pyfalog = Logger(__name__) diff --git a/gui/builtinShipBrowser/sfBrowserItem.py b/gui/builtinShipBrowser/sfBrowserItem.py index 246a1b407..876143bdb 100644 --- a/gui/builtinShipBrowser/sfBrowserItem.py +++ b/gui/builtinShipBrowser/sfBrowserItem.py @@ -1,6 +1,6 @@ # noinspection PyPackageRequirements import wx -import gui.utils.drawUtils as drawUtils +import gui.utils.draw as drawUtils SB_ITEM_NORMAL = 0 SB_ITEM_SELECTED = 1 diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py index 1f7112d0a..b6879f01b 100644 --- a/gui/builtinShipBrowser/shipItem.py +++ b/gui/builtinShipBrowser/shipItem.py @@ -5,11 +5,11 @@ from logbook import Logger import gui.builtinShipBrowser.sfBrowserItem as SFItem import gui.mainFrame -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils import gui.utils.fonts as fonts from .events import * -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.contextMenu import ContextMenu from service.fit import Fit from service.market import Market diff --git a/gui/builtinStatsViews/capacitorViewFull.py b/gui/builtinStatsViews/capacitorViewFull.py index 1986d29f9..2c478b163 100644 --- a/gui/builtinStatsViews/capacitorViewFull.py +++ b/gui/builtinStatsViews/capacitorViewFull.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 618ca52de..33c6092d8 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -21,7 +21,7 @@ import wx import gui.mainFrame from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.fit import Fit diff --git a/gui/builtinStatsViews/miningyieldViewFull.py b/gui/builtinStatsViews/miningyieldViewFull.py index 476a5644a..f35902653 100644 --- a/gui/builtinStatsViews/miningyieldViewFull.py +++ b/gui/builtinStatsViews/miningyieldViewFull.py @@ -21,7 +21,7 @@ import wx import gui.mainFrame from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.fit import Fit diff --git a/gui/builtinStatsViews/outgoingViewFull.py b/gui/builtinStatsViews/outgoingViewFull.py index da0c73829..a73363172 100644 --- a/gui/builtinStatsViews/outgoingViewFull.py +++ b/gui/builtinStatsViews/outgoingViewFull.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index 7a91c8d04..b8fc25803 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.price import Price diff --git a/gui/builtinStatsViews/priceViewMinimal.py b/gui/builtinStatsViews/priceViewMinimal.py index a928918fb..04746b1a5 100644 --- a/gui/builtinStatsViews/priceViewMinimal.py +++ b/gui/builtinStatsViews/priceViewMinimal.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.price import Price diff --git a/gui/builtinStatsViews/rechargeViewFull.py b/gui/builtinStatsViews/rechargeViewFull.py index 18af61d73..767172560 100644 --- a/gui/builtinStatsViews/rechargeViewFull.py +++ b/gui/builtinStatsViews/rechargeViewFull.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount import gui.mainFrame from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index 98b0c896f..a06e4e444 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.pygauge import PyGauge from gui.utils.numberFormatter import formatAmount import gui.mainFrame diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index 298fed756..2a8cb0196 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -20,10 +20,10 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.pygauge import PyGauge import gui.mainFrame -from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED +from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED from eos.saveddata.module import Hardpoint diff --git a/gui/builtinViewColumns/ammo.py b/gui/builtinViewColumns/ammo.py index 069328a32..862148285 100644 --- a/gui/builtinViewColumns/ammo.py +++ b/gui/builtinViewColumns/ammo.py @@ -21,7 +21,7 @@ import wx from eos.saveddata.fighter import Fighter from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class Ammo(ViewColumn): diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index 0ccdcd96b..6b5422f1d 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -21,7 +21,7 @@ import wx from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.attribute import Attribute diff --git a/gui/builtinViewColumns/capacitorUse.py b/gui/builtinViewColumns/capacitorUse.py index 5d0287375..8770a9d4c 100644 --- a/gui/builtinViewColumns/capacitorUse.py +++ b/gui/builtinViewColumns/capacitorUse.py @@ -24,7 +24,7 @@ from eos.saveddata.mode import Mode from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class CapacitorUse(ViewColumn): diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py index d4b3367da..728e2b9bc 100644 --- a/gui/builtinViewColumns/maxRange.py +++ b/gui/builtinViewColumns/maxRange.py @@ -23,7 +23,7 @@ import wx from eos.saveddata.mode import Mode from service.attribute import Attribute from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index 0c3971445..e3ee83bd6 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -24,7 +24,7 @@ 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.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from gui.utils.listFormatter import formatList from eos.saveddata.drone import Drone diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 3797052a2..728376bef 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -24,7 +24,7 @@ from eos.saveddata.cargo import Cargo from eos.saveddata.drone import Drone from service.price import Price as ServicePrice from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount diff --git a/gui/builtinViews/emptyView.py b/gui/builtinViews/emptyView.py index ebf667726..c5df9e4bd 100644 --- a/gui/builtinViews/emptyView.py +++ b/gui/builtinViews/emptyView.py @@ -1,7 +1,7 @@ # noinspection PyPackageRequirements import wx import gui.globalEvents as GE -from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED +from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED import gui.mainFrame diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index 5e717378f..13ebb8162 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -1,6 +1,6 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class BaseValidator(wx.PyValidator): diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 1b9544e08..75f76cc31 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -30,10 +30,10 @@ import gui.multiSwitch from eos.saveddata.mode import Mode from eos.saveddata.module import Module, Slot, Rack from gui.builtinViewColumns.state import State -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.builtinViews.emptyView from logbook import Logger -from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED +from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED from service.fit import Fit from service.market import Market diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index 827d514a2..36bcc37f0 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -5,7 +5,7 @@ from wx.lib.buttons import GenBitmapButton import gui.builtinMarketBrowser.pfSearchBox as SBox import gui.display as d -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.marketBrowser import SearchBox from service.market import Market diff --git a/gui/cachingImageList.py b/gui/cachingImageList.py index 4bbef1e23..578da97c7 100644 --- a/gui/cachingImageList.py +++ b/gui/cachingImageList.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class CachingImageList(wx.ImageList): diff --git a/gui/characterEditor.py b/gui/characterEditor.py index bb8c52892..4865cb578 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -25,7 +25,7 @@ from .utils.floatspin import FloatSpin import wx.lib.newevent # noinspection PyPackageRequirements import wx.gizmos -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.contextMenu import ContextMenu import gui.globalEvents as GE from gui.builtinViews.implantEditor import BaseImplantEditorView diff --git a/gui/characterSelection.py b/gui/characterSelection.py index 4b6947e4b..c28f8b798 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -19,7 +19,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.globalEvents as GE import gui.mainFrame from service.character import Character diff --git a/gui/chromeTabs.py b/gui/chrome_tabs.py similarity index 99% rename from gui/chromeTabs.py rename to gui/chrome_tabs.py index 89ad9327b..9ee74a2ac 100644 --- a/gui/chromeTabs.py +++ b/gui/chrome_tabs.py @@ -21,10 +21,10 @@ import wx # noinspection PyPackageRequirements import wx.lib.newevent -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from logbook import Logger from service.fit import Fit diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 178e9c8d9..d8eb5c9ea 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -28,7 +28,7 @@ import gui.display import gui.mainFrame import gui.globalEvents as GE from gui.graph import Graph -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import traceback pyfalog = Logger(__name__) diff --git a/gui/itemStats.py b/gui/itemStats.py index 90f3172e4..79a9b9caf 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -45,7 +45,7 @@ from service.market import Market from service.attribute import Attribute from service.price import Price as ServicePrice import gui.mainFrame -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from gui.contextMenu import ContextMenu diff --git a/gui/mainFrame.py b/gui/mainFrame.py index a7eafadcd..bffee39b7 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -39,10 +39,10 @@ import config from eos.config import gamedata_version import gui.aboutData -from gui.chromeTabs import PFNotebook +from gui.chrome_tabs import PFNotebook import gui.globalEvents as GE -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.mainMenuBar import MainMenuBar from gui.additionsPane import AdditionsPane from gui.marketBrowser import MarketBrowser diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index bade53d3d..128cb23fb 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -25,7 +25,7 @@ from service.character import Character from service.fit import Fit import gui.graphFrame import gui.globalEvents as GE -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from logbook import Logger pyfalog = Logger(__name__) diff --git a/gui/multiSwitch.py b/gui/multiSwitch.py index 2efe80bc0..d64d41e68 100644 --- a/gui/multiSwitch.py +++ b/gui/multiSwitch.py @@ -17,7 +17,7 @@ # along with pyfa. If not, see . # ============================================================================= -from gui.chromeTabs import PFNotebook +from gui.chrome_tabs import PFNotebook import gui.builtinViews.emptyView diff --git a/gui/patternEditor.py b/gui/patternEditor.py index 4fe15077e..736815fb2 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -19,7 +19,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader # noinspection PyPackageRequirements from wx.lib.intctrl import IntCtrl from gui.utils.clipboard import toClipboard, fromClipboard diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index d51ee2515..13850567f 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class PreferenceDialog(wx.Dialog): diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index 658e0bc21..7cf012f08 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -19,7 +19,7 @@ import gui.display as d import gui.globalEvents as GE import gui.builtinMarketBrowser.pfSearchBox as SBox from gui.marketBrowser import SearchBox -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader pyfalog = Logger(__name__) diff --git a/gui/pyfatogglepanel.py b/gui/pyfatogglepanel.py index ae653e11b..75af0b214 100644 --- a/gui/pyfatogglepanel.py +++ b/gui/pyfatogglepanel.py @@ -23,7 +23,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class TogglePanel(wx.Panel): diff --git a/gui/pygauge.py b/gui/pygauge.py index f670c1427..6c08ea209 100644 --- a/gui/pygauge.py +++ b/gui/pygauge.py @@ -15,9 +15,9 @@ It uses the easeOutQuad equation from caurina.transitions.Tweener to do the anim import wx import copy -from gui.utils import colorUtils -import gui.utils.drawUtils as drawUtils -import gui.utils.animEffects as animEffects +from gui.utils import color +import gui.utils.draw as drawUtils +import gui.utils.anim_effects as animEffects import gui.utils.fonts as fonts from service.fit import Fit @@ -347,16 +347,16 @@ class PyGauge(wx.PyWindow): if transition != -1: colorS, colorE = self.transitionsColors[transition] - color = colorUtils.CalculateTransitionColor(colorS, colorE, xv) + color = color.CalculateTransitionColor(colorS, colorE, xv) else: 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 = color.BrightenColor(color, float(self.gradientEffect) / 100) + gMid = color.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 = color.DarkenColor(color, float(-self.gradientEffect) / 100) + gMid = color.DarkenColor(color, float(-self.gradientEffect / 2) / 100) gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) dc.DrawBitmap(gBmp, r.left, r.top) diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index 76ea7551a..34737b1af 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from service.targetResists import TargetResists -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.clipboard import toClipboard, fromClipboard from gui.builtinViews.entityEditor import EntityEditor, BaseValidator from logbook import Logger diff --git a/gui/utils/animUtils.py b/gui/utils/anim.py similarity index 98% rename from gui/utils/animUtils.py rename to gui/utils/anim.py index 4003e7760..947fa2abf 100644 --- a/gui/utils/animUtils.py +++ b/gui/utils/anim.py @@ -1,6 +1,6 @@ # noinspection PyPackageRequirements import wx -import gui.utils.colorUtils as colorUtils +import gui.utils.color as colorUtils class LoadAnimation(wx.Window): diff --git a/gui/utils/animEffects.py b/gui/utils/anim_effects.py similarity index 100% rename from gui/utils/animEffects.py rename to gui/utils/anim_effects.py diff --git a/gui/utils/colorUtils.py b/gui/utils/color.py similarity index 100% rename from gui/utils/colorUtils.py rename to gui/utils/color.py diff --git a/gui/utils/drawUtils.py b/gui/utils/draw.py similarity index 98% rename from gui/utils/drawUtils.py rename to gui/utils/draw.py index d7034ece7..51d0ba903 100644 --- a/gui/utils/drawUtils.py +++ b/gui/utils/draw.py @@ -1,6 +1,6 @@ # noinspection PyPackageRequirements import wx -import gui.utils.colorUtils as colorUtils +import gui.utils.color as colorUtils def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor=None, fillRatio=2): From ae0da59ed2d076ab8e9eb4be1939952b45245336 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 18:14:29 -0400 Subject: [PATCH 004/212] Clean up draw and color utils --- gui/utils/color.py | 72 +++++++++++++++++-------------------- gui/utils/draw.py | 88 +++++++++++++++++----------------------------- 2 files changed, 65 insertions(+), 95 deletions(-) diff --git a/gui/utils/color.py b/gui/utils/color.py index d2f10336b..4b44e77a4 100644 --- a/gui/utils/color.py +++ b/gui/utils/color.py @@ -1,13 +1,10 @@ # noinspection PyPackageRequirements import wx -import math -def BrightenColor(color, factor): - # Brightens a color (wx.Colour), factor = [0,1] - - r, g, b = color - a = color.Alpha() +def Brighten(color: wx.Colour, factor: [0, 1]): + """ Brightens a Color using a factor between 0 and 1""" + r, g, b, a = color factor = min(max(factor, 0), 1) @@ -18,64 +15,59 @@ def BrightenColor(color, factor): return wx.Colour(r, g, b, a) -def DarkenColor(color, factor): - # Darkens a color (wx.Colour), factor = [0, 1] - - bkR, bkG, bkB = color - - alpha = color.Alpha() +def Darken(color: wx.Colour, factor: [0, 1]): + """ Darkens a Color using a factor between 0 and 1""" + r, g, b, a = color factor = min(max(factor, 0), 1) factor = 1 - factor - r = float(bkR * factor) - g = float(bkG * factor) - b = float(bkB * factor) + r *= factor + g *= factor + b *= factor 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) + return wx.Colour(r, g, b, a) -def GetBrightnessO1(color): - # Calculates the brightness of a color, different options - - r, g, b = color - return 0.299 * r + 0.587 * g + 0.114 * b +def _getBrightness(color: wx.Colour): + """ + Calculates brightness of color + http://stackoverflow.com/a/596243/788054 + """ + r, g, b, a = 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) +def GetSuitable(color: wx.Colour, factor: [0, 1]): + """ + Calculates a suitable color based on original color (wx.Colour), its + brightness, and a factor (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) + brightness = _getBrightness(color) if brightness > 129: - return DarkenColor(color, factor) + return Darken(color, factor) else: - return BrightenColor(color, factor) + return Brighten(color, factor) -def CalculateTransitionColor(startColor, endColor, delta): +def CalculateTransition(s_color: wx.Colour, e_color: wx.Colour, delta: [0, 1]): """ - Calculates the color between a given start and end colors, delta = [0,1] - Colors are wx.Colour objects + Calculates the color between a given start and end color using a delta + value between 0 and 1 """ - sR, sG, sB = startColor - eR, eG, eB = endColor - - alphaS = startColor.Alpha() - alphaE = endColor.Alpha() + sR, sG, sB, sA = s_color + eR, eG, eB, eA = e_color 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, (sA + eA)/2) diff --git a/gui/utils/draw.py b/gui/utils/draw.py index 51d0ba903..70a4dcd5c 100644 --- a/gui/utils/draw.py +++ b/gui/utils/draw.py @@ -1,26 +1,27 @@ # noinspection PyPackageRequirements import wx -import gui.utils.color as colorUtils +from . import color -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 is None: - return DrawFilledBitmap(width, height, windowColor) + return DrawFilledBitmap(width,height, windowColor) - gStart = colorUtils.GetSuitableColor(windowColor, sFactor) + gStart = color.GetSuitable(windowColor, sFactor) if mFactor: - gMid = colorUtils.GetSuitableColor(windowColor, mFactor) + gMid = color.GetSuitable(windowColor, mFactor) else: - gMid = colorUtils.GetSuitableColor(windowColor, sFactor + (eFactor - sFactor) / 2) + gMid = color.GetSuitable(windowColor, sFactor + (eFactor - sFactor) / 2) - gEnd = colorUtils.GetSuitableColor(windowColor, eFactor) + gEnd = color.GetSuitable(windowColor, eFactor) return DrawGradientBar(width, height, gStart, gEnd, gMid, fillRatio) def DrawFilledBitmap(width, height, color): - canvas = wx.EmptyBitmap(width, height) + canvas = wx.Bitmap(width,height) mdc = wx.MemoryDC() mdc.SelectObject(canvas) @@ -32,25 +33,21 @@ def DrawFilledBitmap(width, height, color): return canvas - -# noinspection PyPropertyAccess 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) + canvas = wx.Bitmap(width,height) mdc = wx.MemoryDC() mdc.SelectObject(canvas) r = wx.Rect(0, 0, width, height) - r.height = height / fillRatio + r.SetHeight(height / fillRatio) if gMid is None: gMid = gStart 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.SetTop(r.GetHeight()) + r.SetHeight(height * (fillRatio - 1)/fillRatio + (1 if height % fillRatio != 0 else 0)) mdc.GradientFillLinear(r, gMid, gEnd, wx.SOUTH) @@ -59,50 +56,31 @@ def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4): return canvas -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] +def GetPartialText(dc, text , maxWidth, defEllipsis="..."): + ellipsis = defEllipsis 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) -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.SetBrush(wx.Brush(shownColor)) - dc.SetPen(wx.Pen(shownColor)) - dc.DrawRoundedRectangle(0, 0, w, h, r) - dc.SelectObject(wx.NullBitmap) - b.SetMaskColour(maskColor) - return b + while lenText > 0: + if w + base_w <= maxWidth: + break -def GetRoundShape(w, h, r): - return wx.RegionFromBitmap(GetRoundBitmap(w, h, r)) + 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 CreateDropShadowBitmap(bitmap, opacity): From 9712ec4fbfbbf1bd0e3f56b6a763488d408d14aa Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 20:38:00 -0400 Subject: [PATCH 005/212] Fix up some stuff, migrate to cleaned up chrome tabs --- config.py | 4 +- gui/bitmap_loader.py | 26 +- gui/chrome_tabs.py | 1277 +++++++++++++++++++++--------------------- 3 files changed, 660 insertions(+), 647 deletions(-) diff --git a/config.py b/config.py index c9bd69842..50b0344d5 100644 --- a/config.py +++ b/config.py @@ -45,14 +45,14 @@ def __createDirs(path): def getPyfaRoot(): - base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] + base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else __file__ root = os.path.dirname(os.path.realpath(os.path.abspath(base))) root = root return root def getDefaultSave(): - return str(os.path.expanduser(os.path.join("~", ".pyfa")), sys.getfilesystemencoding()) + return os.path.expanduser(os.path.join("~", ".pyfa")) def defPaths(customSavePath): diff --git a/gui/bitmap_loader.py b/gui/bitmap_loader.py index 0c3f90aad..9330f1fc3 100644 --- a/gui/bitmap_loader.py +++ b/gui/bitmap_loader.py @@ -20,6 +20,7 @@ import io import os.path import zipfile +from collections import OrderedDict # noinspection PyPackageRequirements import wx @@ -29,23 +30,18 @@ import config from logbook import Logger logging = Logger(__name__) -try: - from collections import OrderedDict -except ImportError: - from .utils.compat import OrderedDict - class BitmapLoader(object): try: archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') logging.info("Using zipped image files.") - except IOError: + except (IOError, TypeError): logging.info("Using local image files.") archive = None - cachedBitmaps = OrderedDict() - dontUseCachedBitmaps = False - max_bmps = 500 + cached_bitmaps = OrderedDict() + dont_use_cached_bitmaps = False + max_cached_bitmaps = 500 @classmethod def getStaticBitmap(cls, name, parent, location): @@ -55,25 +51,25 @@ class BitmapLoader(object): @classmethod def getBitmap(cls, name, location): - if cls.dontUseCachedBitmaps: + if cls.dont_use_cached_bitmaps: img = cls.getImage(name, location) if img is not None: return img.ConvertToBitmap() path = "%s%s" % (name, location) - if len(cls.cachedBitmaps) == cls.max_bmps: - cls.cachedBitmaps.popitem(False) + if len(cls.cached_bitmaps) == cls.max_cached_bitmaps: + cls.cached_bitmaps.popitem(False) - if path not in cls.cachedBitmaps: + if path not in cls.cached_bitmaps: img = cls.getImage(name, location) if img is not None: bmp = img.ConvertToBitmap() else: bmp = None - cls.cachedBitmaps[path] = bmp + cls.cached_bitmaps[path] = bmp else: - bmp = cls.cachedBitmaps[path] + bmp = cls.cached_bitmaps[path] return bmp diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index 9ee74a2ac..ce97f6c5a 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -1,34 +1,25 @@ -# ============================================================================= -# Copyright (C) 2010 Darriele +#=============================================================================== # -# This file is part of pyfa. +# ToDo: Bug - when selecting close on a tab, sometimes the tab to the right is +# selected, most likely due to determination of mouse position +# ToDo: Tab Selection seems overly complicated. OnLeftDown finds tab at +# position, and then call's CheckTabSelected which calls TabHitTest (when +# we are already aware it will return due to FindTabAtPos) +# ToDo: Perhaps a better way of finding tabs at position instead of looping +# through them and getting their regions. Perhaps some smart trickery with +# mouse pos x (all tabs have same width, so we divide x by width to find +# tab index?). This will also help with finding close buttons. +# ToDo: Fix page preview code (PFNotebookPagePreview) # -# 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 . -# ============================================================================= +#=============================================================================== -# noinspection PyPackageRequirements import wx -# noinspection PyPackageRequirements import wx.lib.newevent -import gui.utils.color as colorUtils -import gui.utils.draw as drawUtils -import gui.utils.fonts as fonts -from gui.bitmap_loader import BitmapLoader -from logbook import Logger -from service.fit import Fit -pyfalog = Logger(__name__) +from gui.bitmap_loader import BitmapLoader +from gui.utils import draw +from gui.utils import color as color_utils + _PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() _PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() @@ -37,8 +28,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(object): +class VetoAble(): def __init__(self): self.__vetoed = False @@ -49,7 +39,7 @@ class VetoAble(object): return self.__vetoed -class NotebookTabChangeEvent(object): +class NotebookTabChangeEvent(): def __init__(self, old, new): self.__old = old self.__new = new @@ -66,21 +56,21 @@ class NotebookTabChangeEvent(object): class PageChanging(_PageChanging, NotebookTabChangeEvent, VetoAble): def __init__(self, old, new): - NotebookTabChangeEvent.__init__(self, old, new) _PageChanging.__init__(self) + NotebookTabChangeEvent.__init__(self, old, new) VetoAble.__init__(self) class PageChanged(_PageChanged, NotebookTabChangeEvent): def __init__(self, old, new): - NotebookTabChangeEvent.__init__(self, old, new) _PageChanged.__init__(self) + NotebookTabChangeEvent.__init__(self, old, new) class PageClosing(_PageClosing, VetoAble): def __init__(self, i): - self.__index = i _PageClosing.__init__(self) + self.__index = i VetoAble.__init__(self) self.Selection = property(self.GetSelection) @@ -94,68 +84,69 @@ class PageAdding(_PageAdding, VetoAble): VetoAble.__init__(self) -class PFNotebook(wx.Panel): - def __init__(self, parent, canAdd=True): +class ChromeNotebook(wx.Panel): + + def __init__(self, parent, can_add=True): """ - Instance of Pyfa Notebook. Initializes general layout, includes methods - for setting current page, replacing pages, etc - - parent - wx parent element - canAdd - True if tabs be deleted and added, passed directly to - PFTabsContainer + Instance of Notebook. Initializes general layout, includes methods + for setting current page, replacing pages, any public function for the + notebook """ + super().__init__(parent, wx.ID_ANY, size=(-1, -1)) - wx.Panel.__init__(self, parent, wx.ID_ANY, size=(-1, -1)) + self._pages = [] + self._active_page = None - self.pages = [] - self.activePage = None + main_sizer = wx.BoxSizer(wx.VERTICAL) - mainSizer = wx.BoxSizer(wx.VERTICAL) + tabs_sizer = wx.BoxSizer(wx.VERTICAL) + self.tabs_container = _TabsContainer(self, can_add=can_add) + tabs_sizer.Add(self.tabs_container, 0, wx.EXPAND) - tabsSizer = wx.BoxSizer(wx.VERTICAL) - self.tabsContainer = PFTabsContainer(self, canAdd=canAdd) - tabsSizer.Add(self.tabsContainer, 0, wx.EXPAND) + if 'wxMSW' in wx.PlatformInfo: + style = wx.DOUBLE_BORDER + else: + style = wx.SIMPLE_BORDER - style = wx.DOUBLE_BORDER if 'wxMSW' in wx.PlatformInfo else wx.SIMPLE_BORDER + back_color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) - contentSizer = wx.BoxSizer(wx.VERTICAL) - self.pageContainer = wx.Panel(self, style=style) - self.pageContainer.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - contentSizer.Add(self.pageContainer, 1, wx.EXPAND, 5) + content_sizer = wx.BoxSizer(wx.VERTICAL) + self.page_container = wx.Panel(self, style=style) + self.page_container.SetBackgroundColour(back_color) + content_sizer.Add(self.page_container, 1, wx.EXPAND, 5) - mainSizer.Add(tabsSizer, 0, wx.EXPAND, 5) - mainSizer.Add(contentSizer, 1, wx.EXPAND | wx.BOTTOM, 2) + main_sizer.Add(tabs_sizer, 0, wx.EXPAND, 5) + main_sizer.Add(content_sizer, 1, wx.EXPAND | wx.BOTTOM, 2) - self.SetSizer(mainSizer) + self.SetSizer(main_sizer) self.Bind(wx.EVT_SIZE, self.OnSize) self.Layout() def GetPage(self, i): - return self.pages[i] + return self._pages[i] def SetPage(self, i, page): - if i >= len(self.pages) or i is None or page is None: + if i >= len(self._pages) or i is None or page is None: return - oldPage = self.pages[i] - self.pages[i] = page - if oldPage == self.activePage: - oldPage.Destroy() - self.activePage = page + old_page = self._pages[i] + self._pages[i] = page + if old_page == self._active_page: + old_page.Destroy() + self._active_page = page else: - oldPage.Destroy() + old_page.Destroy() - page.Reparent(self.pageContainer) + page.Reparent(self.page_container) - if self.activePage == page: + if self._active_page == page: self.ShowActive() - @staticmethod - def GetBorders(): + def GetBorders(self): """Gets border widths to better determine page size in ShowActive()""" - bx = wx.SystemSettings_GetMetric(wx.SYS_BORDER_X) - by = wx.SystemSettings_GetMetric(wx.SYS_BORDER_Y) + bx = wx.SystemSettings.GetMetric(wx.SYS_BORDER_X) + by = wx.SystemSettings.GetMetric(wx.SYS_BORDER_Y) if bx < 0: bx = 1 @@ -168,220 +159,194 @@ class PFNotebook(wx.Panel): self.SetPage(self.GetSelection(), page) def GetSelectedPage(self): - return self.activePage + return self._active_page def GetPageIndex(self, page): - return self.pages.index(page) if page in self.pages else None + return self._pages.index(page) if page in self._pages else None def GetSelection(self): - return self.GetPageIndex(self.activePage) + return self.GetPageIndex(self._active_page) def GetCurrentPage(self): - return self.activePage + return self._active_page def GetPageCount(self): - return len(self.pages) + return len(self._pages) def NextPage(self): """Used with keyboard shortcut for next page navigation""" - cpage = self.GetSelection() + current_page = self.GetSelection() - if cpage is None: + if current_page is None: return - if cpage < self.GetPageCount() - 1: - self.SetSelection(cpage + 1) - npage = cpage + 1 + if current_page < self.GetPageCount() - 1: + self.SetSelection(current_page + 1) + new_page = current_page + 1 else: self.SetSelection(0) - npage = 0 + new_page = 0 - wx.PostEvent(self, PageChanged(cpage, npage)) + wx.PostEvent(self, PageChanged(current_page, new_page)) def PrevPage(self): """Used with keyboard shortcut for previous page navigation""" - cpage = self.GetSelection() + current_page = self.GetSelection() - if cpage is None: + if current_page is None: return - if cpage > 0: - self.SetSelection(cpage - 1) - npage = cpage - 1 + if current_page > 0: + self.SetSelection(current_page - 1) + new_page = current_page - 1 else: self.SetSelection(self.GetPageCount() - 1) - npage = self.GetPageCount() - 1 + new_page = self.GetPageCount() - 1 - wx.PostEvent(self, PageChanged(cpage, npage)) + wx.PostEvent(self, PageChanged(current_page, new_page)) - def AddPage(self, tabWnd=None, tabTitle="Empty Tab", tabImage=None, showClose=True): - if self.activePage: - self.activePage.Hide() + def AddPage(self, win=None, title="Empty Tab", image: wx.Image=None, + closeable=True): + if self._active_page: + self._active_page.Hide() - if not tabWnd: - tabWnd = wx.Panel(self) + if not win: + win = wx.Panel(self) - tabWnd.Reparent(self.pageContainer) + win.Reparent(self.page_container) - self.pageContainer.Layout() + self.page_container.Layout() - self.pages.append(tabWnd) - self.tabsContainer.AddTab(tabTitle, tabImage, showClose) + self._pages.append(win) + self.tabs_container.AddTab(title, image, closeable) - self.activePage = tabWnd + self._active_page = win self.ShowActive(True) - def DisablePage(self, page, toggle): - idx = self.GetPageIndex(page) - - if toggle and page == self.activePage: - 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: - self.SetSelection(0) - - self.tabsContainer.DisableTab(idx, toggle) - def SetSelection(self, page): - oldsel = self.GetSelection() - if oldsel != page: - self.activePage.Hide() - self.activePage = self.pages[page] - self.tabsContainer.SetSelected(page) + old_selection = self.GetSelection() + if old_selection != page: + self._active_page.Hide() + self._active_page = self._pages[page] + self.tabs_container.SetSelected(page) self.ShowActive() - def DeletePage(self, n, internal=False): - """ - Deletes page. - - n -- index of page to be deleted - internal -- True if we're deleting the page from the PFTabsContainer - """ - page = self.pages[n] - self.pages.remove(page) + def DeletePage(self, n): + page = self._pages[n] + self._pages.remove(page) page.Destroy() - if not internal: - # If we're not deleting from the tab, delete the tab - # (deleting from the tab automatically deletes itself) - self.tabsContainer.DeleteTab(n, True) + self.tabs_container.DeleteTab(n) - sel = self.tabsContainer.GetSelected() - if sel is not None: - self.activePage = self.pages[sel] + selection = self.tabs_container.GetSelected() + if selection is not None: + self._active_page = self._pages[selection] self.ShowActive() - wx.PostEvent(self, PageChanged(-1, sel)) + wx.PostEvent(self, PageChanged(-1, selection)) else: - self.activePage = None + self._active_page = None - def SwitchPages(self, src, dest): - self.pages[src], self.pages[dest] = self.pages[dest], self.pages[src] + def SwitchPages(self, src, dst): + self._pages[src], self._pages[dst] = self._pages[dst], self._pages[src] - def ShowActive(self, resizeOnly=False): + def ShowActive(self, resize_only=False): """ Sets the size of the page and shows. The sizing logic adjusts for some minor sizing errors (scrollbars going beyond bounds) - resizeOnly -- if we are not interested in showing the page, only setting - the size + resize_only + if we are not interested in showing the page, only setting the size - @todo: is resizeOnly still needed? Was introduced with 8b8b97 in mid 2011 - to fix a resizing bug with blank pages, cannot reproduce 13Sept2014 + @todo: is resize_only still needed? Was introduced with 8b8b97 in mid + 2011 to fix a resizing bug with blank _pages, cannot reproduce + 13Sept2014 """ - ww, wh = self.pageContainer.GetSize() + ww, wh = self.page_container.GetSize() bx, by = self.GetBorders() ww -= bx * 4 wh -= by * 4 - self.activePage.SetSize((max(ww, -1), max(wh, -1))) - self.activePage.SetPosition((0, 0)) + self._active_page.SetSize((ww, wh)) + self._active_page.SetPosition((0, 0)) - if not resizeOnly: - self.activePage.Show() + if not resize_only: + self._active_page.Show() self.Layout() def IsActive(self, page): - return self.activePage == page + return self._active_page == page def SetPageTitle(self, i, text, refresh=True): - tab = self.tabsContainer.tabs[i] + tab = self.tabs_container.tabs[i] tab.text = text if refresh: - self.tabsContainer.AdjustTabsSize() + self.tabs_container.AdjustTabsSize() self.Refresh() def SetPageIcon(self, i, icon, refresh=True): - tab = self.tabsContainer.tabs[i] + tab = self.tabs_container.tabs[i] tab.tabImg = icon if refresh: - self.tabsContainer.AdjustTabsSize() + self.tabs_container.AdjustTabsSize() self.Refresh() def SetPageTextIcon(self, i, text=wx.EmptyString, icon=None): self.SetPageTitle(i, text, False) self.SetPageIcon(i, icon, False) - self.tabsContainer.AdjustTabsSize() + self.tabs_container.AdjustTabsSize() self.Refresh() def Refresh(self): - self.tabsContainer.Refresh() + self.tabs_container.Refresh() def OnSize(self, event): w, h = self.GetSize() - self.tabsContainer.SetSize((w, -1)) - self.tabsContainer.UpdateSize() - self.tabsContainer.Refresh() + self.tabs_container.SetSize((w, -1)) + self.tabs_container.UpdateSize() + self.tabs_container.Refresh() self.Layout() - if self.activePage: + if self._active_page: self.ShowActive() event.Skip() -class PFTabRenderer(object): - def __init__(self, size=(36, 24), text=wx.EmptyString, img=None, inclination=6, closeButton=True): - """ - Renders a new tab +class _TabRenderer: + def __init__(self, size=(36, 24), text=wx.EmptyString, img: wx.Image=None, + closeable=True): - text -- tab label - img -- wxImage of tab icon - inclination -- does not seem to affect class, maybe used to be a variable - for custom drawn tab inclinations before there were bitmaps? - closeButton -- True if tab can be closed - """ # tab left/right zones inclination - self.ctabLeft = BitmapLoader.getImage("ctableft", "gui") - self.ctabMiddle = BitmapLoader.getImage("ctabmiddle", "gui") - self.ctabRight = BitmapLoader.getImage("ctabright", "gui") - self.ctabClose = BitmapLoader.getImage("ctabclose", "gui") + self.ctab_left = BitmapLoader.getImage("ctableft", "gui") + self.ctab_middle = BitmapLoader.getImage("ctabmiddle", "gui") + self.ctab_right = BitmapLoader.getImage("ctabright", "gui") + self.ctab_close = BitmapLoader.getImage("ctabclose", "gui") - self.leftWidth = self.ctabLeft.GetWidth() - self.rightWidth = self.ctabRight.GetWidth() - self.middleWidth = self.ctabMiddle.GetWidth() - self.closeBtnWidth = self.ctabClose.GetWidth() + self.left_width = self.ctab_left.GetWidth() + self.right_width = self.ctab_right.GetWidth() + self.middle_width = self.ctab_middle.GetWidth() + self.close_btn_width = self.ctab_close.GetWidth() width, height = size - if width < self.leftWidth + self.rightWidth + self.middleWidth: - width = self.leftWidth + self.rightWidth + self.middleWidth - if height < self.ctabMiddle.GetHeight(): - height = self.ctabMiddle.GetHeight() - self.inclination = inclination + self.min_width = self.left_width + self.right_width + self.middle_width + self.min_height = self.ctab_middle.GetHeight() + + # set minimum width and height to what is allotted to images + width = max(width, self.min_width) + height = max(height, self.min_height) + self.text = text - self.disabled = False - self.tabSize = (width, height) - self.closeButton = closeButton + self.tab_size = (width, height) + self.closeable = closeable self.selected = False - self.closeBtnHovering = False - self.tabBitmap = None - self.tabBackBitmap = None - self.cbSize = 5 + self.close_btn_hovering = False + self.tab_bitmap = None + self.tab_back_bitmap = None self.padding = 4 - self.font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False) - self.tabImg = img + self.tab_img = img self.position = (0, 0) # Not used internally for rendering - helper for tab container self.InitTab() @@ -392,16 +357,15 @@ class PFTabRenderer(object): return self.position def GetSize(self): - return self.tabSize + return self.tab_size def SetSize(self, size): - w, h = size - if w < self.leftWidth + self.rightWidth + self.middleWidth: - w = self.leftWidth + self.rightWidth + self.middleWidth - if h < self.ctabMiddle.GetHeight(): - h = self.ctabMiddle.GetHeight() + width, height = size - self.tabSize = (w, h) + width = max(width, self.min_width) + height = max(height, self.min_height) + + self.tab_size = (width, height) self.InitTab() def SetSelected(self, sel=True): @@ -415,49 +379,48 @@ class PFTabRenderer(object): return self.selected def ShowCloseButtonHovering(self, hover=True): - if self.closeBtnHovering != hover: - self.closeBtnHovering = hover + if self.close_btn_hovering != hover: + self.close_btn_hovering = hover self._Render() def GetCloseButtonHoverStatus(self): - return self.closeBtnHovering + return self.close_btn_hovering def GetTabRegion(self): - nregion = self.CopyRegion(self.tabRegion) - nregion.SubtractRegion(self.closeBtnRegion) if self.closeButton else self.tabRegion - return nregion + new_region = self.CopyRegion(self.tab_region) + new_region.Subtract(self.close_region) if self.closeable else self.tab_region + return new_region def GetCloseButtonRegion(self): - return self.CopyRegion(self.closeBtnRegion) + return self.CopyRegion(self.close_region) def GetMinSize(self): - ebmp = wx.EmptyBitmap(1, 1) + ebmp = wx.Bitmap(1, 1) mdc = wx.MemoryDC() 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.left_width + self.right_width + textSizeX + self.close_btn_width/2 + 16 + self.padding*2 mdc.SelectObject(wx.NullBitmap) - return totalSize, self.tabHeight + return totalSize, self.tab_height def SetTabImage(self, img): - self.tabImg = img + self.tab_img = img - @staticmethod - def CopyRegion(region): + def CopyRegion(self, region): rect = region.GetBox() newRegion = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) - newRegion.IntersectRegion(region) + newRegion.Intersect(region) return newRegion def InitTab(self): - self.tabWidth, self.tabHeight = self.tabSize + self.tab_width, self.tab_height = self.tab_size - self.contentWidth = self.tabWidth - self.leftWidth - self.rightWidth - self.tabRegion = None - self.closeBtnRegion = None + self.content_width = self.tab_width - self.left_width - self.right_width + self.tab_region = None + self.close_region = None self.InitColors() self.InitBitmaps() @@ -475,132 +438,138 @@ class PFTabRenderer(object): on platform -- see InitColors(). """ if self.selected: - tr, tg, tb = self.selectedColor + tr, tg, tb, ta = self.selected_color else: - tr, tg, tb = self.inactiveColor + tr, tg, tb, ta = self.inactive_color - ctabLeft = self.ctabLeft.Copy() - ctabRight = self.ctabRight.Copy() - ctabMiddle = self.ctabMiddle.Copy() + ctab_left = self.ctab_left.Copy() + ctab_right = self.ctab_right.Copy() + ctab_middle = self.ctab_middle.Copy() - ctabLeft.Replace(0, 0, 0, tr, tg, tb) - ctabRight.Replace(0, 0, 0, tr, tg, tb) - ctabMiddle.Replace(0, 0, 0, tr, tg, tb) + ctab_left.Replace(0, 0, 0, tr, tg, tb) + ctab_right.Replace(0, 0, 0, tr, tg, tb) + ctab_middle.Replace(0, 0, 0, tr, tg, tb) - self.ctabLeftBmp = wx.BitmapFromImage(ctabLeft) - self.ctabRightBmp = wx.BitmapFromImage(ctabRight) - self.ctabMiddleBmp = wx.BitmapFromImage(ctabMiddle) - self.ctabCloseBmp = wx.BitmapFromImage(self.ctabClose) + self.ctab_left_bmp = wx.Bitmap(ctab_left) + self.ctab_right_bmp = wx.Bitmap(ctab_right) + self.ctab_middle_bmp = wx.Bitmap(ctab_middle) + self.ctab_close_bmp = wx.Bitmap(self.ctab_close) def ComposeTabBack(self): """ Creates the tab background bitmap based upon calculated dimension values and modified bitmaps via InitBitmaps() """ - bkbmp = wx.EmptyBitmap(self.tabWidth, self.tabHeight) + bk_bmp = wx.Bitmap(self.tab_width, self.tab_height) mdc = wx.MemoryDC() - mdc.SelectObject(bkbmp) - - # mdc.SetBackground(wx.Brush((0x12, 0x23, 0x32))) + mdc.SelectObject(bk_bmp) mdc.Clear() - mdc.DrawBitmap(self.ctabLeftBmp, 0, 0) # set the left bitmap + # draw the left bitmap + mdc.DrawBitmap(self.ctab_left_bmp, 0, 0) # convert middle bitmap and scale to tab width - 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 + cm = self.ctab_middle_bmp.ConvertToImage() + mimg = cm.Scale(self.content_width, self.ctab_middle.GetHeight(), + wx.IMAGE_QUALITY_NORMAL) + mbmp = wx.Bitmap(mimg) - # set right bitmap offset by left + middle - mdc.DrawBitmap(self.ctabRightBmp, self.contentWidth + self.leftWidth, 0) + # draw middle bitmap, offset by left + mdc.DrawBitmap(mbmp, self.left_width, 0) + + # draw right bitmap offset by left + middle + mdc.DrawBitmap(self.ctab_right_bmp, + self.content_width + self.left_width, 0) mdc.SelectObject(wx.NullBitmap) - # bkbmp.SetMaskColour((0x12, 0x23, 0x32)) + if self.tab_back_bitmap: + del self.tab_back_bitmap - if self.tabBackBitmap: - del self.tabBackBitmap - - self.tabBackBitmap = bkbmp + self.tab_back_bitmap = bk_bmp def InitTabRegions(self): """ Initializes regions for tab, which makes it easier to determine if - given coordinates are incluced in a region + given coordinates are included in a region """ - 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.tab_region = wx.Region(self.tab_back_bitmap) + self.close_region = wx.Region(self.ctab_close_bmp) + + x_offset = self.content_width \ + + self.left_width \ + - self.ctab_close_bmp.GetWidth() / 2 + y_offset = (self.tab_height - self.ctab_close_bmp.GetHeight())/2 + self.close_region.Offset(x_offset, y_offset) def InitColors(self): """Determines colors used for tab, based on system settings""" - self.tabColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) - self.inactiveColor = colorUtils.GetSuitableColor(self.tabColor, 0.25) - self.selectedColor = colorUtils.GetSuitableColor(self.tabColor, 0.10) + self.tab_color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE) + self.inactive_color = color_utils.GetSuitable(self.tab_color, 0.25) + self.selected_color = color_utils.GetSuitable(self.tab_color, 0.10) def Render(self): - return self.tabBitmap + return self.tab_bitmap def _Render(self): """Renders the tab, complete with the icon, text, and close button""" - if self.tabBitmap: - del self.tabBitmap + if self.tab_bitmap: + del self.tab_bitmap - height = self.tabHeight + height = self.tab_height - # rect = wx.Rect(0, 0, self.tabWidth, self.tabHeight) - - canvas = wx.EmptyBitmap(self.tabWidth, self.tabHeight, 24) + canvas = wx.Bitmap(self.tab_width, self.tab_height, 24) mdc = wx.MemoryDC() mdc.SelectObject(canvas) - # mdc.SetBackground(wx.Brush ((0x12,0x23,0x32))) mdc.Clear() - # r = copy.copy(rect) - # r.top = r.left = 0 - # r.height = height - mdc.DrawBitmap(self.tabBackBitmap, 0, 0, True) + mdc.DrawBitmap(self.tab_back_bitmap, 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 the tab icon + if self.tab_img: + bmp = wx.Bitmap(self.tab_img) + # @todo: is this conditional relevant anymore? + if self.content_width > 16: # 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.left_width + self.padding - bmp.GetWidth() / 2, + (height - bmp.GetHeight()) / 2) + text_start = self.left_width + self.padding + bmp.GetWidth() / 2 else: - textStart = self.leftWidth + text_start = self.left_width mdc.SetFont(self.font) - maxsize = self.tabWidth - textStart - self.rightWidth - self.padding * 4 - color = self.selectedColor if self.selected else self.inactiveColor + maxsize = self.tab_width \ + - text_start \ + - self.right_width \ + - self.padding * 4 + color = self.selected_color if self.selected else self.inactive_color - mdc.SetTextForeground(colorUtils.GetSuitableColor(color, 1)) + mdc.SetTextForeground(color_utils.GetSuitable(color, 1)) - text = drawUtils.GetPartialText(mdc, self.text, maxsize, "") + # draw text (with no ellipses) + text = draw.GetPartialText(mdc, self.text, maxsize, "") tx, ty = mdc.GetTextExtent(text) - mdc.DrawText(text, textStart + self.padding, height / 2 - ty / 2) + mdc.DrawText(text, text_start + self.padding, height / 2 - ty / 2) - if self.closeButton: - if self.closeBtnHovering: - cbmp = self.ctabCloseBmp + # draw close button + if self.closeable: + if self.close_btn_hovering: + cbmp = self.ctab_close_bmp else: - cimg = self.ctabCloseBmp.ConvertToImage() + cimg = self.ctab_close_bmp.ConvertToImage() cimg = cimg.AdjustChannels(0.7, 0.7, 0.7, 0.3) - cbmp = wx.BitmapFromImage(cimg) + cbmp = wx.Bitmap(cimg) mdc.DrawBitmap( - cbmp, - self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth() / 2, - (height - self.ctabCloseBmp.GetHeight()) / 2, - ) + cbmp, + self.content_width + self.left_width - cbmp.GetWidth()/2, + (height - cbmp.GetHeight())/2) mdc.SelectObject(wx.NullBitmap) @@ -610,25 +579,20 @@ class PFTabRenderer(object): if not img.HasAlpha(): img.InitAlpha() - bmp = wx.BitmapFromImage(img) - self.tabBitmap = bmp - - def __repr__(self): - return "PFTabRenderer(text={}, disabled={}) at {}".format( - self.text, self.disabled, hex(id(self)) - ) + bmp = wx.Bitmap(img) + self.tab_bitmap = bmp -class PFAddRenderer(object): +class _AddRenderer: def __init__(self): """Renders the add tab button""" - self.addImg = BitmapLoader.getImage("ctabadd", "gui") - self.width = self.addImg.GetWidth() - self.height = self.addImg.GetHeight() + self.add_img = BitmapLoader.getImage("ctabadd", "gui") + self.width = self.add_img.GetWidth() + self.height = self.add_img.GetHeight() self.region = None - self.tbmp = wx.BitmapFromImage(self.addImg) - self.addBitmap = None + self.tbmp = wx.Bitmap(self.add_img) + self.add_bitmap = None self.position = (0, 0) self.highlighted = False @@ -638,7 +602,7 @@ class PFAddRenderer(object): def GetPosition(self): return self.position - def SetPosition(self, pos): + def SetPosition(self,pos): self.position = pos def GetSize(self): @@ -655,17 +619,16 @@ class PFAddRenderer(object): self._Render() def CreateRegion(self): - region = wx.RegionFromBitmap(self.tbmp) + region = wx.Region(self.tbmp) return region - @staticmethod - def CopyRegion(region): + def CopyRegion(self, region): rect = region.GetBox() - newRegion = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) - newRegion.IntersectRegion(region) + new_region = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) + new_region.Intersect(region) - return newRegion + return new_region def GetRegion(self): return self.CopyRegion(self.region) @@ -678,67 +641,68 @@ class PFAddRenderer(object): return self.highlighted def Render(self): - return self.addBitmap + return self.add_bitmap def _Render(self): - if self.addBitmap: - del self.addBitmap + if self.add_bitmap: + del self.add_bitmap alpha = 1 if self.highlighted else 0.3 - img = self.addImg.AdjustChannels(1, 1, 1, alpha) - bbmp = wx.BitmapFromImage(img) - self.addBitmap = bbmp + img = self.add_img.AdjustChannels(1, 1, 1, alpha) + bmp = wx.Bitmap(img) + self.add_bitmap = bmp -class PFTabsContainer(wx.Panel): - def __init__(self, parent, pos=(0, 0), size=(100, 22), id=wx.ID_ANY, canAdd=True): +class _TabsContainer(wx.Panel): + def __init__(self, parent, pos=(50, 0), size=(100, 22), id=wx.ID_ANY, + can_add=True): """ Defines the tab container. Handles functions such as tab selection and - dragging, and defines minimum width of tabs (all tabs are of equal width, - which is determined via widest tab). Also handles the tab preview, if any. + dragging, and defines minimum width of tabs (all tabs are of equal + width, which is determined via widest tab). Also handles the tab + preview, if any. """ - wx.Panel.__init__(self, parent, id, pos, size) - if wx.VERSION >= (3, 0): - self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + super().__init__(parent, id, pos, size) self.tabs = [] - width, height = size - self.width = width - self.height = height - self.containerHeight = height - self.startDrag = False + self.width, self.height = size + self.container_height = self.height + self.start_drag = False self.dragging = False - self.sFit = Fit.getInstance() + # amount of overlap of tabs? self.inclination = 7 - if canAdd: + + if can_add: self.reserved = 48 else: self.reserved = self.inclination * 4 - self.dragTrail = 3 # pixel distance to drag before we actually start dragging + # pixel distance to drag before we actually start dragging + self.drag_trail = 5 + self.dragx = 0 self.dragy = 0 - self.draggedTab = None - self.dragTrigger = self.dragTrail + self.dragged_tab = None + self.drag_trigger = self.drag_trail - self.showAddButton = canAdd + self.show_add_button = can_add - self.tabContainerWidth = width - self.reserved - self.tabMinWidth = width - self.tabShadow = None + self.tab_container_width = self.width - self.reserved + self.tab_min_width = self.width + self.tab_shadow = _TabRenderer((self.tab_min_width, self.height + 1)) - self.addButton = PFAddRenderer() - self.addBitmap = self.addButton.Render() + self.add_button = _AddRenderer() + self.add_bitmap = self.add_button.Render() - self.previewTimer = None - self.previewTimerID = wx.ID_ANY - self.previewWnd = None - self.previewBmp = None - self.previewPos = None - self.previewTab = None + self.preview_timer = None + self.preview_timer_id = wx.ID_ANY + self.preview_wnd = None + self.preview_bmp = None + self.preview_pos = None + self.preview_tab = None self.Bind(wx.EVT_TIMER, self.OnTimer) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) @@ -747,11 +711,9 @@ class PFTabsContainer(wx.Panel): self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) - self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp) self.Bind(wx.EVT_MOTION, self.OnMotion) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged) - self.tabShadow = PFTabRenderer((self.tabMinWidth, self.height + 1), inclination=self.inclination) def OnSysColourChanged(self, event): for tab in self.tabs: @@ -763,47 +725,110 @@ class PFTabsContainer(wx.Panel): event.Skip() def UpdateSize(self): + """Update tab sizes based on new tab container size""" width, _ = self.GetSize() if width != self.width: self.width = width - self.tabContainerWidth = self.width - self.reserved + self.tab_container_width = self.width - self.reserved self.AdjustTabsSize() def OnLeftDown(self, event): - """Determines what happens when user left clicks (down)""" + """ Select tab on mouse down and start dragging logic """ mposx, mposy = event.GetPosition() - if not self.startDrag: + if not self.start_drag: tab = self.FindTabAtPos(mposx, mposy) if tab: self.CheckTabSelected(tab, mposx, mposy) - if self.showAddButton: + if self.show_add_button: # If we can add tabs, we can drag them. Set flag - self.startDrag = True + self.start_drag = True tx, ty = tab.GetPosition() self.dragx = mposx - tx - self.dragy = self.containerHeight - self.height + self.dragy = self.container_height - self.height self.Refresh() - self.draggedTab = tab + self.dragged_tab = tab + + def OnMotion(self, event): + """ + Determines what happens when the mouse moves. This handles primarily + dragging (region tab can be dragged) as well as checking if we are over + an actionable button. + """ + mposx, mposy = event.GetPosition() + + if self.start_drag: + if not self.dragging: + if self.drag_trigger < 0: + self.dragging = True + self.drag_trigger = self.drag_trail + self.CaptureMouse() + else: + self.drag_trigger -= 1 + if self.dragging: + # we wish to keep tab within tab container boundaries. To do + # this, we must detect when mouse moves outside of boundaries. + # Set hard limits to 0 and the size of tab container. + dtx = mposx - self.dragx + w, h = self.dragged_tab.GetSize() + + dtx = max(dtx, 0) + + if dtx + w > self.tab_container_width + self.inclination * 2: + dtx = self.tab_container_width - w + self.inclination * 2 + + self.dragged_tab.SetPosition((dtx, self.dragy)) + + # we must modify the surrounding tabs + index = self.GetTabIndex(self.dragged_tab) + + left_tab = self.GetTabAtLeft(index) + right_tab = self.GetTabAtRight(index) + + if left_tab: + lw, lh = left_tab.GetSize() + lx, ly = left_tab.GetPosition() + + if lx + lw / 2 - self.inclination * 2 > dtx: + self.SwitchTabs(index - 1, index, self.dragged_tab) + + if right_tab: + rw, rh = right_tab.GetSize() + rx, ry = right_tab.GetPosition() + + if rx + rw / 2 + self.inclination * 2 < dtx + w: + self.SwitchTabs(index + 1, index, self.dragged_tab) + + self.UpdateTabsPosition(self.dragged_tab) + self.Refresh() + return + + # If we aren't dragging, check for actionable buttons under mouse + self.CheckCloseHighlighted(mposx, mposy) + self.CheckAddHighlighted(mposx, mposy) + self.CheckTabPreview(mposx, mposy) + + event.Skip() def OnLeftUp(self, event): """Determines what happens when user left clicks (up)""" mposx, mposy = event.GetPosition() - if self.startDrag and self.dragging: + if self.start_drag and self.dragging: self.dragging = False - self.startDrag = False - self.draggedTab = None - self.dragTrigger = self.dragTrail + self.start_drag = False + self.dragged_tab = None + self.drag_trigger = self.drag_trail self.UpdateTabsPosition() self.Refresh() + if self.HasCapture(): self.ReleaseMouse() return - if self.startDrag: - self.startDrag = False - self.dragTrigger = self.dragTrail + if self.start_drag: + self.start_drag = False + self.drag_trigger = self.drag_trail # Checks if we selected the add button and, if True, returns if self.CheckAddButton(mposx, mposy): @@ -814,10 +839,10 @@ class PFTabsContainer(wx.Panel): return # Gets selected tab (was set when user down clicked) - selTab = self.GetSelectedTab() + sel_tab = self.GetSelectedTab() # Check if we selected close button for selected tab - if self.CheckTabClose(selTab, mposx, mposy): + if self.CheckTabClose(sel_tab, mposx, mposy): return # Check if we selected close button for all others @@ -825,28 +850,6 @@ class PFTabsContainer(wx.Panel): if self.CheckTabClose(tab, mposx, mposy): return - def OnMiddleUp(self, event): - mposx, mposy = event.GetPosition() - - tab = self.FindTabAtPos(mposx, mposy) - - if tab is None or not tab.closeButton: # if not able to close, return False - return False - - index = self.tabs.index(tab) - ev = PageClosing(index) - wx.PostEvent(self.Parent, ev) - if ev.isVetoed(): - return False - - index = self.GetTabIndex(tab) - self.DeleteTab(index) - wx.PostEvent(self.Parent, PageClosed(index=index)) - sel = self.GetSelected() - - if sel is not None: - wx.PostEvent(self.Parent, PageChanged(-1, sel)) - def GetSelectedTab(self): for tab in self.tabs: if tab.GetSelected(): @@ -860,8 +863,9 @@ class PFTabsContainer(wx.Panel): return None def SetSelected(self, tabIndex): - oldSelTab = self.GetSelectedTab() - oldSelTab.SetSelected(False) + """Set tab as selected given its index""" + old_sel_tab = self.GetSelectedTab() + old_sel_tab.SetSelected(False) self.tabs[tabIndex].SetSelected(True) self.Refresh() @@ -871,53 +875,56 @@ class PFTabsContainer(wx.Panel): return true. Otherwise, perform TabHitTest and set tab at position to selected """ - oldSelTab = self.GetSelectedTab() - if oldSelTab == tab: + old_sel_tab = self.GetSelectedTab() + if old_sel_tab == tab: return True if self.TabHitTest(tab, x, y): - if tab.disabled: - return tab.SetSelected(True) - oldSelTab.SetSelected(False) + old_sel_tab.SetSelected(False) - ev = PageChanging(self.tabs.index(oldSelTab), self.tabs.index(tab)) + ev = PageChanging(self.tabs.index(old_sel_tab), self.tabs.index(tab)) wx.PostEvent(self.Parent, ev) if ev.isVetoed(): return False self.Refresh() - selTab = self.tabs.index(tab) - self.Parent.SetSelection(selTab) + sel_tab = self.tabs.index(tab) + self.Parent.SetSelection(sel_tab) - wx.PostEvent(self.Parent, PageChanged(self.tabs.index(oldSelTab), self.tabs.index(tab))) + wx.PostEvent(self.Parent, PageChanged(self.tabs.index(old_sel_tab), + self.tabs.index(tab))) return True return False def CheckTabClose(self, tab, x, y): - """Determines if close button was selected for the given tab.""" - if not tab.closeButton: # if not able to close, return False + """ + Determines if close button was selected for the given tab by comparing + x and y position with known close button region + """ + if not tab.closeable: # if not able to close, return False return False - closeBtnReg = tab.GetCloseButtonRegion() - tabPosX, tabPosY = tab.GetPosition() - closeBtnReg.Offset(tabPosX, tabPosY) + region = tab.GetCloseButtonRegion() + posx, posy = tab.GetPosition() + region.Offset(posx, posy) - if closeBtnReg.Contains(x, y): + if region.Contains(x, y): index = self.tabs.index(tab) ev = PageClosing(index) wx.PostEvent(self.Parent, ev) + if ev.isVetoed(): return False index = self.GetTabIndex(tab) - self.DeleteTab(index) + self.Parent.DeletePage(index) wx.PostEvent(self.Parent, PageClosed(index=index)) - sel = self.GetSelected() + sel = self.GetSelected() if sel is not None: wx.PostEvent(self.Parent, PageChanged(-1, sel)) @@ -925,15 +932,18 @@ class PFTabsContainer(wx.Panel): return False def CheckAddButton(self, x, y): - """Determines if add button was selected.""" - if not self.showAddButton: # if not able to add, return False + """ + Determines if add button was selected by comparing x and y position with + add button region + """ + if not self.show_add_button: # if not able to add, return False return - reg = self.addButton.GetRegion() - ax, ay = self.addButton.GetPosition() - reg.Offset(ax, ay) + region = self.add_button.GetRegion() + ax, ay = self.add_button.GetPosition() + region.Offset(ax, ay) - if reg.Contains(x, y): + if region.Contains(x, y): ev = PageAdding() wx.PostEvent(self.Parent, ev) if ev.isVetoed(): @@ -943,20 +953,19 @@ class PFTabsContainer(wx.Panel): wx.PostEvent(self.Parent, PageAdded()) return True - def CheckCloseButtons(self, x, y): + def CheckCloseHighlighted(self, x, y): """ Checks if mouse pos at x, y is over a close button. If so, set the close hovering flag for that tab """ dirty = False - # @todo: maybe change to for...else - for tab in self.tabs: - closeBtnReg = tab.GetCloseButtonRegion() - tabPos = tab.GetPosition() - tabPosX, tabPosY = tabPos - closeBtnReg.Offset(tabPosX, tabPosY) - if closeBtnReg.Contains(x, y): + for tab in self.tabs: + region = tab.GetCloseButtonRegion() + posx, posy = tab.GetPosition() + region.Offset(posx, posy) + + if region.Contains(x, y): if not tab.GetCloseButtonHoverStatus(): tab.ShowCloseButtonHovering(True) dirty = True @@ -964,24 +973,28 @@ class PFTabsContainer(wx.Panel): if tab.GetCloseButtonHoverStatus(): tab.ShowCloseButtonHovering(False) dirty = True - if dirty: - self.Refresh() + if dirty: + self.Refresh() + break def FindTabAtPos(self, x, y): if self.GetTabsCount() == 0: return None - selTab = self.GetSelectedTab() - if self.TabHitTest(selTab, x, y): - return selTab + # test current tab first + sel_tab = self.GetSelectedTab() + if self.TabHitTest(sel_tab, x, y): + return sel_tab + # test all other tabs next for tab in self.tabs: if self.TabHitTest(tab, x, y): return tab + return None - @staticmethod - def TabHitTest(tab, x, y): + def TabHitTest(self, tab, x, y): + """ Test if x and y are contained within a tabs region """ tabRegion = tab.GetTabRegion() tabPos = tab.GetPosition() tabPosX, tabPosY = tabPos @@ -992,96 +1005,39 @@ class PFTabsContainer(wx.Panel): return False - def GetTabAtLeft(self, tabIndex): - return self.tabs[tabIndex - 1] if tabIndex > 0 else None + def GetTabAtLeft(self, tab_index): + return self.tabs[tab_index - 1] if tab_index > 0 else None - def GetTabAtRight(self, tabIndex): - return self.tabs[tabIndex + 1] if tabIndex < self.GetTabsCount() - 1 else None + def GetTabAtRight(self, tab_index): + if tab_index < self.GetTabsCount() - 1: + return self.tabs[tab_index + 1] + else: + return None - def SwitchTabs(self, src, dest, draggedTab=None): - self.tabs[src], self.tabs[dest] = self.tabs[dest], self.tabs[src] - self.UpdateTabsPosition(draggedTab) - self.Parent.SwitchPages(src, dest) + def SwitchTabs(self, src, dst, dragged_tab=None): + self.tabs[src], self.tabs[dst] = self.tabs[dst], self.tabs[src] + self.UpdateTabsPosition(dragged_tab) + self.Parent.SwitchPages(src, dst) self.Refresh() def GetTabIndex(self, tab): return self.tabs.index(tab) - def OnMotion(self, event): - """ - Determines what happens when the mouse moves. This handles primarily - dragging (region tab can be dragged) as well as checking if we are over - an actionable button. - """ - mposx, mposy = event.GetPosition() - - if self.startDrag: - if not self.dragging: - if self.dragTrigger < 0: - self.dragging = True - self.dragTrigger = self.dragTrail - self.CaptureMouse() - else: - self.dragTrigger -= 1 - if self.dragging: - dtx = mposx - self.dragx - w, h = self.draggedTab.GetSize() - - if dtx < 0: - dtx = 0 - if dtx + w > self.tabContainerWidth + self.inclination * 2: - dtx = self.tabContainerWidth - w + self.inclination * 2 - self.draggedTab.SetPosition((dtx, self.dragy)) - - index = self.GetTabIndex(self.draggedTab) - - leftTab = self.GetTabAtLeft(index) - rightTab = self.GetTabAtRight(index) - - if leftTab: - lw, lh = leftTab.GetSize() - lx, ly = leftTab.GetPosition() - - if lx + lw / 2 - self.inclination * 2 > dtx: - self.SwitchTabs(index - 1, index, self.draggedTab) - return - - if rightTab: - rw, rh = rightTab.GetSize() - rx, ry = rightTab.GetPosition() - - if rx + rw / 2 + self.inclination * 2 < dtx + w: - self.SwitchTabs(index + 1, index, self.draggedTab) - return - self.UpdateTabsPosition(self.draggedTab) - self.Refresh() - return - return - - self.CheckCloseButtons(mposx, mposy) - self.CheckAddHighlighted(mposx, mposy) - self.CheckTabPreview(mposx, mposy) - - event.Skip() - def CheckTabPreview(self, mposx, mposy): """ Checks to see if we have a tab preview and sets up the timer for it to display """ - if not self.sFit.serviceFittingOptions["showTooltip"] or False: - return - - if self.previewTimer: - if self.previewTimer.IsRunning(): - if self.previewWnd: - self.previewTimer.Stop() + if self.preview_timer: + if self.preview_timer.IsRunning(): + if self.preview_wnd: + self.preview_timer.Stop() return - if self.previewWnd: - self.previewWnd.Show(False) - del self.previewWnd - self.previewWnd = None + if self.preview_wnd: + self.preview_wnd.Show(False) + del self.preview_wnd + self.preview_wnd = None for tab in self.tabs: if not tab.GetSelected(): @@ -1089,35 +1045,35 @@ class PFTabsContainer(wx.Panel): try: page = self.Parent.GetPage(self.GetTabIndex(tab)) if hasattr(page, "Snapshot"): - if not self.previewTimer: - self.previewTimer = wx.Timer(self, self.previewTimerID) + if not self.preview_timer: + self.preview_timer = wx.Timer( + self, self.preview_timer_id) - self.previewTab = tab - self.previewTimer.Start(500, True) + self.preview_tab = tab + self.preview_timer.Start(500, True) break - except Exception as e: - pyfalog.critical("Exception caught in CheckTabPreview.") - pyfalog.critical(e) + except: + pass def CheckAddHighlighted(self, x, y): """ Checks to see if x, y are in add button region, and sets the highlight flag """ - if not self.showAddButton: + if not self.show_add_button: return - reg = self.addButton.GetRegion() - ax, ay = self.addButton.GetPosition() - reg.Offset(ax, ay) + region = self.add_button.GetRegion() + ax, ay = self.add_button.GetPosition() + region.Offset(ax, ay) - if reg.Contains(x, y): - if not self.addButton.IsHighlighted(): - self.addButton.Highlight(True) + if region.Contains(x, y): + if not self.add_button.IsHighlighted(): + self.add_button.Highlight(True) self.Refresh() else: - if self.addButton.IsHighlighted(): - self.addButton.Highlight(False) + if self.add_button.IsHighlighted(): + self.add_button.Highlight(False) self.Refresh() def OnPaint(self, event): @@ -1126,16 +1082,14 @@ class PFTabsContainer(wx.Panel): else: mdc = wx.BufferedPaintDC(self) - if 'wxMac' in wx.PlatformInfo and wx.VERSION < (3, 0): + if 'wxMac' in wx.PlatformInfo: color = wx.Colour(0, 0, 0) brush = wx.Brush(color) - - # noinspection PyPackageRequirements,PyUnresolvedReferences,PyUnresolvedReferences,PyUnresolvedReferences + # @todo: what needs to be changed with wxPheonix? from Carbon.Appearance import kThemeBrushDialogBackgroundActive - # noinspection PyUnresolvedReferences brush.MacSetTheme(kThemeBrushDialogBackgroundActive) else: - color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) + color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE) brush = wx.Brush(color) if "wxGTK" not in wx.PlatformInfo: @@ -1144,40 +1098,38 @@ class PFTabsContainer(wx.Panel): selected = None - tabsWidth = 0 - - for tab in self.tabs: - tabsWidth += tab.tabWidth - self.inclination * 2 - - if self.showAddButton: - ax, ay = self.addButton.GetPosition() - mdc.DrawBitmap(self.addButton.Render(), ax, ay, True) + if self.show_add_button: + ax, ay = self.add_button.GetPosition() + mdc.DrawBitmap(self.add_button.Render(), ax, ay, True) for i in range(len(self.tabs) - 1, -1, -1): tab = self.tabs[i] posx, posy = tab.GetPosition() if not tab.IsSelected(): - mdc.DrawBitmap(self.efxBmp, posx, posy, True) + # drop shadow first + mdc.DrawBitmap(self.fx_bmp, posx, posy, True) bmp = tab.Render() img = bmp.ConvertToImage() img = img.AdjustChannels(1, 1, 1, 0.85) - bmp = wx.BitmapFromImage(img) + bmp = wx.Bitmap(img) mdc.DrawBitmap(bmp, posx, posy, True) - else: selected = tab + # we draw selected tab separately (instead of in else) so as to not hide + # the front bit behind the preceding tab if selected: posx, posy = selected.GetPosition() - mdc.DrawBitmap(self.efxBmp, posx, posy, True) + # drop shadow first + mdc.DrawBitmap(self.fx_bmp, posx, posy, True) bmp = selected.Render() if self.dragging: img = bmp.ConvertToImage() img = img.AdjustChannels(1.2, 1.2, 1.2, 0.7) - bmp = wx.BitmapFromImage(img) + bmp = wx.Bitmap(img) mdc.DrawBitmap(bmp, posx, posy, True) @@ -1185,26 +1137,25 @@ class PFTabsContainer(wx.Panel): pass def UpdateTabFX(self): - w, h = self.tabShadow.GetSize() - if w != self.tabMinWidth: - self.tabShadow.SetSize((self.tabMinWidth, self.height + 1)) - fxBmp = self.tabShadow.Render() + """ Updates tab drop shadow bitmap """ + self.tab_shadow.SetSize((self.tab_min_width, self.height + 1)) + fx_bmp = self.tab_shadow.Render() - simg = fxBmp.ConvertToImage() - if not simg.HasAlpha(): - simg.InitAlpha() - simg = simg.Blur(2) - simg = simg.AdjustChannels(0.3, 0.3, 0.3, 0.35) + img = fx_bmp.ConvertToImage() + if not img.HasAlpha(): + img.InitAlpha() + img = img.Blur(2) + img = img.AdjustChannels(0.3, 0.3, 0.3, 0.35) - self.efxBmp = wx.BitmapFromImage(simg) + self.fx_bmp = wx.Bitmap(img) - def AddTab(self, title=wx.EmptyString, img=None, showClose=False): + def AddTab(self, title=wx.EmptyString, img=None, closeable=False): self.ClearTabsSelected() - tabRenderer = PFTabRenderer((120, self.height), title, img, self.inclination, closeButton=showClose) - tabRenderer.SetSelected(True) + tab_renderer = _TabRenderer((200, self.height), title, img, closeable) + tab_renderer.SetSelected(True) - self.tabs.append(tabRenderer) + self.tabs.append(tab_renderer) self.AdjustTabsSize() self.Refresh() @@ -1212,30 +1163,21 @@ class PFTabsContainer(wx.Panel): for tab in self.tabs: tab.SetSelected(False) - def DisableTab(self, tab, disabled=True): - tabRenderer = self.tabs[tab] - tabRenderer.disabled = disabled + def DeleteTab(self, tab): + tab_renderer = self.tabs[tab] + was_selected = tab_renderer.GetSelected() + self.tabs.remove(tab_renderer) - self.AdjustTabsSize() - self.Refresh() + if tab_renderer: + del tab_renderer - def DeleteTab(self, tab, external=False): - tabRenderer = self.tabs[tab] - wasSelected = tabRenderer.GetSelected() - self.tabs.remove(tabRenderer) - - if tabRenderer: - del tabRenderer - - if wasSelected and self.GetTabsCount() > 0: + # determine our new selection + if was_selected and self.GetTabsCount() > 0: if tab > self.GetTabsCount() - 1: self.tabs[self.GetTabsCount() - 1].SetSelected(True) else: self.tabs[tab].SetSelected(True) - if not external: - self.Parent.DeletePage(tab, True) - self.AdjustTabsSize() self.Refresh() @@ -1243,104 +1185,101 @@ class PFTabsContainer(wx.Panel): return len(self.tabs) def AdjustTabsSize(self): - tabMinWidth = 9000000 # Really, it should be over 9000 - tabMaxWidth = 0 - for tab in self.tabs: - mw, mh = tab.GetMinSize() - if tabMinWidth > mw: - tabMinWidth = mw - if tabMaxWidth < mw: - tabMaxWidth = mw - if tabMaxWidth < 100: - tabMaxWidth = 100 - if self.GetTabsCount() > 0: - if (self.GetTabsCount()) * (tabMaxWidth - self.inclination * 2) > self.tabContainerWidth: - self.tabMinWidth = float(self.tabContainerWidth) / float(self.GetTabsCount()) + self.inclination * 2 - else: - self.tabMinWidth = tabMaxWidth + """ + Adjust tab sizes to ensure that they are all consistent and can fit into + the tab container. + """ - if self.tabMinWidth < 1: - self.tabMinWidth = 1 + # first we loop through our tabs and calculate the the largest tab. This + # is the size that we will base our calculations off + + max_width = 100 # Tab should be at least 100 for tab in self.tabs: - tab.GetSize() - tab.SetSize((self.tabMinWidth, self.height)) + mw, _ = tab.GetMinSize() # Tab min size includes tab contents + max_width = max(mw, max_width) + + # Divide tab container by number of tabs and add inclination. This will + # return the ideal max size for the containers size + if self.GetTabsCount() > 0: + dx = self.tab_container_width / self.GetTabsCount() + self.inclination * 2 + self.tab_min_width = min(dx, max_width) + + # Apply new size to all tabs + for tab in self.tabs: + tab.SetSize((self.tab_min_width, self.height)) if self.GetTabsCount() > 0: + # update drop shadow based on new sizes self.UpdateTabFX() self.UpdateTabsPosition() - def UpdateTabsPosition(self, skipTab=None): + def UpdateTabsPosition(self, skip_tab=None): tabsWidth = 0 for tab in self.tabs: - tabsWidth += tab.tabWidth - self.inclination * 2 + tabsWidth += tab.tab_width - self.inclination * 2 pos = tabsWidth selected = None - selpos = None for i in range(len(self.tabs) - 1, -1, -1): tab = self.tabs[i] - width = tab.tabWidth - self.inclination * 2 + width = tab.tab_width - self.inclination*2 pos -= width if not tab.IsSelected(): - tab.SetPosition((pos, self.containerHeight - self.height)) + tab.SetPosition((pos, self.container_height - self.height)) else: selected = tab 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 - ) - ) + + if selected is not skip_tab: + selected.SetPosition((selpos, self.container_height - self.height)) + + self.add_button.SetPosition(( + round(tabsWidth) + + self.inclination*2, + self.container_height + - self.height / 2 + - self.add_button.GetHeight()/3)) def OnLeaveWindow(self, event): - - if self.startDrag and not self.dragging: + if self.start_drag and not self.dragging: self.dragging = False - self.startDrag = False - self.draggedTab = None - self.dragTrigger = self.dragTrail + self.start_drag = False + self.dragged_tab = None + self.drag_trigger = self.drag_trail if self.HasCapture(): self.ReleaseMouse() - if self.previewWnd: - self.previewWnd.Show(False) - del self.previewWnd - self.previewWnd = None + if self.preview_wnd: + self.preview_wnd.Show(False) + del self.preview_wnd + self.preview_wnd = None event.Skip() def OnTimer(self, event): mposx, mposy = wx.GetMousePosition() cposx, cposy = self.ScreenToClient((mposx, mposy)) - if self.FindTabAtPos(cposx, cposy) == self.previewTab: - if not self.previewTab.GetSelected(): - page = self.Parent.GetPage(self.GetTabIndex(self.previewTab)) + + if self.FindTabAtPos(cposx, cposy) == self.preview_tab: + if not self.preview_tab.GetSelected(): + page = self.Parent.GetPage(self.GetTabIndex(self.preview_tab)) if page.Snapshot(): - self.previewWnd = PFNotebookPagePreview( - self, - (mposx + 3, mposy + 3), - page.Snapshot(), - self.previewTab.text - ) - self.previewWnd.Show() + self.preview_wnd = PFNotebookPagePreview( + self, + (mposx+3, mposy+3), + page.Snapshot(), + self.preview_tab.text) + self.preview_wnd.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 - ) + super().__init__(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 @@ -1356,7 +1295,7 @@ class PFNotebookPagePreview(wx.Frame): self.padding = 15 self.transp = 0 - hfont = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + hfont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False) self.SetFont(hfont) tx, ty = self.GetTextExtent(self.title) @@ -1367,13 +1306,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 @@ -1382,7 +1321,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) @@ -1406,29 +1345,30 @@ class PFNotebookPagePreview(wx.Frame): self.direction = -1 self.timer.Start(10) - def OnWindowEraseBk(self, event): + + def OnWindowEraseBk(self,event): pass def OnWindowPaint(self, event): rect = self.GetRect() - canvas = wx.EmptyBitmap(rect.width, rect.height) + canvas = wx.Bitmap(rect.width, rect.height) mdc = wx.BufferedPaintDC(self) mdc.SelectObject(canvas) - color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) mdc.SetBackground(wx.Brush(color)) mdc.Clear() - font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + font = wx.Font(10, 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.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.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) @@ -1436,3 +1376,80 @@ class PFNotebookPagePreview(wx.Frame): mdc.SetBrush(wx.TRANSPARENT_BRUSH) mdc.DrawRectangle(0, 16, rect.width, rect.height - 16) + + +if __name__ == "__main__": + + import os + os.chdir('..') + import config + config.defPaths(None) + + class Frame(wx.Frame): + def __init__(self, title): + super().__init__(None, title=title, size=(1000, 500)) + + if 'wxMSW' in wx.PlatformInfo: + color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) + self.SetBackgroundColour(color) + + main_sizer = wx.BoxSizer(wx.HORIZONTAL) + splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) + main_sizer.Add(splitter, 1, wx.EXPAND | wx.ALL, 2) + + # Main test notebook + self.notebook = ChromeNotebook(splitter) + + # Tests can_add, has dummy tabs + notebook2 = ChromeNotebook(splitter, can_add=False) + + self.statusbar = self.CreateStatusBar() + + panel = wx.Panel(self) + box = wx.BoxSizer(wx.VERTICAL) + + head = wx.StaticText(panel, -1, "Chome Tabs Test") + head.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD)) + box.Add(head, 0, wx.ALL, 10) + + self.tctrl = wx.TextCtrl(panel, wx.ID_ANY, "Tab Name") + + self.close_check = wx.CheckBox(panel, label="Closable?") + self.close_check.SetValue(True) + + self.icon_check = wx.CheckBox(panel, label="Icon?") + self.icon_check.SetValue(True) + + button = wx.Button(panel, wx.ID_ANY, "Create") + button.Bind(wx.EVT_BUTTON, self.OnCreate) + + box.Add(self.tctrl, 0, wx.ALL, 5) + box.Add(self.close_check, 0, wx.ALL, 5) + box.Add(self.icon_check, 0, wx.ALL, 5) + box.Add(button, 0, wx.ALL, 10) + + self.notebook.AddPage(panel, "Tab1", closeable=False) + + # Add dummy pages + notebook2.AddPage() + notebook2.AddPage() + + splitter.SplitVertically(self.notebook, notebook2) + + panel.SetSizer(box) + panel.Layout() + self.SetSizer(main_sizer) + + def OnCreate(self, event): + tab_name = self.tctrl.GetValue() + tab_icon = BitmapLoader.getImage("ship_small", "gui") + self.notebook.AddPage( + title=tab_name, + image=tab_icon if self.icon_check.GetValue() else None, + closeable=self.close_check.GetValue()) + + app = wx.App(redirect=False) # Error messages go to popup window + top = Frame("Test Chrome Tabs") + top.Show() + app.MainLoop() + From 2acb3e759e7fc927a3241bbcd289edccf22819ff Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 20:42:03 -0400 Subject: [PATCH 006/212] Touch up toggle panel --- gui/additionsPane.py | 2 +- gui/chrome_tabs.py | 2 + gui/pyfatogglepanel.py | 199 ----------------------------------------- gui/statsPane.py | 2 +- gui/toggle_panel.py | 187 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 191 insertions(+), 201 deletions(-) delete mode 100644 gui/pyfatogglepanel.py create mode 100644 gui/toggle_panel.py diff --git a/gui/additionsPane.py b/gui/additionsPane.py index 00a126a30..90edcc68b 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -30,7 +30,7 @@ from gui.builtinAdditionPanes.implantView import ImplantView from gui.builtinAdditionPanes.notesView import NotesView from gui.builtinAdditionPanes.projectedView import ProjectedView from gui.chrome_tabs import PFNotebook -from gui.pyfatogglepanel import TogglePanel +from gui.toggle_panel import TogglePanel class AdditionsPane(TogglePanel): diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index ce97f6c5a..4e454d014 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -1380,6 +1380,8 @@ class PFNotebookPagePreview(wx.Frame): if __name__ == "__main__": + # need to set up some paths, since bitmap loader requires config to have things + # Should probably change that so that it's not dependant on config import os os.chdir('..') import config diff --git a/gui/pyfatogglepanel.py b/gui/pyfatogglepanel.py deleted file mode 100644 index 75af0b214..000000000 --- a/gui/pyfatogglepanel.py +++ /dev/null @@ -1,199 +0,0 @@ -########################################################################### -# 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 -########################################################################### - -# noinspection PyPackageRequirements -import wx -from gui.bitmap_loader 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) - - 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 - self.headerPanel = wx.Panel(self) - 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") - - # 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]) - self.bmpExpanded = wx.BitmapFromImage(img) - - img = self.bmpCollapsed.ConvertToImage() - 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) - - # Create the header sizer and add static bitmap and static text controls to it - - 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.Add(self.headerBmp, 0, 0, 5) - - self.headerLabel = wx.StaticText(self.headerPanel, wx.ID_ANY, "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) - - # Set the static text font weight to BOLD - - headerFont = parent.GetFont() - headerFont.SetWeight(wx.BOLD) - self.headerLabel.SetFont(headerFont) - - # Create the content panel and its main sizer - - 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.Layout() - - # 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) - - def __del__(self): - pass - - def AddToggleItem(self, hitem): - hitem.Bind(wx.EVT_LEFT_UP, self.toggleContent) - - def GetHeaderContentSizer(self): - return self.hcntSizer - - def GetHeaderPanel(self): - return self.headerPanel - - def InsertItemInHeader(self, item): - self.hcntSizer.Add(item, 0, 0, 0) - self.Layout() - - def AddSizer(self, sizer): - self.contentSizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 0) - self.Layout() - - def GetContentPane(self): - return self.contentPanel - - def SetLabel(self, label): - self.headerLabel.SetLabel(label) - - def IsCollapsed(self): - """ Returns ``True`` if the pane window is currently hidden. """ - if self._toggle == 1: - return False - else: - return True - - def IsExpanded(self): - """ Returns ``True`` if the pane window is currently shown. """ - if self._toggle == 1: - return False - else: - return True - - def OnStateChange(self, sz): - """ - Handles the status changes (collapsing/expanding). - - :param sz: an instance of `wx.Size`. - """ - - # minimal size has priority over the best size so set here our min size - self.SetMinSize(sz) - self.SetSize(sz) - - self.parent.GetSizer().SetSizeHints(self.parent) - - if self.IsCollapsed(): - # expanded . collapsed transition - if self.parent.GetSizer(): - # we have just set the size hints... - sz = self.parent.GetSizer().CalcMin() - - # use SetClientSize() and not SetSize() otherwise the size for - # e.g. a wxFrame with a menubar wouldn't be correctly set - self.parent.SetClientSize(sz) - else: - self.parent.Layout() - else: - # collapsed . expanded transition - # force our parent to "fit", i.e. expand so that it can honour - # our minimal size - self.parent.Fit() - - # Toggle the content panel (hide/show) - def toggleContent(self, event): - self.Freeze() - - if self._toggle == 1: - self.contentMinSize = self.contentPanel.GetSize() - self.contentPanel.Hide() - self.headerBmp.SetBitmap(self.bmpCollapsed) - else: - self.contentPanel.Show() - self.headerBmp.SetBitmap(self.bmpExpanded) - - self._toggle *= -1 - self.Layout() - self.Thaw() - - if self.forceLayout == -1: - if wx.VERSION >= (3, 0): - x, y = self.GetBestSize() - y -= self.contentPanel.GetSize()[1] - else: - x, y = self.GetBestSize() - self.OnStateChange((x, y)) - else: - self.parent.Layout() diff --git a/gui/statsPane.py b/gui/statsPane.py index ce09e2e62..e39007ff7 100644 --- a/gui/statsPane.py +++ b/gui/statsPane.py @@ -28,7 +28,7 @@ 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 +from gui.toggle_panel import TogglePanel from logbook import Logger pyfalog = Logger(__name__) diff --git a/gui/toggle_panel.py b/gui/toggle_panel.py new file mode 100644 index 000000000..7340af112 --- /dev/null +++ b/gui/toggle_panel.py @@ -0,0 +1,187 @@ +#=============================================================================== +# TogglePanel is based on PyCollapsiblePane - includes a few improvements +# such as adding items to header, lack of button implementation ("GTK +# expander" style is implemented with plain text with unicode arrows rather +# than drawn geometry), etc. +# +# When adding TogglePanel to sizer, it is important to ensure the following: +# sizer is vertical +# set proportion = 0 +# +# ToDo: Create animations for collapsing / expanding +# +#=============================================================================== + +import wx + + +class TogglePanel (wx.Panel): + def __init__(self, parent, force_layout=False, *args, **kargs): + super().__init__(parent, *args, **kargs) + + self._toggled = True + self.parent = parent + self.force_layout = force_layout + + # Create the main sizer of this panel + self.main_sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.main_sizer) + + # Create the header panel, set sizer, and add to main sizer + self.header_panel = wx.Panel(self) + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.header_panel.SetSizer(header_sizer) + + self.main_sizer.Add(self.header_panel, 0, wx.EXPAND | wx.TOP | + wx.BOTTOM | wx.RIGHT, 1) + + # Add arrow + self.header_arrow = wx.StaticText(self.header_panel, wx.ID_ANY, + "\u25bc") + header_sizer.Add(self.header_arrow, 0, wx.RIGHT, 5) + + # Add header text + self.header_label = wx.StaticText(self.header_panel, wx.ID_ANY, "") + font = parent.GetFont() + font.SetWeight(wx.BOLD) + self.header_label.SetFont(font) + header_sizer.Add(self.header_label, 0, wx.RIGHT, 5) + + # Add a sizer for additional header items if we need it + self.hcontent_sizer = wx.BoxSizer(wx.HORIZONTAL) + header_sizer.Add(self.hcontent_sizer, 0, wx.RIGHT, 5) + + # Create the content panel, set sizer, and add to main sizer + self.content_panel = wx.Panel(self) + self.content_sizer = wx.BoxSizer(wx.VERTICAL) + self.content_panel.SetSizer(self.content_sizer) + + self.main_sizer.Add(self.content_panel, 0, wx.EXPAND | wx.RIGHT | + wx.LEFT, 5) + + self.Layout() + + # Connect Events + self.header_label.Bind(wx.EVT_LEFT_UP, self.ToggleContent) + self.header_arrow.Bind(wx.EVT_LEFT_UP, self.ToggleContent) + self.header_panel.Bind(wx.EVT_LEFT_UP, self.ToggleContent) + + def __del__(self): + pass + + def AddToggleItem(self, item): + item.Bind(wx.EVT_LEFT_UP, self.ToggleContent) + + def GetHeaderContentSizer(self): + return self.hcontent_sizer + + def GetHeaderPanel(self): + return self.header_panel + + def InsertItemInHeader(self, item): + self.hcontent_sizer.Add(item, 0, 0, 0) + self.AddToggleItem(item) + self.Layout() + + def AddSizer(self, sizer): + self.content_sizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 0) + self.Layout() + + def GetContentPanel(self): + return self.content_panel + + def SetLabel(self, label): + self.header_label.SetLabel(label) + + def IsCollapsed(self): + return not self._toggled + + def IsExpanded(self): + return self._toggled + + def OnStateChange(self, sz): + self.SetSize(sz) + + self.parent.GetSizer().SetSizeHints(self.parent) + + if not self._toggled: + if self.parent.GetSizer(): + # we have just set the size hints... + sz = self.parent.GetSizer().CalcMin() + + # use SetClientSize() and not SetSize() otherwise the size for + # e.g. a wxFrame with a menubar wouldn't be correctly set + self.parent.SetClientSize(sz) + else: + self.parent.Layout() + else: + # force our parent to "fit", i.e. expand so that it can honor + # our minimal size + self.parent.Fit() + + def ToggleContent(self, event): + #self.Freeze() + + if self._toggled: + # If we are expanded, save previous size and collapse by setting + # content height to 0 + self.content_min_size = self.content_panel.GetSize() + self.content_panel.SetMinSize((self.content_min_size[0], 0)) + self.header_arrow.SetLabel("\u25b6") + else: + # If we are collapsed, set content size to previously saved value + self.content_panel.SetMinSize(self.content_min_size) + self.header_arrow.SetLabel("\u25bc") + + self._toggled = not self._toggled + + #self.Thaw() + + if self.force_layout: + self.parent.Layout() + else: + self.OnStateChange(self.GetBestSize()) + +if __name__ == "__main__": + class TestPanel(wx.Panel): + def __init__(self, parent): + super().__init__(parent, size=(-1, -1)) + + if 'wxMSW' in wx.PlatformInfo: + color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) + self.SetBackgroundColour(color) + + main_sizer = wx.BoxSizer(wx.VERTICAL) + + # Generate 3 test panels with different font sizes + for x in range(3): + toggle_panel = TogglePanel(self) + toggle_panel.SetLabel("Test Header") + + content_panel = toggle_panel.GetContentPanel() + content_panel.SetBackgroundColour(wx.WHITE) + + content_sizer = wx.BoxSizer(wx.HORIZONTAL) + header = wx.StaticText(content_panel, -1, "TogglePanel Test") + header.SetFont(wx.Font(10+(x*3), wx.SWISS, wx.NORMAL, wx.BOLD)) + content_sizer.Add(header, 0, wx.ALL, 10) + content_panel.SetSizer(content_sizer) + + main_sizer.Add(toggle_panel, 0, wx.EXPAND | wx.ALL, 2) + + self.SetSizer(main_sizer) + + class Frame(wx.Frame): + def __init__(self, title): + super().__init__(None, title=title, size=(500, 500)) + main_sizer = wx.BoxSizer(wx.VERTICAL) + + self.statsPane = TestPanel(self) + main_sizer.Add(self.statsPane, 0, wx.EXPAND) + + self.SetSizerAndFit(main_sizer) + + app = wx.App(redirect=False) # Error messages go to popup window + top = Frame("Test Toggle Panel") + top.Show() + app.MainLoop() From 6c317d56eee016aa9aba60d0dce50378d803ff76 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 20:45:58 -0400 Subject: [PATCH 007/212] touch up the gauge code --- gui/builtinStatsViews/resistancesViewFull.py | 2 +- gui/builtinStatsViews/resourcesViewFull.py | 2 +- gui/pyfa_gauge.py | 419 +++++++++++++++++ gui/pygauge.py | 448 ------------------- 4 files changed, 421 insertions(+), 450 deletions(-) create mode 100644 gui/pyfa_gauge.py delete mode 100644 gui/pygauge.py diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index a06e4e444..7a6cc6ba9 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -21,7 +21,7 @@ import wx from gui.statsView import StatsView from gui.bitmap_loader import BitmapLoader -from gui.pygauge import PyGauge +from gui.pyfa_gauge import PyGauge from gui.utils.numberFormatter import formatAmount import gui.mainFrame import gui.globalEvents as GE diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index 2a8cb0196..bfc256fa3 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -21,7 +21,7 @@ import wx from gui.statsView import StatsView from gui.bitmap_loader import BitmapLoader -from gui.pygauge import PyGauge +from gui.pyfa_gauge import PyGauge import gui.mainFrame from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py new file mode 100644 index 000000000..ef436d74f --- /dev/null +++ b/gui/pyfa_gauge.py @@ -0,0 +1,419 @@ +#=============================================================================== +# PyfaGauge is a generic Gauge implementation tailored for pyfa (the Python +# Fitting Assistant). It uses the easeOutQuad equation from +# caurina.transitions.Tweener to do animations +# +# ToDo: make SetGradient(from and not dependant on value) +# ToDo: fix 0 range (currently resets range to 0.01, but this causes problems if +# we really set range at 0.01). Perhaps make it -1 and test percentage as +# a negativeor something. +# ToDo: possibly devise a way to determine transition percents on init +# (currently hardcoded) +# +#=============================================================================== + +import copy +import wx + +from gui.utils import color as color_utils +from gui.utils import draw, anim_effects + + +class PyGauge(wx.Window): + def __init__(self, parent, font, max_range=100, size=(-1, 30), *args, + **kargs): + + super().__init__(parent, size=size, *args, **kargs) + + self._size = size + + self._border_colour = wx.BLACK + self._bar_colour = None + self._bar_gradient = None + + self._border_padding = 0 + self._max_range = max_range + self._value = 0 + + self._fraction_digits = 0 + + self._timer_id = wx.NewId() + self._timer = None + + self._oldValue = 0 + + self._anim_duration = 500 + self._anim_step = 0 + self._period = 20 + self._anim_value = 0 + self._anim_direction = 0 + self.anim_effect = anim_effects.OUT_QUAD + + # transition colors used based on how full (or overfilled) the gauge is. + self.transition_colors = [ + (wx.Colour(191, 191, 191), wx.Colour(96, 191, 0)), # < 0-100% + (wx.Colour(191, 167, 96), wx.Colour(255, 191, 0)), # < 100-101% + (wx.Colour(255, 191, 0), wx.Colour(255, 128, 0)), # < 101-103% + (wx.Colour(255, 128, 0), wx.Colour(255, 0, 0)) # < 103-105% + ] + + self.gradient_effect = -35 + + self._percentage = 0 + self._old_percentage = 0 + self._show_remaining = False + + self.font = font + + self.SetBackgroundColour(wx.Colour(51, 51, 51)) + + self._tooltip = wx.ToolTip("0.00/100.00") + self.SetToolTip(self._tooltip) + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) + self.Bind(wx.EVT_TIMER, self.OnTimer) + self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) + + def OnWindowEnter(self, event): + self._show_remaining = True + self.Refresh() + + def OnWindowLeave(self, event): + self._show_remaining = False + self.Refresh() + + def GetBorderColour(self): + return self._border_colour + + def SetBorderColour(self, colour: wx.Colour): + self._border_colour = colour + + def GetBarColour(self): + return self._bar_colour + + def SetBarColour(self, colour: wx.Colour=None): + self._bar_colour = colour + + def SetFractionDigits(self, digits): + self._fraction_digits = digits + + def GetBarGradient(self): + raise NotImplemented() + + def SetBarGradient(self, gradient=None): + raise NotImplemented() + + def GetBorderPadding(self) -> int: + return self._border_padding + + def SetBorderPadding(self, padding): + self._border_padding = padding + + def GetRange(self): + """ Returns the maximum value of the gauge. """ + return self._max_range + + def Animate(self): + if not self._timer: + self._timer = wx.Timer(self, self._timer_id) + + self._anim_step = 0 + self._timer.Start(self._period) + + def SetRange(self, range, reinit=False, animate=True): + """ + Sets the range of the gauge. The gauge length is its + value as a proportion of the range. + """ + + if self._max_range == range: + return + + # we cannot have a range of zero (laws of physics, etc), so we set it + if range <= 0: + self._max_range = 0.01 + else: + self._max_range = range + + if reinit is False: + self._old_percentage = self._percentage + self._percentage = (self._value/self._max_range) * 100 + else: + self._old_percentage = self._percentage + self._percentage = 0 + self._value = 0 + + if animate: + self.Animate() + + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range if self._max_range >0.01 else 0)) + + def GetValue(self): + return self._value + + def SetValue(self, value, animate=True): + """ Sets the current position of the gauge. """ + if self._value == value: + return + + self._old_percentage = self._percentage + self._value = value + + if value < 0: + self._value = 0 + + self._percentage = (self._value/self._max_range) * 100 + + if animate: + self.Animate() + + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range)) + + def SetValueRange(self, value, range, reinit=False): + """ Set both value and range of the gauge. """ + + self.SetRange(range, reinit, animate=False) + self.SetValue(value, animate=False) + + self.Animate() + self._tooltip.SetTip("%.2f/%.2f" % + (self._value, + self._max_range if self._max_range > 0.01 else 0)) + + def OnEraseBackground(self, event): + pass + + def OnPaint(self, event): + dc = wx.BufferedPaintDC(self) + rect = self.GetClientRect() + + dc.SetBackground(wx.Brush(self.GetBackgroundColour())) + dc.Clear() + + colour = self.GetBackgroundColour() + + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.Pen(colour)) + + dc.DrawRectangle(rect) + + value = self._percentage + + if self._timer: + if self._timer.IsRunning(): + value = self._anim_value + + if self._border_colour: + dc.SetPen(wx.Pen(self.GetBorderColour())) + dc.DrawRectangle(rect) + pad = 1 + self.GetBorderPadding() + rect.Deflate(pad, pad) + + if self.GetBarColour(): + # if we have a bar color set, then we will use this + + colour = self.GetBarColour() + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.Pen(colour)) + + # calculate width of bar and draw it + if value > 100: + w = rect.width + else: + w = rect.width * (float(value) / 100) + + r = copy.copy(rect) + r.width = w + dc.DrawRectangle(r) + else: + # if bar color is not set, then we use pre-defined transitions + # for the colors based on the percentage value + + # calculate width of bar + if value > 100: + w = rect.width + else: + w = rect.width * (float(value) / 100) + r = copy.copy(rect) + r.width = w + + # determine transition range number and calculate xv (which is the + # progress between the two transition ranges) + pv = value + if pv <= 100: + xv = pv/100 + transition = 0 + elif pv <= 101: + xv = pv - 100 + transition = 1 + elif pv <= 103: + xv = (pv - 101)/2 + transition = 2 + elif pv <= 105: + xv = (pv - 103)/2 + transition = 3 + else: + pv = 106 + xv = pv - 100 + transition = -1 + + if transition != -1: + start_color, end_color = self.transition_colors[transition] + color = color_utils.CalculateTransition(start_color, end_color, + xv) + else: + color = wx.Colour(191, 48, 48) # dark red + + color_factor = self.gradient_effect / 100 + mid_factor = (self.gradient_effect / 2) / 100 + + if self.gradient_effect > 0: + gradient_color = color_utils.Brighten(color, color_factor) + gradient_mid = color_utils.Brighten(color, mid_factor) + else: + gradient_color = color_utils.Darken(color, color_factor * -1) + gradient_mid = color_utils.Darken(color, mid_factor * -1) + + # draw bar + gradient_bitmap = draw.DrawGradientBar( + r.width, + r.height, + gradient_mid, + color, + gradient_color + ) + dc.DrawBitmap(gradient_bitmap, r.left, r.top) + + # font stuff begins here + dc.SetFont(self.font) + + # determine shadow position + r = copy.copy(rect) + r.left += 1 + r.top += 1 + + if self._max_range == 0.01 and self._value > 0: + format = u'\u221e' # infinity symbol + # drop shadow + dc.SetTextForeground(wx.Colour(80, 80, 80)) # dark grey + dc.DrawLabel(format, r, wx.ALIGN_CENTER) + # text + dc.SetTextForeground(wx.WHITE) + dc.DrawLabel(format, rect, wx.ALIGN_CENTER) + else: + if not self.GetBarColour() and self._show_remaining: + # we only do these for gradients with mouse over + range = self._max_range if self._max_range > 0.01 else 0 + value = range - self._value + if value < 0: + label = "over" + value = -value + else: + label = "left" + format = "{0:." + str(self._fraction_digits) + "f} " + label + else: + format = "{0:." + str(self._fraction_digits) + "f}%" + + # drop shadow + dc.SetTextForeground(wx.Colour(80, 80, 80)) + dc.DrawLabel(format.format(value), r, wx.ALIGN_CENTER) + # text + dc.SetTextForeground(wx.WHITE) + dc.DrawLabel(format.format(value), rect, wx.ALIGN_CENTER) + + def OnTimer(self, event): + old_value = self._old_percentage + value = self._percentage + start = 0 + + # -1 = left direction, 1 = right direction + direction = 1 if old_value < value else -1 + + end = direction * (value - old_value) + + self._anim_direction = direction + step = self.anim_effect(self._anim_step, start, end, self._anim_duration) + + self._anim_step += self._period + + if self._timer_id == event.GetId(): + stop_timer = False + + if self._anim_step > self._anim_duration: + stop_timer = True + + # add new value to the animation if we haven't reached our goal + # otherwise, stop animation + if direction == 1: + if old_value + step < value: + self._anim_value = old_value + step + else: + stop_timer = True + else: + if old_value - step > value: + self._anim_value = old_value - step + else: + stop_timer = True + + if stop_timer: + self._timer.Stop() + + self.Refresh() + +if __name__ == "__main__": + def frange(x, y, jump): + while x < y: + yield x + x += jump + + class TestPanel(wx.Panel): + def __init__(self, parent, size=(500, 500)): + wx.Panel.__init__(self, parent, size=size) + box = wx.BoxSizer(wx.VERTICAL) + + # tests the colors of transition based on percentage used + # list of test values (from 99 -> 106 in increments of 0.5) + tests = [x for x in frange(99, 106.5, .5)] + + font = wx.Font(9, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + + for i, value in enumerate(tests): + self.gauge = PyGauge(self, font, size=(100, 25)) + self.gauge.SetValueRange(value, 100) + self.gauge.SetFractionDigits(2) + box.Add(self.gauge, 0, wx.ALL, 2) + + gauge = PyGauge(self, font, size=(100, 25)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(38, 133, 198)) + gauge.SetValue(59) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL, 2) + + self.SetSizer(box) + self.Layout() + + # see animation going backwards with last gauge + wx.CallLater(2000, self.ChangeValues) + + def ChangeValues(self): + self.gauge.SetValueRange(4, 100) + + class Frame(wx.Frame): + def __init__(self, title, size=(500, 800)): + wx.Frame.__init__(self, None, title=title, size=size) + self.statusbar = self.CreateStatusBar() + main_sizer = wx.BoxSizer(wx.VERTICAL) + panel = TestPanel(self, size=size) + main_sizer.Add(panel) + self.SetSizer(main_sizer) + + + app = wx.App(redirect=False) # Error messages go to popup window + top = Frame("Test Chrome Tabs") + top.Show() + app.MainLoop() + diff --git a/gui/pygauge.py b/gui/pygauge.py deleted file mode 100644 index 6c08ea209..000000000 --- a/gui/pygauge.py +++ /dev/null @@ -1,448 +0,0 @@ -# --------------------------------------------------------------------------------- # -# PYFAGAUGE wxPython IMPLEMENTATION -# -# Darriele, @ 08/30/2010 -# Updated: 09/07/2010 -# Based on AWG : pygauge code -# --------------------------------------------------------------------------------- # - -""" -PyfaGauge is a generic Gauge implementation tailored for PYFA (Python Fitting Assistant) -It uses the easeOutQuad equation from caurina.transitions.Tweener to do the animation stuff -""" - -# noinspection PyPackageRequirements -import wx -import copy - -from gui.utils import color -import gui.utils.draw as drawUtils -import gui.utils.anim_effects as animEffects -import gui.utils.fonts as fonts - -from service.fit import Fit - - -class PyGauge(wx.PyWindow): - """ - This class provides a visual alternative for `wx.Gauge`. It currently - only support determinant mode (see SetValue and SetRange) - """ - - def __init__(self, parent, id=wx.ID_ANY, range=100, pos=wx.DefaultPosition, - size=(-1, 30), style=0): - """ - Default class constructor. - - :param `parent`: parent window. Must not be ``None``; - :param `id`: window identifier. A value of -1 indicates a default value; - :param `pos`: the control position. A value of (-1, -1) indicates a default position, - chosen by either the windowing system or wxPython, depending on platform; - :param `size`: the control size. A value of (-1, -1) indicates a default size, - chosen by either the windowing system or wxPython, depending on platform. - """ - - wx.PyWindow.__init__(self, parent, id, pos, size, style) - - self._size = size - - self._border_colour = wx.BLACK - self._barColour = self._barColourSorted = [wx.Colour(212, 228, 255)] - self._barGradient = self._barGradientSorted = None - - self._border_padding = 0 - self._range = range - self._value = 0 - - self._fractionDigits = 0 - - self._timerId = wx.NewId() - self._timer = None - - self._oldValue = 0 - - self._animDuration = 500 - self._animStep = 0 - self._period = 20 - self._animValue = 0 - 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.gradientEffect = -35 - - self._percentage = 0 - self._oldPercentage = 0 - self._showRemaining = False - - 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._tooltip = wx.ToolTip("") - self.SetToolTip(self._tooltip) - self._tooltip.SetTip("0.00/100.00") - - self.Bind(wx.EVT_PAINT, self.OnPaint) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) - self.Bind(wx.EVT_TIMER, self.OnTimer) - self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) - self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) - - def OnWindowEnter(self, event): - self._showRemaining = True - self.Refresh() - - def OnWindowLeave(self, event): - self._showRemaining = False - self.Refresh() - - def DoGetBestSize(self): - """ - Overridden base class virtual. Determines the best size of the - button based on the label and bezel size. - """ - - return wx.Size(self._size[0], self._size[1]) - - def GetBorderColour(self): - return self._border_colour - - def SetBorderColour(self, colour): - self._border_colour = colour - - SetBorderColor = SetBorderColour - GetBorderColor = GetBorderColour - - def GetBarColour(self): - return self._barColour[0] - - def SetBarColour(self, colour): - if not isinstance(colour, list): - self._barColour = [colour] - else: - self._barColour = list(colour) - - SetBarColor = SetBarColour - GetBarColor = GetBarColour - - def SetFractionDigits(self, digits): - self._fractionDigits = digits - - def GetBarGradient(self): - """ Returns a tuple containing the gradient start and end colours. """ - - if self._barGradient is None: - return None - - return self._barGradient[0] - - 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 is None: - self._barGradient = None - else: - if not isinstance(gradient, list): - self._barGradient = [gradient] - else: - self._barGradient = list(gradient) - - def GetBorderPadding(self): - """ Gets the border padding. """ - - return self._border_padding - - def SetBorderPadding(self, padding): - """ - Sets the border padding. - - :param padding: pixels between the border and the progress bar. - """ - - self._border_padding = padding - - def GetRange(self): - """ Returns the maximum value of the gauge. """ - - return self._range - - def Animate(self): - sFit = Fit.getInstance() - if sFit.serviceFittingOptions["enableGaugeAnimation"]: - if not self._timer: - self._timer = wx.Timer(self, self._timerId) - self._animStep = 0 - self._timer.Start(self._period) - else: - self._animValue = self._percentage - self.Refresh() - - def SetRange(self, range, reinit=False): - """ - Sets the range of the gauge. The gauge length is its - value as a proportion of the range. - - :param reinit: - :param range: The maximum value of the gauge. - """ - - if self._range == range: - return - - range_ = float(range) - - if range_ <= 0: - self._range = 0.01 - else: - self._range = range_ - - if reinit is False: - self._oldPercentage = self._percentage - self._percentage = (self._value / self._range) * 100 - else: - self._oldPercentage = self._percentage - self._percentage = 0 - self._value = 0 - - self.Animate() - - 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. """ - - return self._value - - def SetValue(self, value): - """ Sets the current position of the gauge. """ - if self._value == value: - return - - value = float(value) - self._oldPercentage = self._percentage - self._value = value - if value < 0: - self._value = 0 - 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): - if self._value == value and self._range == range: - return - - range_ = float(range) - - if range_ <= 0: - self._range = 0.01 - else: - self._range = range_ - - value = float(value) - - self._value = value - if value < 0: - self._value = float(0) - - if reinit is False: - self._oldPercentage = self._percentage - 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)) - - @staticmethod - def OnEraseBackground(event): - """ - Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{PyGauge}. - - :param event: a `wx.EraseEvent` event to be processed. - - :note: This method is intentionally empty to reduce flicker. - """ - - pass - - def OnPaint(self, event): - """ - Handles the ``wx.EVT_PAINT`` event for L{PyGauge}. - - :param event: a `wx.PaintEvent` event to be processed. - """ - - dc = wx.BufferedPaintDC(self) - rect = self.GetClientRect() - - dc.SetBackground(wx.Brush(self.GetBackgroundColour())) - dc.Clear() - - colour = self.GetBackgroundColour() - - dc.SetBrush(wx.Brush(colour)) - dc.SetPen(wx.Pen(colour)) - - dc.DrawRectangleRect(rect) - - value = self._percentage - if self._timer: - if self._timer.IsRunning(): - value = self._animValue - - if self._border_colour: - dc.SetPen(wx.Pen(self.GetBorderColour())) - dc.DrawRectangleRect(rect) - pad = 1 + self.GetBorderPadding() - rect.Deflate(pad, pad) - - if self.GetBarGradient(): - - if value > 100: - w = rect.width - else: - w = rect.width * (float(value) / 100) - r = copy.copy(rect) - r.width = w - - if r.width > 0: - # If we draw it with zero width, GTK throws errors. This way, - # only draw it if the gauge will actually show something. - # We stick other calculations in this block to avoid wasting - # time on them if not needed. See GH issue #282 - - pv = value - - if pv <= 100: - xv = pv / 100 - transition = 0 - - elif pv <= 101: - xv = pv - 100 - transition = 1 - - elif pv <= 103: - xv = (pv - 101) / 2 - transition = 2 - - elif pv <= 105: - xv = (pv - 103) / 2 - transition = 3 - - else: - pv = 106 - xv = pv - 100 - transition = -1 - - if transition != -1: - colorS, colorE = self.transitionsColors[transition] - color = color.CalculateTransitionColor(colorS, colorE, xv) - else: - color = wx.Colour(191, 48, 48) - - if self.gradientEffect > 0: - gcolor = color.BrightenColor(color, float(self.gradientEffect) / 100) - gMid = color.BrightenColor(color, float(self.gradientEffect / 2) / 100) - else: - gcolor = color.DarkenColor(color, float(-self.gradientEffect) / 100) - gMid = color.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() - dc.SetBrush(wx.Brush(colour)) - dc.SetPen(wx.Pen(colour)) - if value > 100: - w = rect.width - else: - w = rect.width * (float(value) / 100) - r = copy.copy(rect) - r.width = w - dc.DrawRectangleRect(r) - - dc.SetFont(self.font) - - r = copy.copy(rect) - r.left += 1 - r.top += 1 - if self._range == 0.01 and self._value > 0: - formatStr = '\u221e' - dc.SetTextForeground(wx.Colour(80, 80, 80)) - dc.DrawLabel(formatStr, r, wx.ALIGN_CENTER) - - dc.SetTextForeground(wx.Colour(255, 255, 255)) - dc.DrawLabel(formatStr, rect, wx.ALIGN_CENTER) - else: - if self.GetBarGradient() and self._showRemaining: - range = self._range if self._range > 0.01 else 0 - value = range - self._value - if value < 0: - label = "over" - value = -value - else: - label = "left" - formatStr = "{0:." + str(self._fractionDigits) + "f} " + label - - else: - formatStr = "{0:." + str(self._fractionDigits) + "f}%" - - dc.SetTextForeground(wx.Colour(80, 80, 80)) - dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) - - dc.SetTextForeground(wx.Colour(255, 255, 255)) - dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) - - def OnTimer(self, event): - """ - Handles the ``wx.EVT_TIMER`` event for L{PyfaGauge}. - - :param event: a timer event - """ - oldValue = self._oldPercentage - value = self._percentage - start = 0 - - direction = 1 if oldValue < value else -1 - - end = direction * (value - oldValue) - - self._animDirection = direction - step = self.animEffect(self._animStep, start, end, self._animDuration) - - self._animStep += self._period - - if self._timerId == event.GetId(): - stop_timer = False - - if self._animStep > self._animDuration: - stop_timer = True - - if direction == 1: - if (oldValue + step) < value: - self._animValue = oldValue + step - else: - stop_timer = True - else: - if (oldValue - step) > value: - self._animValue = oldValue - step - - else: - stop_timer = True - - if stop_timer: - self._timer.Stop() - - self.Refresh() From 951b35a34508a79e2d89c908af78411aa0741962 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 20:50:05 -0400 Subject: [PATCH 008/212] PyDeadObjectError fixes and PFNotebook -> ChromeNotebook renaming --- gui/additionsPane.py | 4 ++-- gui/builtinViews/fittingView.py | 4 ++-- gui/mainFrame.py | 16 +++++++--------- gui/multiSwitch.py | 10 +++++----- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/gui/additionsPane.py b/gui/additionsPane.py index 90edcc68b..9dc5c7e1d 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -29,7 +29,7 @@ from gui.builtinAdditionPanes.fighterView import FighterView from gui.builtinAdditionPanes.implantView import ImplantView from gui.builtinAdditionPanes.notesView import NotesView from gui.builtinAdditionPanes.projectedView import ProjectedView -from gui.chrome_tabs import PFNotebook +from gui.chrome_tabs import ChromeNotebook from gui.toggle_panel import TogglePanel @@ -44,7 +44,7 @@ class AdditionsPane(TogglePanel): baseSizer = wx.BoxSizer(wx.HORIZONTAL) pane.SetSizer(baseSizer) - self.notebook = PFNotebook(pane, False) + self.notebook = ChromeNotebook(pane, False) self.notebook.SetMinSize((-1, 1000)) baseSizer.Add(self.notebook, 1, wx.EXPAND) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 75f76cc31..6ee570a55 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -288,7 +288,7 @@ class FittingView(d.Display): sFit = Fit.getInstance() sFit.refreshFit(self.getActiveFit()) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) - except wx._core.PyDeadObjectError: + except RuntimeError: pyfalog.warning("Caught dead object") pass @@ -518,7 +518,7 @@ class FittingView(d.Display): self.Refresh() self.Show(self.activeFitID is not None and self.activeFitID == event.fitID) - except wx._core.PyDeadObjectError: + except RuntimeError: pyfalog.error("Caught dead object") finally: event.Skip() diff --git a/gui/mainFrame.py b/gui/mainFrame.py index bffee39b7..93635fe9a 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -25,8 +25,6 @@ import sqlalchemy # noinspection PyPackageRequirements import wx # noinspection PyPackageRequirements -from wx._core import PyDeadObjectError -# noinspection PyPackageRequirements from wx.lib.wordwrap import wordwrap # noinspection PyPackageRequirements from wx.lib.inspection import InspectionTool @@ -39,7 +37,7 @@ import config from eos.config import gamedata_version import gui.aboutData -from gui.chrome_tabs import PFNotebook +from gui.chrome_tabs import ChromeNotebook import gui.globalEvents as GE from gui.bitmap_loader import BitmapLoader @@ -177,7 +175,7 @@ class MainFrame(wx.Frame, IPortUser): self.fitMultiSwitch = MultiSwitch(self.fitting_additions_split) self.additionsPane = AdditionsPane(self.fitting_additions_split) - self.notebookBrowsers = PFNotebook(self.browser_fitting_split, False) + self.notebookBrowsers = ChromeNotebook(self.browser_fitting_split, False) marketImg = BitmapLoader.getImage("market_small", "gui") shipBrowserImg = BitmapLoader.getImage("ship_small", "gui") @@ -403,7 +401,7 @@ class MainFrame(wx.Frame, IPortUser): dlg.ShowModal() try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") def showImplantSetEditor(self, event): @@ -430,7 +428,7 @@ class MainFrame(wx.Frame, IPortUser): print(("oops, invalid fit format %d" % format_)) try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") return @@ -440,7 +438,7 @@ class MainFrame(wx.Frame, IPortUser): try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") def showPreferenceDialog(self, event): @@ -779,7 +777,7 @@ class MainFrame(wx.Frame, IPortUser): try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") def exportSkillsNeeded(self, event): @@ -835,7 +833,7 @@ class MainFrame(wx.Frame, IPortUser): self.progressDialog.ShowModal() try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") def backupToXml(self, event): diff --git a/gui/multiSwitch.py b/gui/multiSwitch.py index d64d41e68..b199848eb 100644 --- a/gui/multiSwitch.py +++ b/gui/multiSwitch.py @@ -17,13 +17,13 @@ # along with pyfa. If not, see . # ============================================================================= -from gui.chrome_tabs import PFNotebook +from gui.chrome_tabs import ChromeNotebook import gui.builtinViews.emptyView -class MultiSwitch(PFNotebook): +class MultiSwitch(ChromeNotebook): def __init__(self, parent): - PFNotebook.__init__(self, parent) + ChromeNotebook.__init__(self, parent) # self.AddPage() # now handled by mainFrame self.handlers = handlers = [] for type in TabSpawner.tabTypes: @@ -40,10 +40,10 @@ class MultiSwitch(PFNotebook): tabWnd = gui.builtinViews.emptyView.BlankPage(self) tabWnd.handleDrag = lambda type, info: self.handleDrag(type, info) - PFNotebook.AddPage(self, tabWnd, tabTitle, tabImage, True) + ChromeNotebook.AddPage(self, tabWnd, tabTitle, tabImage, True) def DeletePage(self, n, *args, **kwargs): - PFNotebook.DeletePage(self, n, *args, **kwargs) + ChromeNotebook.DeletePage(self, n, *args, **kwargs) if self.GetPageCount() == 0: self.AddPage() From 361f7fc5bb932667a80841e2b6b12d35476c825e Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 21:09:50 -0400 Subject: [PATCH 009/212] misc fixes --- gui/additionsPane.py | 20 ++++++++++---------- gui/builtinAdditionPanes/fighterView.py | 7 ++++--- gui/builtinAdditionPanes/implantView.py | 4 ++-- gui/builtinViews/fittingView.py | 2 +- gui/characterEditor.py | 4 ++-- gui/chrome_tabs.py | 3 +-- gui/display.py | 4 ++-- gui/mainFrame.py | 10 +++++----- gui/statsPane.py | 2 +- 9 files changed, 28 insertions(+), 28 deletions(-) diff --git a/gui/additionsPane.py b/gui/additionsPane.py index 9dc5c7e1d..dd1ebf25e 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -36,10 +36,10 @@ from gui.toggle_panel import TogglePanel class AdditionsPane(TogglePanel): def __init__(self, parent): - TogglePanel.__init__(self, parent, forceLayout=1) + TogglePanel.__init__(self, parent, force_layout=1) self.SetLabel("Additions") - pane = self.GetContentPane() + pane = self.GetContentPanel() baseSizer = wx.BoxSizer(wx.HORIZONTAL) pane.SetSizer(baseSizer) @@ -59,28 +59,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", image=droneImg, closeable=False) self.fighter = FighterView(self.notebook) - self.notebook.AddPage(self.fighter, "Fighters", tabImage=fighterImg, showClose=False) + self.notebook.AddPage(self.fighter, "Fighters", image=fighterImg, closeable=False) self.cargo = CargoView(self.notebook) - self.notebook.AddPage(self.cargo, "Cargo", tabImage=cargoImg, showClose=False) + self.notebook.AddPage(self.cargo, "Cargo", image=cargoImg, closeable=False) self.implant = ImplantView(self.notebook) - self.notebook.AddPage(self.implant, "Implants", tabImage=implantImg, showClose=False) + self.notebook.AddPage(self.implant, "Implants", image=implantImg, closeable=False) self.booster = BoosterView(self.notebook) - self.notebook.AddPage(self.booster, "Boosters", tabImage=boosterImg, showClose=False) + self.notebook.AddPage(self.booster, "Boosters", image=boosterImg, closeable=False) self.projectedPage = ProjectedView(self.notebook) - self.notebook.AddPage(self.projectedPage, "Projected", tabImage=projectedImg, showClose=False) + self.notebook.AddPage(self.projectedPage, "Projected", image=projectedImg, closeable=False) self.gangPage = CommandView(self.notebook) - self.notebook.AddPage(self.gangPage, "Command", tabImage=gangImg, showClose=False) + self.notebook.AddPage(self.gangPage, "Command", image=gangImg, closeable=False) self.notes = NotesView(self.notebook) - self.notebook.AddPage(self.notes, "Notes", tabImage=notesImg, showClose=False) + self.notebook.AddPage(self.notes, "Notes", image=notesImg, closeable=False) self.notebook.SetSelection(0) diff --git a/gui/builtinAdditionPanes/fighterView.py b/gui/builtinAdditionPanes/fighterView.py index f3b4aea1f..2d1ee4c17 100644 --- a/gui/builtinAdditionPanes/fighterView.py +++ b/gui/builtinAdditionPanes/fighterView.py @@ -60,7 +60,8 @@ 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) + # @todo pheonix: Add spacer doesn't take a tuple anymore, using int of 0, and other parameters are killed off + textSizer.AddSpacer(0) for x in self.labels: lbl = wx.StaticText(self, wx.ID_ANY, x.capitalize()) @@ -75,7 +76,7 @@ class FighterView(wx.Panel): lbl = wx.StaticText(self, wx.ID_ANY, "0") setattr(self, "label%sTotal" % (x.capitalize()), lbl) textSizer.Add(lbl, 0, wx.ALIGN_CENTER) - textSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + textSizer.AddSpacer(0) mainSizer.Add(textSizer, 0, wx.EXPAND, 5) @@ -94,7 +95,7 @@ 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( + color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOWTEXT) lbl = getattr(self, "label%sUsed" % x.capitalize()) diff --git a/gui/builtinAdditionPanes/implantView.py b/gui/builtinAdditionPanes/implantView.py index eb7997e4d..e779f5672 100644 --- a/gui/builtinAdditionPanes/implantView.py +++ b/gui/builtinAdditionPanes/implantView.py @@ -41,12 +41,12 @@ class ImplantView(wx.Panel): mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0) radioSizer = wx.BoxSizer(wx.HORIZONTAL) - radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + radioSizer.AddSpacer(0) 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) radioSizer.Add(self.rbChar, 0, wx.ALL, 5) - radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + radioSizer.AddSpacer(0) mainSizer.Add(radioSizer, 0, wx.EXPAND, 5) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 6ee570a55..3a9623739 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -51,7 +51,7 @@ class FitSpawner(gui.multiSwitch.TabSpawner): self.multiSwitch = multiSwitch self.mainFrame = mainFrame = gui.mainFrame.MainFrame.getInstance() mainFrame.Bind(EVT_FIT_SELECTED, self.fitSelected) - self.multiSwitch.tabsContainer.handleDrag = self.handleDrag + self.multiSwitch.tabs_container.handleDrag = self.handleDrag def fitSelected(self, event): count = -1 diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 4865cb578..0328f6b05 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -24,7 +24,7 @@ from .utils.floatspin import FloatSpin # noinspection PyPackageRequirements import wx.lib.newevent # noinspection PyPackageRequirements -import wx.gizmos +from wx.dataview import TreeListCtrl from gui.bitmap_loader import BitmapLoader from gui.contextMenu import ContextMenu import gui.globalEvents as GE @@ -306,7 +306,7 @@ class SkillTreeView(wx.Panel): self.searchTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.populateSkillTreeSkillSearch, self.searchTimer) - tree = self.skillTreeListCtrl = wx.gizmos.TreeListCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) + tree = self.skillTreeListCtrl = 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) diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index 4e454d014..af98c7d09 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -205,8 +205,7 @@ class ChromeNotebook(wx.Panel): wx.PostEvent(self, PageChanged(current_page, new_page)) - def AddPage(self, win=None, title="Empty Tab", image: wx.Image=None, - closeable=True): + def AddPage(self, win=None, title="Empty Tab", image: wx.Image=None, closeable=True): if self._active_page: self._active_page.Hide() diff --git a/gui/display.py b/gui/display.py index 0462fdf24..0006ebc84 100644 --- a/gui/display.py +++ b/gui/display.py @@ -69,7 +69,7 @@ class Display(wx.ListCtrl): info = wx.ListItem() # noinspection PyPropertyAccess info.m_mask = wx.LIST_MASK_WIDTH - self.InsertColumnInfo(i, info) + self.InsertColumn(i, info) self.SetColumnWidth(i, 0) self.imageListBase = self.imageList.ImageCount @@ -156,7 +156,7 @@ class Display(wx.ListCtrl): info.m_text = col.columnText info.m_width = -1 info.m_format = wx.LIST_FORMAT_LEFT - self.InsertColumnInfo(i, info) + self.InsertColumn(i, info) col.resized = False if i == 0 and col.size != wx.LIST_AUTOSIZE_USEHEADER: col.size += 4 diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 93635fe9a..2a79ca1c0 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -136,8 +136,8 @@ class OpenFitsThread(threading.Thread): wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fits[-1], startup=2)) wx.CallAfter(self.callback) - -class MainFrame(wx.Frame, IPortUser): +# todo: include IPortUser again +class MainFrame(wx.Frame): __instance = None @classmethod @@ -161,7 +161,7 @@ class MainFrame(wx.Frame, IPortUser): self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) # Load and set the icon for pyfa main window - i = wx.IconFromBitmap(BitmapLoader.getBitmap("pyfa", "gui")) + i = wx.Icon(BitmapLoader.getBitmap("pyfa", "gui")) self.SetIcon(i) # Create the layout and windows @@ -181,11 +181,11 @@ class MainFrame(wx.Frame, IPortUser): 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", image=marketImg, closeable=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", image=shipBrowserImg, closeable=False) self.notebookBrowsers.SetSelection(1) diff --git a/gui/statsPane.py b/gui/statsPane.py index e39007ff7..f245ae6d1 100644 --- a/gui/statsPane.py +++ b/gui/statsPane.py @@ -96,7 +96,7 @@ class StatsPane(wx.Panel): i = 0 for viewName in self.DEFAULT_VIEWS: tp = TogglePanel(self) - contentPanel = tp.GetContentPane() + contentPanel = tp.GetContentPanel() contentPanel.viewName = viewName try: From 610f501608eb0b7b61164fd54e07d9978057863f Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 21:11:10 -0400 Subject: [PATCH 010/212] "Color Correction" --- gui/builtinMarketBrowser/pfSearchBox.py | 4 ++-- gui/builtinShipBrowser/categoryItem.py | 2 +- gui/builtinShipBrowser/fitItem.py | 4 ++-- gui/builtinShipBrowser/navigationPanel.py | 4 ++-- gui/builtinShipBrowser/pfListPane.py | 2 +- gui/builtinShipBrowser/pfStaticText.py | 2 +- gui/builtinShipBrowser/raceSelector.py | 2 +- gui/builtinShipBrowser/sfBrowserItem.py | 2 +- gui/builtinShipBrowser/shipItem.py | 2 +- gui/builtinStatsViews/resourcesViewFull.py | 2 +- gui/builtinViews/emptyView.py | 2 +- gui/builtinViews/fittingView.py | 4 ++-- gui/builtinViews/implantEditor.py | 2 +- gui/characterEditor.py | 4 ++-- gui/itemStats.py | 4 ++-- gui/utils/anim.py | 6 +++--- 16 files changed, 24 insertions(+), 24 deletions(-) diff --git a/gui/builtinMarketBrowser/pfSearchBox.py b/gui/builtinMarketBrowser/pfSearchBox.py index 12a069e85..b696026f8 100644 --- a/gui/builtinMarketBrowser/pfSearchBox.py +++ b/gui/builtinMarketBrowser/pfSearchBox.py @@ -226,8 +226,8 @@ class PFSearchBox(wx.Window): def OnPaint(self, event): dc = wx.BufferedPaintDC(self) - bkColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - sepColor = colorUtils.GetSuitableColor(bkColor, 0.2) + bkColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + sepColor = colorUtils.GetSuitable(bkColor, 0.2) rect = self.GetRect() if self.resized: diff --git a/gui/builtinShipBrowser/categoryItem.py b/gui/builtinShipBrowser/categoryItem.py index 7788aeef9..14d413e21 100644 --- a/gui/builtinShipBrowser/categoryItem.py +++ b/gui/builtinShipBrowser/categoryItem.py @@ -100,7 +100,7 @@ class CategoryItem(SFBrowserItem): # rect = self.GetRect() self.UpdateElementsPos(mdc) - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) textColor = colorUtils.GetSuitableColor(windowColor, 1) mdc.SetTextForeground(textColor) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index 5e6b089c3..d942acdb1 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -479,7 +479,7 @@ class FitItem(SFItem.SFBrowserItem): def DrawItem(self, mdc): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) textColor = colorUtils.GetSuitableColor(windowColor, 1) mdc.SetTextForeground(textColor) @@ -568,7 +568,7 @@ class FitItem(SFItem.SFBrowserItem): def RenderBackground(self): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) # activeFitID = self.mainFrame.getActiveFit() state = self.GetState() diff --git a/gui/builtinShipBrowser/navigationPanel.py b/gui/builtinShipBrowser/navigationPanel.py index 7f802a71b..ef55dec9d 100644 --- a/gui/builtinShipBrowser/navigationPanel.py +++ b/gui/builtinShipBrowser/navigationPanel.py @@ -211,7 +211,7 @@ class NavigationPanel(SFItem.SFBrowserItem): def DrawItem(self, mdc): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) textColor = colorUtils.GetSuitableColor(windowColor, 1) sepColor = colorUtils.GetSuitableColor(windowColor, 0.2) @@ -230,7 +230,7 @@ class NavigationPanel(SFItem.SFBrowserItem): def RenderBackground(self): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) sFactor = 0.1 diff --git a/gui/builtinShipBrowser/pfListPane.py b/gui/builtinShipBrowser/pfListPane.py index bb0bf7bc4..752417729 100644 --- a/gui/builtinShipBrowser/pfListPane.py +++ b/gui/builtinShipBrowser/pfListPane.py @@ -30,7 +30,7 @@ class PFListPane(wx.ScrolledWindow): self._wCount = 0 self.itemsHeight = 1 - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) self.SetVirtualSize((1, 1)) self.SetScrollRate(0, 1) diff --git a/gui/builtinShipBrowser/pfStaticText.py b/gui/builtinShipBrowser/pfStaticText.py index 76640e3de..af6ba21be 100644 --- a/gui/builtinShipBrowser/pfStaticText.py +++ b/gui/builtinShipBrowser/pfStaticText.py @@ -9,7 +9,7 @@ pyfalog = Logger(__name__) 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()) - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + 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) diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index 5b3b40601..4434541eb 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -166,7 +166,7 @@ class RaceSelector(wx.Window): def OnPaint(self, event): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) # bkColor = colorUtils.GetSuitableColor(windowColor, 0.1) sepColor = colorUtils.GetSuitableColor(windowColor, 0.2) diff --git a/gui/builtinShipBrowser/sfBrowserItem.py b/gui/builtinShipBrowser/sfBrowserItem.py index 876143bdb..449364ce6 100644 --- a/gui/builtinShipBrowser/sfBrowserItem.py +++ b/gui/builtinShipBrowser/sfBrowserItem.py @@ -400,7 +400,7 @@ class SFBrowserItem(wx.Window): def RenderBackground(self): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) state = self.GetState() diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py index b6879f01b..e4e3902b6 100644 --- a/gui/builtinShipBrowser/shipItem.py +++ b/gui/builtinShipBrowser/shipItem.py @@ -232,7 +232,7 @@ class ShipItem(SFItem.SFBrowserItem): def DrawItem(self, mdc): # rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) textColor = colorUtils.GetSuitableColor(windowColor, 1) mdc.SetTextForeground(textColor) diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index bfc256fa3..3983d19d5 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -283,7 +283,7 @@ class ResourcesViewFull(StatsView): label.SetToolTip(wx.ToolTip("%.1f" % value)) colorWarn = wx.Colour(204, 51, 51) - colorNormal = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + colorNormal = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) if usedTurretHardpoints > totalTurretHardpoints: colorT = colorWarn diff --git a/gui/builtinViews/emptyView.py b/gui/builtinViews/emptyView.py index c5df9e4bd..845364fd1 100644 --- a/gui/builtinViews/emptyView.py +++ b/gui/builtinViews/emptyView.py @@ -13,7 +13,7 @@ class BlankPage(wx.Panel): self.parent = parent self.parent.Bind(EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=None)) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 3a9623739..7734cf634 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -790,11 +790,11 @@ class FittingView(d.Display): mdc.SelectObject(mbmp) - mdc.SetBackground(wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))) + mdc.SetBackground(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))) mdc.Clear() mdc.SetFont(font) - mdc.SetTextForeground(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)) + mdc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)) cx = padding for i, col in enumerate(self.activeColumns): diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index 36bcc37f0..245f4471f 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -23,7 +23,7 @@ class BaseImplantEditorView(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) - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.HORIZONTAL) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 0328f6b05..0ab0bde02 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -271,7 +271,7 @@ class SkillTreeView(wx.Panel): 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)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.VERTICAL) @@ -609,7 +609,7 @@ class APIView(wx.Panel): 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)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) self.apiUrlCreatePredefined = "https://community.eveonline.com/support/api-key/CreatePredefined?accessMask=8" self.apiUrlKeyList = "https://community.eveonline.com/support/api-key/" diff --git a/gui/itemStats.py b/gui/itemStats.py index 79a9b9caf..c22a8f368 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -251,8 +251,8 @@ class ItemDescription(wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainSizer) - bgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - fgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + bgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + fgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) self.description = wx.html.HtmlWindow(self) diff --git a/gui/utils/anim.py b/gui/utils/anim.py index 947fa2abf..59f6d748f 100644 --- a/gui/utils/anim.py +++ b/gui/utils/anim.py @@ -53,11 +53,11 @@ class LoadAnimation(wx.Window): def OnPaint(self, event): rect = self.GetClientRect() dc = wx.BufferedPaintDC(self) - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) dc.SetBackground(wx.Brush(windowColor)) dc.Clear() - barColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + barColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) shadeColor = colorUtils.GetSuitableColor(barColor, 0.75) barWidth = rect.width / self.bars @@ -81,7 +81,7 @@ class LoadAnimation(wx.Window): dc.DrawRectangle(x, y, barWidth, bh) x += barWidth - textColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + textColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) dc.SetTextForeground(textColor) dc.DrawLabel(self.label, rect, wx.ALIGN_CENTER) From 9d75dea31aefa204f7146bbae890fefdd646ea93 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 21:14:45 -0400 Subject: [PATCH 011/212] "Fix" more spacers --- gui/builtinPreferenceViews/dummyView.py | 3 +-- gui/builtinPreferenceViews/pyfaDatabasePreferences.py | 2 +- gui/builtinPreferenceViews/pyfaNetworkPreferences.py | 2 +- gui/builtinPreferenceViews/pyfaUpdatePreferences.py | 4 ++-- gui/builtinShipBrowser/pfListPane.py | 3 +-- gui/builtinStatsViews/resourcesViewFull.py | 4 ++-- gui/builtinViews/implantEditor.py | 4 ++-- gui/preferenceDialog.py | 2 +- gui/updateDialog.py | 4 ++-- 9 files changed, 13 insertions(+), 15 deletions(-) diff --git a/gui/builtinPreferenceViews/dummyView.py b/gui/builtinPreferenceViews/dummyView.py index 43fa34e12..6d964ad78 100644 --- a/gui/builtinPreferenceViews/dummyView.py +++ b/gui/builtinPreferenceViews/dummyView.py @@ -80,8 +80,7 @@ class DummyView(PreferenceView): def initFooter(self, panel): footerSizer = wx.BoxSizer(wx.HORIZONTAL) - footerSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - + footerSizer.AddSpacer(0) self.btnRestore = wx.Button(panel, wx.ID_ANY, "Restore", wx.DefaultPosition, wx.DefaultSize, 0) self.btnRestore.Enable(False) diff --git a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py index 200cbb597..d734d577a 100644 --- a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py +++ b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py @@ -76,7 +76,7 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.m_staticline3, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) btnSizer = wx.BoxSizer(wx.VERTICAL) - btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + btnSizer.AddSpacer(0) self.btnDeleteDamagePatterns = wx.Button(panel, wx.ID_ANY, "Delete All Damage Pattern Profiles", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnDeleteDamagePatterns, 0, wx.ALL, 5) diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index 711d332c4..4b8b620a8 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -139,7 +139,7 @@ class PFNetworkPref(PreferenceView): 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.AddSpacer(0) self.btnApply = wx.Button(panel, wx.ID_ANY, "Apply Proxy Settings", wx.DefaultPosition, wx.DefaultSize, 0) diff --git a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py index 8bb6faad2..b99b9fac2 100644 --- a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py +++ b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py @@ -52,11 +52,11 @@ class PFUpdatePref(PreferenceView): "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(0) 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.AddSpacer(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) diff --git a/gui/builtinShipBrowser/pfListPane.py b/gui/builtinShipBrowser/pfListPane.py index 752417729..cbdebff05 100644 --- a/gui/builtinShipBrowser/pfListPane.py +++ b/gui/builtinShipBrowser/pfListPane.py @@ -23,8 +23,7 @@ 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, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL) self._wList = [] self._wCount = 0 diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index 3983d19d5..959127944 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -101,7 +101,7 @@ class ResourcesViewFull(StatsView): panel = "full" base = sizerResources - sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + sizer.AddSpacer(0) # Turrets & launcher hardslots display tooltipText = {"turret": "Turret hardpoints", "launcher": "Launcher hardpoints", "drones": "Drones active", "fighter": "Fighter squadrons active", "calibration": "Calibration"} @@ -133,7 +133,7 @@ class ResourcesViewFull(StatsView): # 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": - sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + sizer.AddSpacer(0) # PG, Cpu & drone stuff tooltipText = {"cpu": "CPU", "pg": "PowerGrid", "droneBay": "Drone bay", "fighterBay": "Fighter bay", diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index 245f4471f..a79587f04 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -54,7 +54,7 @@ class BaseImplantEditorView(wx.Panel): pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) buttonSizer = wx.BoxSizer(wx.VERTICAL) - buttonSizer.AddSpacer((0, 0), 1) + buttonSizer.AddSpacer(0) self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style=wx.BORDER_NONE) @@ -64,7 +64,7 @@ class BaseImplantEditorView(wx.Panel): style=wx.BORDER_NONE) buttonSizer.Add(self.btnRemove, 0) - buttonSizer.AddSpacer((0, 0), 1) + buttonSizer.AddSpacer(0) pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 0) characterImplantSizer = wx.BoxSizer(wx.VERTICAL) diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index 13850567f..f442ff5db 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -46,7 +46,7 @@ class PreferenceDialog(wx.Dialog): mainSizer.Add(self.m_staticline2, 0, wx.EXPAND, 5) btnSizer = wx.BoxSizer(wx.HORIZONTAL) - btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + btnSizer.AddSpacer(0) self.btnOK = wx.Button(self, wx.ID_ANY, "OK", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnOK, 0, wx.ALL, 5) mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 019901819..4e9281a90 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -70,10 +70,10 @@ class UpdateDialog(wx.Dialog): 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.AddSpacer(15) mainSizer.Add(versionSizer, 0, wx.EXPAND, 5) - mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) + mainSizer.AddSpacer(0) releaseDate = dateutil.parser.parse(self.releaseInfo['published_at']) notesSizer = wx.BoxSizer(wx.HORIZONTAL) From 0e7dccccfe12079db4ff2383d343d4a07a3bbd85 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 21:16:21 -0400 Subject: [PATCH 012/212] Remove stat panels for now. I HAVE A FRAME SPAWNING OMG --- gui/mainFrame.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 2a79ca1c0..d264a33d8 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -198,15 +198,16 @@ class MainFrame(wx.Frame): self.fitting_additions_split.SetSashPosition(self.fittingHeight) self.fitting_additions_split.SetSashGravity(1.0) - cstatsSizer = wx.BoxSizer(wx.VERTICAL) - - self.charSelection = CharacterSelection(self) - cstatsSizer.Add(self.charSelection, 0, wx.EXPAND) - - self.statsPane = StatsPane(self) - cstatsSizer.Add(self.statsPane, 0, wx.EXPAND) - - mainSizer.Add(cstatsSizer, 0, wx.EXPAND) + # @todo pheonix: fix all stats stuff + # cstatsSizer = wx.BoxSizer(wx.VERTICAL) + # + # self.charSelection = CharacterSelection(self) + # cstatsSizer.Add(self.charSelection, 0, wx.EXPAND) + # + # self.statsPane = StatsPane(self) + # cstatsSizer.Add(self.statsPane, 0, wx.EXPAND) + # + # mainSizer.Add(cstatsSizer, 0, wx.EXPAND) self.SetSizer(mainSizer) From 7e86cb0f843f022976499c83f17c01cae7fe1617 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 21:25:07 -0400 Subject: [PATCH 013/212] OMG I HAVE A SHIP BROWSER NOW :3 (replace old instances of GetSuitableColor with GetSuitable) --- gui/builtinShipBrowser/categoryItem.py | 2 +- gui/builtinShipBrowser/fitItem.py | 2 +- gui/builtinShipBrowser/navigationPanel.py | 4 ++-- gui/builtinShipBrowser/raceSelector.py | 4 ++-- gui/builtinShipBrowser/shipItem.py | 2 +- gui/utils/anim.py | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gui/builtinShipBrowser/categoryItem.py b/gui/builtinShipBrowser/categoryItem.py index 14d413e21..66f30016d 100644 --- a/gui/builtinShipBrowser/categoryItem.py +++ b/gui/builtinShipBrowser/categoryItem.py @@ -101,7 +101,7 @@ class CategoryItem(SFBrowserItem): self.UpdateElementsPos(mdc) windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) + textColor = colorUtils.GetSuitable(windowColor, 1) mdc.SetTextForeground(textColor) mdc.DrawBitmap(self.dropShadowBitmap, self.shipBmpx + 1, self.shipBmpy + 1) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index d942acdb1..be3bf0e1d 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -480,7 +480,7 @@ class FitItem(SFItem.SFBrowserItem): rect = self.GetRect() windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) + textColor = colorUtils.GetSuitable(windowColor, 1) mdc.SetTextForeground(textColor) diff --git a/gui/builtinShipBrowser/navigationPanel.py b/gui/builtinShipBrowser/navigationPanel.py index ef55dec9d..69989389b 100644 --- a/gui/builtinShipBrowser/navigationPanel.py +++ b/gui/builtinShipBrowser/navigationPanel.py @@ -212,8 +212,8 @@ class NavigationPanel(SFItem.SFBrowserItem): rect = self.GetRect() windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) - sepColor = colorUtils.GetSuitableColor(windowColor, 0.2) + textColor = colorUtils.GetSuitable(windowColor, 1) + sepColor = colorUtils.GetSuitable(windowColor, 0.2) mdc.SetTextForeground(textColor) diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index 4434541eb..629140f34 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -167,8 +167,8 @@ class RaceSelector(wx.Window): rect = self.GetRect() windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) - # bkColor = colorUtils.GetSuitableColor(windowColor, 0.1) - sepColor = colorUtils.GetSuitableColor(windowColor, 0.2) + # bkColor = colorUtils.GetSuitable(windowColor, 0.1) + sepColor = colorUtils.GetSuitable(windowColor, 0.2) mdc = wx.BufferedPaintDC(self) diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py index e4e3902b6..254a141c0 100644 --- a/gui/builtinShipBrowser/shipItem.py +++ b/gui/builtinShipBrowser/shipItem.py @@ -233,7 +233,7 @@ class ShipItem(SFItem.SFBrowserItem): # rect = self.GetRect() windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) + textColor = colorUtils.GetSuitable(windowColor, 1) mdc.SetTextForeground(textColor) diff --git a/gui/utils/anim.py b/gui/utils/anim.py index 59f6d748f..8791b273d 100644 --- a/gui/utils/anim.py +++ b/gui/utils/anim.py @@ -58,7 +58,7 @@ class LoadAnimation(wx.Window): dc.Clear() barColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) - shadeColor = colorUtils.GetSuitableColor(barColor, 0.75) + shadeColor = colorUtils.GetSuitable(barColor, 0.75) barWidth = rect.width / self.bars barHeight = rect.height - self.padding * 2 @@ -72,7 +72,7 @@ class LoadAnimation(wx.Window): bh = barHeight y = self.padding else: - barColor = colorUtils.GetSuitableColor(barColor, float(self.animCount / 2) / 10) + barColor = colorUtils.GetSuitable(barColor, float(self.animCount / 2) / 10) dc.SetPen(wx.Pen(barColor)) dc.SetBrush(wx.Brush(barColor)) bh = rect.height From 1cbd8ee9014cca1c950be799dc4c5554623531e9 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 12 Jun 2017 21:31:37 -0400 Subject: [PATCH 014/212] Fix some deprecated stuff --- gui/builtinAdditionPanes/boosterView.py | 4 ++-- gui/builtinAdditionPanes/cargoView.py | 6 +++--- gui/builtinAdditionPanes/commandView.py | 6 +++--- gui/builtinAdditionPanes/droneView.py | 6 +++--- gui/builtinAdditionPanes/fighterView.py | 6 +++--- gui/builtinAdditionPanes/projectedView.py | 6 +++--- gui/builtinMarketBrowser/itemView.py | 2 +- gui/builtinShipBrowser/fitItem.py | 4 ++-- gui/builtinShipBrowser/navigationPanel.py | 8 ++++---- gui/builtinShipBrowser/pfBitmapFrame.py | 2 +- gui/builtinShipBrowser/raceSelector.py | 6 +++--- gui/builtinShipBrowser/shipItem.py | 4 ++-- gui/builtinViews/fittingView.py | 6 +++--- gui/mainMenuBar.py | 14 +++++++------- gui/utils/draw.py | 4 ++-- wxthing.py | 2 +- 16 files changed, 43 insertions(+), 43 deletions(-) diff --git a/gui/builtinAdditionPanes/boosterView.py b/gui/builtinAdditionPanes/boosterView.py index c1b4748c8..67b690ba1 100644 --- a/gui/builtinAdditionPanes/boosterView.py +++ b/gui/builtinAdditionPanes/boosterView.py @@ -28,12 +28,12 @@ from gui.utils.staticHelpers import DragDropHelper from service.fit import Fit -class BoosterViewDrop(wx.PyDropTarget): +class BoosterViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(BoosterViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): diff --git a/gui/builtinAdditionPanes/cargoView.py b/gui/builtinAdditionPanes/cargoView.py index 742ff11b1..5ea6c56d6 100644 --- a/gui/builtinAdditionPanes/cargoView.py +++ b/gui/builtinAdditionPanes/cargoView.py @@ -28,12 +28,12 @@ from service.fit import Fit from service.market import Market -class CargoViewDrop(wx.PyDropTarget): +class CargoViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(CargoViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -88,7 +88,7 @@ class CargoView(d.Display): row = event.GetIndex() if row != -1: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "cargo:" + str(row) data.SetText(dataStr) diff --git a/gui/builtinAdditionPanes/commandView.py b/gui/builtinAdditionPanes/commandView.py index db7b916d2..697953995 100644 --- a/gui/builtinAdditionPanes/commandView.py +++ b/gui/builtinAdditionPanes/commandView.py @@ -42,12 +42,12 @@ class DummyEntry(object): self.item = DummyItem(txt) -class CommandViewDrop(wx.PyDropTarget): +class CommandViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(CommandViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -117,7 +117,7 @@ class CommandView(d.Display): def startDrag(self, event): row = event.GetIndex() if row != -1 and isinstance(self.get(row), es_Drone): - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "command:" + str(self.GetItemData(row)) data.SetText(dataStr) diff --git a/gui/builtinAdditionPanes/droneView.py b/gui/builtinAdditionPanes/droneView.py index 7fde0a105..b03426e31 100644 --- a/gui/builtinAdditionPanes/droneView.py +++ b/gui/builtinAdditionPanes/droneView.py @@ -30,12 +30,12 @@ from service.fit import Fit from service.market import Market -class DroneViewDrop(wx.PyDropTarget): +class DroneViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(DroneViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -123,7 +123,7 @@ class DroneView(Display): def startDrag(self, event): row = event.GetIndex() if row != -1: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "drone:" + str(row) data.SetText(dataStr) diff --git a/gui/builtinAdditionPanes/fighterView.py b/gui/builtinAdditionPanes/fighterView.py index 2d1ee4c17..df3c5715b 100644 --- a/gui/builtinAdditionPanes/fighterView.py +++ b/gui/builtinAdditionPanes/fighterView.py @@ -32,12 +32,12 @@ from service.fit import Fit from service.market import Market -class FighterViewDrop(wx.PyDropTarget): +class FighterViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(FighterViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -186,7 +186,7 @@ class FighterDisplay(d.Display): def startDrag(self, event): row = event.GetIndex() if row != -1: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "fighter:" + str(row) data.SetText(dataStr) diff --git a/gui/builtinAdditionPanes/projectedView.py b/gui/builtinAdditionPanes/projectedView.py index 0e4176eed..b209da974 100644 --- a/gui/builtinAdditionPanes/projectedView.py +++ b/gui/builtinAdditionPanes/projectedView.py @@ -47,12 +47,12 @@ class DummyEntry(object): self.item = DummyItem(txt) -class ProjectedViewDrop(wx.PyDropTarget): +class ProjectedViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(ProjectedViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -132,7 +132,7 @@ class ProjectedView(d.Display): def startDrag(self, event): row = event.GetIndex() if row != -1 and isinstance(self.get(row), es_Drone): - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "projected:" + str(self.GetItemData(row)) data.SetText(dataStr) diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py index 71aa5b125..a18565a27 100644 --- a/gui/builtinMarketBrowser/itemView.py +++ b/gui/builtinMarketBrowser/itemView.py @@ -66,7 +66,7 @@ class ItemView(Display): row = self.GetFirstSelected() if row != -1: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "market:" + str(self.active[row].ID) pyfalog.debug("Dragging from market: " + dataStr) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index be3bf0e1d..053d8b595 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -72,9 +72,9 @@ class FitItem(SFItem.SFBrowserItem): self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big", "gui") - img = wx.ImageFromBitmap(self.shipEffBk) + img = self.shipEffBk.ConvertToImage() img = img.Mirror(False) - self.shipEffBkMirrored = wx.BitmapFromImage(img) + self.shipEffBkMirrored = wx.Bitmap(img) self.dragTLFBmp = None diff --git a/gui/builtinShipBrowser/navigationPanel.py b/gui/builtinShipBrowser/navigationPanel.py index 69989389b..ff63792b5 100644 --- a/gui/builtinShipBrowser/navigationPanel.py +++ b/gui/builtinShipBrowser/navigationPanel.py @@ -29,11 +29,11 @@ class NavigationPanel(SFItem.SFBrowserItem): switchImg = BitmapLoader.getImage("fit_switch_view_mode_small", "gui") switchImg = switchImg.AdjustChannels(1, 1, 1, 0.4) - self.switchBmpD = wx.BitmapFromImage(switchImg) + self.switchBmpD = wx.Bitmap(switchImg) recentImg = BitmapLoader.getImage("frecent_small", "gui") recentImg = recentImg.AdjustChannels(1, 1, 1, 0.4) - self.recentBmpD = wx.BitmapFromImage(recentImg) + self.recentBmpD = wx.Bitmap(recentImg) self.resetBmp = self.AdjustChannels(self.resetBmpH) self.rewBmp = self.AdjustChannels(self.rewBmpH) @@ -184,9 +184,9 @@ class NavigationPanel(SFItem.SFBrowserItem): @staticmethod def AdjustChannels(bitmap): - img = wx.ImageFromBitmap(bitmap) + img = bitmap.ConvertToImage() img = img.AdjustChannels(1.05, 1.05, 1.05, 1) - return wx.BitmapFromImage(img) + return wx.Bitmap(img) def UpdateElementsPos(self, mdc): rect = self.GetRect() diff --git a/gui/builtinShipBrowser/pfBitmapFrame.py b/gui/builtinShipBrowser/pfBitmapFrame.py index e05e643fc..ad09bf337 100644 --- a/gui/builtinShipBrowser/pfBitmapFrame.py +++ b/gui/builtinShipBrowser/pfBitmapFrame.py @@ -7,7 +7,7 @@ class PFBitmapFrame(wx.Frame): style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) img = bitmap.ConvertToImage() img = img.ConvertToGreyscale() - bitmap = wx.BitmapFromImage(img) + bitmap = wx.Bitmap(img) self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) self.Bind(wx.EVT_PAINT, self.OnWindowPaint) diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index 629140f34..b1231fe6c 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -70,7 +70,7 @@ class RaceSelector(wx.Window): if layout == wx.VERTICAL: img = img.Scale(self.minWidth, 8, wx.IMAGE_QUALITY_HIGH) - self.bmpArrow = wx.BitmapFromImage(img) + self.bmpArrow = wx.Bitmap(img) self.RebuildRaces(self.shipBrowser.RACE_ORDER) @@ -184,12 +184,12 @@ class RaceSelector(wx.Window): if self.shipBrowser.GetRaceFilterState(self.raceNames[self.raceBmps.index(raceBmp)]): bmp = raceBmp else: - img = wx.ImageFromBitmap(raceBmp) + img = raceBmp.ConvertToImage() if self.hoveredItem == self.raceBmps.index(raceBmp): img = img.AdjustChannels(1, 1, 1, 0.7) else: img = img.AdjustChannels(1, 1, 1, 0.4) - bmp = wx.BitmapFromImage(img) + bmp = wx.Bitmap(img) if self.layout == wx.VERTICAL: mdc.DrawBitmap(dropShadow, rect.width - self.buttonsPadding - bmp.GetWidth() + 1, y + 1) diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py index 254a141c0..8f52c64ee 100644 --- a/gui/builtinShipBrowser/shipItem.py +++ b/gui/builtinShipBrowser/shipItem.py @@ -50,9 +50,9 @@ class ShipItem(SFItem.SFBrowserItem): self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big", "gui") - img = wx.ImageFromBitmap(self.shipEffBk) + img = self.shipEffBk.ConvertToImage() img = img.Mirror(False) - self.shipEffBkMirrored = wx.BitmapFromImage(img) + self.shipEffBkMirrored = wx.Bitmap(img) self.raceBmp = BitmapLoader.getBitmap("race_%s_small" % self.shipRace, "gui") diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 7734cf634..01e4230b1 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -102,12 +102,12 @@ FitSpawner.register() # Drag'n'drop handler -class FittingViewDrop(wx.PyDropTarget): +class FittingViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(FittingViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -237,7 +237,7 @@ class FittingView(d.Display): row = event.GetIndex() if row != -1 and row not in self.blanks and isinstance(self.mods[row], Module) and not self.mods[row].isEmpty: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "fitting:" + str(self.mods[row].modPosition) data.SetText(dataStr) diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 128cb23fb..1cd680f63 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -107,23 +107,23 @@ class MainMenuBar(wx.MenuBar): charEditItem = wx.MenuItem(windowMenu, self.characterEditorId, "&Character Editor\tCTRL+E") charEditItem.SetBitmap(BitmapLoader.getBitmap("character_small", "gui")) - windowMenu.AppendItem(charEditItem) + windowMenu.Append(charEditItem) damagePatternEditItem = wx.MenuItem(windowMenu, self.damagePatternEditorId, "Damage Pattern Editor\tCTRL+D") damagePatternEditItem.SetBitmap(BitmapLoader.getBitmap("damagePattern_small", "gui")) - windowMenu.AppendItem(damagePatternEditItem) + windowMenu.Append(damagePatternEditItem) targetResistsEditItem = wx.MenuItem(windowMenu, self.targetResistsEditorId, "Target Resists Editor\tCTRL+R") targetResistsEditItem.SetBitmap(BitmapLoader.getBitmap("explosive_small", "gui")) - windowMenu.AppendItem(targetResistsEditItem) + windowMenu.Append(targetResistsEditItem) implantSetEditItem = wx.MenuItem(windowMenu, self.implantSetEditorId, "Implant Set Editor\tCTRL+I") implantSetEditItem.SetBitmap(BitmapLoader.getBitmap("hardwire_small", "gui")) - windowMenu.AppendItem(implantSetEditItem) + windowMenu.Append(implantSetEditItem) graphFrameItem = wx.MenuItem(windowMenu, self.graphFrameId, "Graphs\tCTRL+G") graphFrameItem.SetBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) - windowMenu.AppendItem(graphFrameItem) + windowMenu.Append(graphFrameItem) if not gui.graphFrame.graphFrame_enabled: self.Enable(self.graphFrameId, False) @@ -131,7 +131,7 @@ class MainMenuBar(wx.MenuBar): preferencesShortCut = "CTRL+," if 'wxMac' in wx.PlatformInfo else "CTRL+P" preferencesItem = wx.MenuItem(windowMenu, wx.ID_PREFERENCES, "Preferences\t" + preferencesShortCut) preferencesItem.SetBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) - windowMenu.AppendItem(preferencesItem) + windowMenu.Append(preferencesItem) if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): self.sCrest = Crest.getInstance() @@ -154,7 +154,7 @@ class MainMenuBar(wx.MenuBar): windowMenu.AppendSeparator() attrItem = wx.MenuItem(windowMenu, self.attrEditorId, "Attribute Overrides\tCTRL+B") attrItem.SetBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) - windowMenu.AppendItem(attrItem) + windowMenu.Append(attrItem) windowMenu.Append(self.toggleOverridesId, "Turn Overrides On") # Help menu diff --git a/gui/utils/draw.py b/gui/utils/draw.py index 70a4dcd5c..7079e4058 100644 --- a/gui/utils/draw.py +++ b/gui/utils/draw.py @@ -84,6 +84,6 @@ def GetPartialText(dc, text , maxWidth, defEllipsis="..."): def CreateDropShadowBitmap(bitmap, opacity): - img = wx.ImageFromBitmap(bitmap) + img = bitmap.ConvertToImage() img = img.AdjustChannels(0, 0, 0, opacity) - return wx.BitmapFromImage(img) + return wx.Bitmap(img) diff --git a/wxthing.py b/wxthing.py index bdcbaf616..7623a2d42 100644 --- a/wxthing.py +++ b/wxthing.py @@ -133,7 +133,7 @@ class MainWindow(wx.Frame): def OnDragInit(self, event): """ Begin a Drag Operation """ # Create a Text Data Object, which holds the text that is to be dragged - tdo = wx.PyTextDataObject(self.text.GetStringSelection()) + tdo = wx.TextDataObject(self.text.GetStringSelection()) # Create a Drop Source Object, which enables the Drag operation tds = wx.DropSource(self.text) # Associate the Data to be dragged with the Drop Source Object From 2bbcd96ce35466846ac98a26ff4ce0e134852b65 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Tue, 13 Jun 2017 15:01:30 -0400 Subject: [PATCH 015/212] Even more fixes --- gui/builtinAdditionPanes/droneView.py | 2 +- gui/builtinAdditionPanes/fighterView.py | 2 +- gui/builtinViews/entityEditor.py | 2 +- gui/builtinViews/fittingView.py | 7 ++++--- gui/characterSelection.py | 6 +++--- gui/display.py | 2 +- gui/mainFrame.py | 14 +++++++------- gui/patternEditor.py | 2 +- gui/resistsEditor.py | 2 +- gui/setEditor.py | 2 +- gui/shipBrowser.py | 2 +- 11 files changed, 22 insertions(+), 21 deletions(-) diff --git a/gui/builtinAdditionPanes/droneView.py b/gui/builtinAdditionPanes/droneView.py index b03426e31..2fd15a1e7 100644 --- a/gui/builtinAdditionPanes/droneView.py +++ b/gui/builtinAdditionPanes/droneView.py @@ -101,7 +101,7 @@ class DroneView(Display): if self.DEFAULT_COLS[col] == "Miscellanea": tooltip = self.activeColumns[col].getToolTip(mod) if tooltip is not None: - self.SetToolTipString(tooltip) + self.SetToolTip(tooltip) else: self.SetToolTip(None) else: diff --git a/gui/builtinAdditionPanes/fighterView.py b/gui/builtinAdditionPanes/fighterView.py index df3c5715b..4291e421d 100644 --- a/gui/builtinAdditionPanes/fighterView.py +++ b/gui/builtinAdditionPanes/fighterView.py @@ -164,7 +164,7 @@ class FighterDisplay(d.Display): if self.DEFAULT_COLS[col] == "Miscellanea": tooltip = self.activeColumns[col].getToolTip(mod) if tooltip is not None: - self.SetToolTipString(tooltip) + self.SetToolTip(tooltip) else: self.SetToolTip(None) else: diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index 13ebb8162..544e7a178 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -60,7 +60,7 @@ class EntityEditor(wx.Panel): btn.SetMinSize(size) btn.SetMaxSize(size) - btn.SetToolTipString("{} {}".format(name.capitalize(), self.entityName)) + btn.SetToolTip("{} {}".format(name.capitalize(), self.entityName)) btn.Bind(wx.EVT_BUTTON, func) setattr(self, "btn%s" % name.capitalize(), btn) self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 01e4230b1..cca20a40b 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -55,7 +55,8 @@ class FitSpawner(gui.multiSwitch.TabSpawner): def fitSelected(self, event): count = -1 - for index, page in enumerate(self.multiSwitch.pages): + # @todo pheonix: _pages is supposed to be private? + for index, page in enumerate(self.multiSwitch._pages): if not isinstance(page, gui.builtinViews.emptyView.BlankPage): # Don't try and process it if it's a blank page. try: if page.activeFitID == event.fitID: @@ -184,7 +185,7 @@ class FittingView(d.Display): mod = self.mods[self.GetItemData(row)] tooltip = self.activeColumns[col].getToolTip(mod) if tooltip is not None: - self.SetToolTipString(tooltip) + self.SetToolTip(tooltip) else: self.SetToolTip(None) else: @@ -610,7 +611,7 @@ class FittingView(d.Display): # update state tooltip tooltip = self.activeColumns[col].getToolTip(self.mods[self.GetItemData(row)]) if tooltip: - self.SetToolTipString(tooltip) + self.SetToolTip(tooltip) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) else: diff --git a/gui/characterSelection.py b/gui/characterSelection.py index c28f8b798..e0ddf7964 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -58,7 +58,7 @@ class CharacterSelection(wx.Panel): self.btnRefresh.SetMinSize(size) self.btnRefresh.SetMaxSize(size) - self.btnRefresh.SetToolTipString("Refresh API") + self.btnRefresh.SetToolTip("Refresh API") self.btnRefresh.Bind(wx.EVT_BUTTON, self.refreshApi) self.btnRefresh.Enable(False) @@ -212,7 +212,7 @@ class CharacterSelection(wx.Panel): if event.fitID is None: self.skillReqsStaticBitmap.SetBitmap(self.cleanSkills) - self.skillReqsStaticBitmap.SetToolTipString("No active fit") + self.skillReqsStaticBitmap.SetToolTip("No active fit") else: sCharacter = Character.getInstance() self.reqs = sCharacter.checkRequirements(fit) @@ -233,7 +233,7 @@ class CharacterSelection(wx.Panel): else: tip += self._buildSkillsTooltip(self.reqs) self.skillReqsStaticBitmap.SetBitmap(self.redSkills) - self.skillReqsStaticBitmap.SetToolTipString(tip.strip()) + self.skillReqsStaticBitmap.SetToolTip(tip.strip()) if newCharID is None: sChar = Character.getInstance() diff --git a/gui/display.py b/gui/display.py index 0006ebc84..173b5fa40 100644 --- a/gui/display.py +++ b/gui/display.py @@ -134,7 +134,7 @@ class Display(wx.ListCtrl): items_rect = wx.Rect(topRect.left, 0, bottomRect.right - topRect.left, bottomRect.bottom) updateRegion = wx.Region(x, y, w, h) - updateRegion.SubtractRect(items_rect) + updateRegion.Subtract(items_rect) dc.DestroyClippingRegion() dc.SetClippingRegionAsRegion(updateRegion) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index d264a33d8..fd49c9b00 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -198,16 +198,16 @@ class MainFrame(wx.Frame): self.fitting_additions_split.SetSashPosition(self.fittingHeight) self.fitting_additions_split.SetSashGravity(1.0) + cstatsSizer = wx.BoxSizer(wx.VERTICAL) + + self.charSelection = CharacterSelection(self) + cstatsSizer.Add(self.charSelection, 0, wx.EXPAND) + # @todo pheonix: fix all stats stuff - # cstatsSizer = wx.BoxSizer(wx.VERTICAL) - # - # self.charSelection = CharacterSelection(self) - # cstatsSizer.Add(self.charSelection, 0, wx.EXPAND) - # # self.statsPane = StatsPane(self) # cstatsSizer.Add(self.statsPane, 0, wx.EXPAND) - # - # mainSizer.Add(cstatsSizer, 0, wx.EXPAND) + + mainSizer.Add(cstatsSizer, 0, wx.EXPAND) self.SetSizer(mainSizer) diff --git a/gui/patternEditor.py b/gui/patternEditor.py index 736815fb2..ae27bf924 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -179,7 +179,7 @@ class DmgPatternEditorDlg(wx.Dialog): btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) + btn.SetToolTip("%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()))) diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index 34737b1af..0e88a10a4 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -170,7 +170,7 @@ class ResistsEditorDlg(wx.Dialog): btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) + btn.SetToolTip("%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()))) diff --git a/gui/setEditor.py b/gui/setEditor.py index 1a9295dea..e8a17c9a3 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -157,7 +157,7 @@ class ImplantSetEditorDlg(wx.Dialog): btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction)) + btn.SetToolTip("%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/shipBrowser.py b/gui/shipBrowser.py index 5f21f489a..5645989f1 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -50,7 +50,7 @@ class ShipBrowser(wx.Panel): if race: self.racesFilter[race] = False - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) From 050f1b4addfdcafb33757078964026ab9efe9d9e Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Tue, 13 Jun 2017 15:18:40 -0400 Subject: [PATCH 016/212] Fixed some missing chrome tab stuff, as well as more conversion issues --- eos/effectHandlerHelpers.py | 20 ++++++++++---------- eos/saveddata/fit.py | 4 ++-- gui/builtinViews/fittingView.py | 15 ++++++++------- gui/chrome_tabs.py | 19 +++++++++++++++++++ 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py index ccd620367..9d86e51e3 100644 --- a/eos/effectHandlerHelpers.py +++ b/eos/effectHandlerHelpers.py @@ -26,7 +26,7 @@ class HandledList(list): def filteredItemPreAssign(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.preAssignItemAttr(*args, **kwargs) except AttributeError: pass @@ -34,7 +34,7 @@ class HandledList(list): def filteredItemIncrease(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.increaseItemAttr(*args, **kwargs) except AttributeError: pass @@ -42,7 +42,7 @@ class HandledList(list): def filteredItemMultiply(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.multiplyItemAttr(*args, **kwargs) except AttributeError: pass @@ -50,7 +50,7 @@ class HandledList(list): def filteredItemBoost(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.boostItemAttr(*args, **kwargs) except AttributeError: pass @@ -58,7 +58,7 @@ class HandledList(list): def filteredItemForce(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.forceItemAttr(*args, **kwargs) except AttributeError: pass @@ -66,7 +66,7 @@ class HandledList(list): def filteredChargePreAssign(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.preAssignChargeAttr(*args, **kwargs) except AttributeError: pass @@ -74,7 +74,7 @@ class HandledList(list): def filteredChargeIncrease(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.increaseChargeAttr(*args, **kwargs) except AttributeError: pass @@ -82,7 +82,7 @@ class HandledList(list): def filteredChargeMultiply(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.multiplyChargeAttr(*args, **kwargs) except AttributeError: pass @@ -90,7 +90,7 @@ class HandledList(list): def filteredChargeBoost(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.boostChargeAttr(*args, **kwargs) except AttributeError: pass @@ -98,7 +98,7 @@ class HandledList(list): def filteredChargeForce(self, filter, *args, **kwargs): for element in self: try: - if list(filter(element)): + if filter(element): element.forceChargeAttr(*args, **kwargs) except AttributeError: pass diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 56386ca00..fdab15b63 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -1488,9 +1488,9 @@ class Fit(object): def __repr__(self): return "Fit(ID={}, ship={}, name={}) at {}".format( self.ID, self.ship.item.name, self.name, hex(id(self)) - ).encode('utf8') + ) def __str__(self): return "{} ({})".format( self.name, self.ship.item.name - ).encode('utf8') + ) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index cca20a40b..25a228bbb 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -678,13 +678,14 @@ class FittingView(d.Display): pyfalog.critical(e) def OnShow(self, event): - if event.GetShow(): - try: - self.MakeSnapshot() - except Exception as e: - pyfalog.critical("Failed to make snapshot") - pyfalog.critical(e) - event.Skip() + pass + # if event.Show(): + # try: + # self.MakeSnapshot() + # except Exception as e: + # pyfalog.critical("Failed to make snapshot") + # pyfalog.critical(e) + # event.Skip() def Snapshot(self): return self.FVsnapshot diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index af98c7d09..e65719867 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -222,6 +222,18 @@ class ChromeNotebook(wx.Panel): self._active_page = win self.ShowActive(True) + def DisablePage(self, page, toggle): + idx = self.GetPageIndex(page) + + if toggle and page == self._active_page: + try: + # Set page to the first non-disabled page + self.SetSelection(next(i for i, _ in enumerate(self._pages) if not self.tabs_container.tabs[i].disabled)) + except StopIteration: + self.SetSelection(0) + + self.tabs_container.DisableTab(idx, toggle) + def SetSelection(self, page): old_selection = self.GetSelection() if old_selection != page: @@ -848,6 +860,13 @@ class _TabsContainer(wx.Panel): for tab in self.tabs: if self.CheckTabClose(tab, mposx, mposy): return + + def DisableTab(self, tab, disabled=True): + tb_renderer = self.tabs[tab] + tb_renderer.disabled = disabled + + self.AdjustTabsSize() + self.Refresh() def GetSelectedTab(self): for tab in self.tabs: From 2857eff88416fe172239aef01cebe2a88e76ee29 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Tue, 13 Jun 2017 23:57:27 -0400 Subject: [PATCH 017/212] Fix a few None comparison operations... python 3 has changed this. I'm sure there's going to be a lot of these issues popping up. Oh, and now we get a hard crash when opening a fit. Yey! --- eos/saveddata/module.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index a07fe07f5..f0eb42921 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -178,7 +178,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def numShots(self): if self.charge is None: - return None + return -1 if self.__chargeCycles is None and self.charge: numCharges = self.numCharges # Usual ammo like projectiles and missiles @@ -738,13 +738,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def rawCycleTime(self): speed = max( - self.getModifiedItemAttr("speed"), # Most weapons - self.getModifiedItemAttr("duration"), # Most average modules - self.getModifiedItemAttr("durationSensorDampeningBurstProjector"), - self.getModifiedItemAttr("durationTargetIlluminationBurstProjector"), - self.getModifiedItemAttr("durationECMJammerBurstProjector"), - self.getModifiedItemAttr("durationWeaponDisruptionBurstProjector"), - 0, # Return 0 if none of the above are valid + self.getModifiedItemAttr("speed", 0), # Most weapons + self.getModifiedItemAttr("duration", 0), # Most average modules + self.getModifiedItemAttr("durationSensorDampeningBurstProjector", 0), + self.getModifiedItemAttr("durationTargetIlluminationBurstProjector", 0), + self.getModifiedItemAttr("durationECMJammerBurstProjector", 0), + self.getModifiedItemAttr("durationWeaponDisruptionBurstProjector", 0) ) return speed From cf4d0706ae743683a0ef6dd1a9ba280e687c98f3 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 00:09:30 -0400 Subject: [PATCH 018/212] Fix column headers, and disable event posting which was causing crash --- gui/builtinViews/fittingView.py | 3 ++- gui/display.py | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 25a228bbb..a24ac9143 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -313,7 +313,8 @@ class FittingView(d.Display): self.Show(fitID is not None) self.slotsChanged() sFit.switchFit(fitID) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + # @todo pheonix: had to disable this as it was causing a crash at the wxWidgets level. Dunno why, investigate + #wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) event.Skip() diff --git a/gui/display.py b/gui/display.py index 173b5fa40..9b2b161df 100644 --- a/gui/display.py +++ b/gui/display.py @@ -151,11 +151,11 @@ class Display(wx.ListCtrl): def addColumn(self, i, col): self.activeColumns.append(col) info = wx.ListItem() - info.m_mask = col.mask | wx.LIST_MASK_FORMAT | wx.LIST_MASK_WIDTH - info.m_image = col.imageId - info.m_text = col.columnText - info.m_width = -1 - info.m_format = wx.LIST_FORMAT_LEFT + info.SetMask(col.mask | wx.LIST_MASK_FORMAT | wx.LIST_MASK_WIDTH) + info.SetImage(col.imageId) + info.SetText(col.columnText) + info.SetWidth(-1) + info.SetAlign(wx.LIST_FORMAT_LEFT) self.InsertColumn(i, info) col.resized = False if i == 0 and col.size != wx.LIST_AUTOSIZE_USEHEADER: From 43cbdc1e57841160099a07b4427c7d1d793ded83 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 00:46:13 -0400 Subject: [PATCH 019/212] Fix issue with crashing (set weight not working as intended?) --- gui/builtinViews/fittingView.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index a24ac9143..b2cb2fe8a 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -314,7 +314,7 @@ class FittingView(d.Display): self.slotsChanged() sFit.switchFit(fitID) # @todo pheonix: had to disable this as it was causing a crash at the wxWidgets level. Dunno why, investigate - #wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) event.Skip() @@ -662,10 +662,11 @@ class FittingView(d.Display): if isinstance(mod, Rack) and \ sFit.serviceFittingOptions["rackSlots"] and \ sFit.serviceFittingOptions["rackLabels"]: - font.SetWeight(wx.FONTWEIGHT_BOLD) + # @ todo pheonix: SetWeight is causing a hard crash, possibly a wxPython bug? + #font.SetWeight(wx.FONTWEIGHT_BOLD) self.SetItemFont(i, font) else: - font.SetWeight(wx.FONTWEIGHT_NORMAL) + #font.SetWeight(wx.FONTWEIGHT_NORMAL) self.SetItemFont(i, font) self.Thaw() From 8f369daf1e6acec256e1de033505ee8057a834fb Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 00:46:38 -0400 Subject: [PATCH 020/212] InsertStringItem > InsertItem --- gui/crestFittings.py | 2 +- gui/display.py | 4 ++-- gui/itemStats.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 7b2bc4802..758aad624 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -343,7 +343,7 @@ class CrestMgmt(wx.Dialog): self.lcCharacters.DeleteAllItems() for index, char in enumerate(chars): - self.lcCharacters.InsertStringItem(index, char.name) + self.lcCharacters.InsertItem(index, char.name) self.lcCharacters.SetStringItem(index, 1, char.refresh_token) self.lcCharacters.SetItemData(index, char.ID) diff --git a/gui/display.py b/gui/display.py index 9b2b161df..50befc0f6 100644 --- a/gui/display.py +++ b/gui/display.py @@ -219,13 +219,13 @@ class Display(wx.ListCtrl): if listItemCount < stuffItemCount: for i in range(stuffItemCount - listItemCount): - self.InsertStringItem(sys.maxsize, "") + self.InsertItem(sys.maxsize, "") if listItemCount > stuffItemCount: if listItemCount - stuffItemCount > 20 > stuffItemCount: self.DeleteAllItems() for i in range(stuffItemCount): - self.InsertStringItem(sys.maxsize, "") + self.InsertItem(sys.maxsize, "") else: for i in range(listItemCount - stuffItemCount): self.DeleteItem(self.getLastItem()) diff --git a/gui/itemStats.py b/gui/itemStats.py index c22a8f368..7072eecb5 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -670,7 +670,7 @@ class ItemCompare(wx.Panel): self.paramList.SetColumnWidth(len(self.attrs) + 1, 60) for item in self.items: - i = self.paramList.InsertStringItem(sys.maxsize, item.name) + i = self.paramList.InsertItem(sys.maxsize, item.name) for x, attr in enumerate(self.attrs.keys()): if attr in item.attributes: info = self.attrs[attr] @@ -857,7 +857,7 @@ class ItemEffects(wx.Panel): names.sort() for name in names: - index = self.effectList.InsertStringItem(sys.maxsize, name) + index = self.effectList.InsertItem(sys.maxsize, name) if effects[name].isImplemented: if effects[name].activeByDefault: @@ -1439,7 +1439,7 @@ class ItemProperties(wx.Panel): attrName = name.title() value = getattr(self.item, name) - index = self.paramList.InsertStringItem(sys.maxsize, attrName) + index = self.paramList.InsertItem(sys.maxsize, attrName) # index = self.paramList.InsertImageStringItem(sys.maxint, attrName) idNameMap[idCount] = attrName self.paramList.SetItemData(index, idCount) From bec26d5d05acfe109dfb235a06cb93e91c28e967 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 00:53:22 -0400 Subject: [PATCH 021/212] Fix unable to close window --- gui/mainFrame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index fd49c9b00..ab74d0dd7 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -342,7 +342,7 @@ class MainFrame(wx.Frame): # save open fits self.prevOpenFits['pyfaOpenFits'] = [] # clear old list - for page in self.fitMultiSwitch.pages: + for page in self.fitMultiSwitch._pages: m = getattr(page, "getActiveFit", None) if m is not None: self.prevOpenFits['pyfaOpenFits'].append(m()) From 8c5c7fba29e4f072d1ef23ac30f99f36a4566728 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 01:02:27 -0400 Subject: [PATCH 022/212] Remove EVT_ERASE_BACKGROUND events - pretty sure these were there for older versions of wx where the background wasn't erased properly --- gui/builtinMarketBrowser/pfSearchBox.py | 4 -- .../pyfaGaugePreferences.py | 4 -- gui/builtinShipBrowser/pfBitmapFrame.py | 4 -- gui/builtinShipBrowser/raceSelector.py | 4 -- gui/builtinShipBrowser/sfBrowserItem.py | 4 -- gui/chrome_tabs.py | 9 ----- gui/display.py | 38 ------------------- gui/mainFrame.py | 4 -- gui/pyfa_gauge.py | 4 -- gui/utils/anim.py | 4 -- 10 files changed, 79 deletions(-) diff --git a/gui/builtinMarketBrowser/pfSearchBox.py b/gui/builtinMarketBrowser/pfSearchBox.py index b696026f8..57c91cc0e 100644 --- a/gui/builtinMarketBrowser/pfSearchBox.py +++ b/gui/builtinMarketBrowser/pfSearchBox.py @@ -45,7 +45,6 @@ class PFSearchBox(wx.Window): 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) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) @@ -187,9 +186,6 @@ class PFSearchBox(wx.Window): self.resized = True self.Refresh() - def OnEraseBk(self, event): - pass - def UpdateElementsPos(self, dc): rect = self.GetRect() diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index c17002590..e5996c666 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -48,10 +48,6 @@ class PFGaugePreview(wx.Window): self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) self.Bind(wx.EVT_TIMER, self.OnTimer) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk) - - def OnEraseBk(self, event): - pass def OnTimer(self, event): if event.GetId() == self.timerID: diff --git a/gui/builtinShipBrowser/pfBitmapFrame.py b/gui/builtinShipBrowser/pfBitmapFrame.py index ad09bf337..2bfd3e28c 100644 --- a/gui/builtinShipBrowser/pfBitmapFrame.py +++ b/gui/builtinShipBrowser/pfBitmapFrame.py @@ -11,7 +11,6 @@ class PFBitmapFrame(wx.Frame): self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) 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) @@ -45,9 +44,6 @@ class PFBitmapFrame(wx.Frame): self.direction = -1 self.timer.Start(5) - def OnWindowEraseBk(self, event): - pass - def OnWindowPaint(self, event): rect = self.GetRect() canvas = wx.EmptyBitmap(rect.width, rect.height) diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index b1231fe6c..6d7270d3e 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -77,7 +77,6 @@ class RaceSelector(wx.Window): self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) self.Bind(wx.EVT_TIMER, self.OnTimer) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnBackgroundErase) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) self.Bind(wx.EVT_MOTION, self.OnMouseMove) @@ -160,9 +159,6 @@ class RaceSelector(wx.Window): self.CalcButtonsBarPos() self.Refresh() - def OnBackgroundErase(self, event): - pass - def OnPaint(self, event): rect = self.GetRect() diff --git a/gui/builtinShipBrowser/sfBrowserItem.py b/gui/builtinShipBrowser/sfBrowserItem.py index 449364ce6..f97ac3de4 100644 --- a/gui/builtinShipBrowser/sfBrowserItem.py +++ b/gui/builtinShipBrowser/sfBrowserItem.py @@ -251,7 +251,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) self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) @@ -293,9 +292,6 @@ class SFBrowserItem(wx.Window): def DrawItem(self, mdc): pass - def OnEraseBackground(self, event): - pass - def OnKeyUp(self, event): pass diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index e65719867..dad6ac182 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -719,7 +719,6 @@ class _TabsContainer(wx.Panel): self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) self.Bind(wx.EVT_PAINT, self.OnPaint) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) self.Bind(wx.EVT_MOTION, self.OnMotion) @@ -1151,9 +1150,6 @@ class _TabsContainer(wx.Panel): mdc.DrawBitmap(bmp, posx, posy, True) - def OnErase(self, event): - pass - def UpdateTabFX(self): """ Updates tab drop shadow bitmap """ self.tab_shadow.SetSize((self.tab_min_width, self.height + 1)) @@ -1303,7 +1299,6 @@ class PFNotebookPagePreview(wx.Frame): self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) 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) @@ -1363,10 +1358,6 @@ class PFNotebookPagePreview(wx.Frame): self.direction = -1 self.timer.Start(10) - - def OnWindowEraseBk(self,event): - pass - def OnWindowPaint(self, event): rect = self.GetRect() canvas = wx.Bitmap(rect.width, rect.height) diff --git a/gui/display.py b/gui/display.py index 50befc0f6..b088a89c8 100644 --- a/gui/display.py +++ b/gui/display.py @@ -37,10 +37,6 @@ class Display(wx.ListCtrl): self.columnsMinWidth = [] self.Bind(wx.EVT_LIST_COL_END_DRAG, self.resizeChecker) self.Bind(wx.EVT_LIST_COL_BEGIN_DRAG, self.resizeSkip) - - if "wxMSW" in wx.PlatformInfo: - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() i = 0 @@ -113,40 +109,6 @@ class Display(wx.ListCtrl): return rowIndex, 0, -1 - 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() - - topItem = self.GetTopItem() - bottomItem = topItem + self.GetCountPerPage() - - if bottomItem >= self.GetItemCount(): - bottomItem = self.GetItemCount() - 1 - - 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) - - updateRegion = wx.Region(x, y, w, h) - updateRegion.Subtract(items_rect) - - dc.DestroyClippingRegion() - dc.SetClippingRegionAsRegion(updateRegion) - - dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID)) - dc.Clear() - - dc.DestroyClippingRegion() - - else: - event.Skip() - # noinspection PyPropertyAccess def addColumn(self, i, col): self.activeColumns.append(col) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index ab74d0dd7..c54bf2969 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -102,14 +102,10 @@ class PFPanel(wx.Panel): 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): diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index ef436d74f..3aa74dc14 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -72,7 +72,6 @@ class PyGauge(wx.Window): self.SetToolTip(self._tooltip) self.Bind(wx.EVT_PAINT, self.OnPaint) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_TIMER, self.OnTimer) self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) @@ -183,9 +182,6 @@ class PyGauge(wx.Window): (self._value, self._max_range if self._max_range > 0.01 else 0)) - def OnEraseBackground(self, event): - pass - def OnPaint(self, event): dc = wx.BufferedPaintDC(self) rect = self.GetClientRect() diff --git a/gui/utils/anim.py b/gui/utils/anim.py index 8791b273d..643a91fc7 100644 --- a/gui/utils/anim.py +++ b/gui/utils/anim.py @@ -18,7 +18,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) @@ -47,9 +46,6 @@ class LoadAnimation(wx.Window): self.Refresh() - def OnEraseBackground(self, event): - pass - def OnPaint(self, event): rect = self.GetClientRect() dc = wx.BufferedPaintDC(self) From 4b8f2ce9e76855f194440cd84b9177f61a3c455c Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 01:03:59 -0400 Subject: [PATCH 023/212] Deprecation stuff --- gui/builtinMarketBrowser/itemView.py | 2 +- gui/builtinMarketBrowser/marketTree.py | 8 ++++---- .../pyfaEnginePreferences.py | 2 +- .../pyfaGeneralPreferences.py | 2 +- gui/builtinShipBrowser/raceSelector.py | 6 +++--- gui/builtinShipBrowser/sfBrowserItem.py | 4 ++-- gui/builtinViews/implantEditor.py | 12 ++++++------ gui/characterEditor.py | 16 ++++++++-------- gui/crestFittings.py | 6 +++--- gui/itemStats.py | 4 ++-- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py index a18565a27..52abea877 100644 --- a/gui/builtinMarketBrowser/itemView.py +++ b/gui/builtinMarketBrowser/itemView.py @@ -103,7 +103,7 @@ class ItemView(Display): 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) + seldata = self.marketView.GetItemData(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 diff --git a/gui/builtinMarketBrowser/marketTree.py b/gui/builtinMarketBrowser/marketTree.py index 1658da2c5..06b74d88a 100644 --- a/gui/builtinMarketBrowser/marketTree.py +++ b/gui/builtinMarketBrowser/marketTree.py @@ -24,7 +24,7 @@ class MarketTree(wx.TreeCtrl): 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)) + childId = self.AppendItem(self.root, mktGrp.name, iconId, data=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") @@ -32,7 +32,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=RECENTLY_USED_MODULES) # Bind our lookup method to when the tree gets expanded self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) @@ -52,14 +52,14 @@ class MarketTree(wx.TreeCtrl): self.Delete(child) # And add real market group contents sMkt = self.sMkt - currentMktGrp = sMkt.getMarketGroup(self.GetPyData(root), eager="children") + currentMktGrp = sMkt.getMarketGroup(self.GetItemData(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)) + childId = self.AppendItem(root, childMktGrp.name, iconId, data=childMktGrp.ID) except Exception as e: pyfalog.debug("Error appending item.") pyfalog.debug(e) diff --git a/gui/builtinPreferenceViews/pyfaEnginePreferences.py b/gui/builtinPreferenceViews/pyfaEnginePreferences.py index c4d8c7f46..815e1b124 100644 --- a/gui/builtinPreferenceViews/pyfaEnginePreferences.py +++ b/gui/builtinPreferenceViews/pyfaEnginePreferences.py @@ -24,7 +24,7 @@ class PFFittingEnginePref(PreferenceView): mainSizer = wx.BoxSizer(wx.VERTICAL) - helpCursor = wx.StockCursor(wx.CURSOR_QUESTION_ARROW) + helpCursor = wx.Cursor(wx.CURSOR_QUESTION_ARROW) self.engine_settings = EOSSettings.getInstance() diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index 23a1d67a4..16bc23832 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -21,7 +21,7 @@ class PFGeneralPref(PreferenceView): self.openFitsSettings = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) - helpCursor = wx.StockCursor(wx.CURSOR_QUESTION_ARROW) + helpCursor = wx.Cursor(wx.CURSOR_QUESTION_ARROW) mainSizer = wx.BoxSizer(wx.VERTICAL) diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index 6d7270d3e..404c4123b 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -92,9 +92,9 @@ class RaceSelector(wx.Window): self.hoveredItem = location self.Refresh() if location is not None: - self.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) + self.SetCursor(wx.Cursor(wx.CURSOR_HAND)) else: - self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) def OnSizeUpdate(self, event): self.CalcButtonsBarPos() @@ -253,7 +253,7 @@ class RaceSelector(wx.Window): def OnWindowLeave(self, event): if self.hoveredItem is not None: self.hoveredItem = None - self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) self.Refresh() if not self.animate: diff --git a/gui/builtinShipBrowser/sfBrowserItem.py b/gui/builtinShipBrowser/sfBrowserItem.py index f97ac3de4..57979ae12 100644 --- a/gui/builtinShipBrowser/sfBrowserItem.py +++ b/gui/builtinShipBrowser/sfBrowserItem.py @@ -122,7 +122,7 @@ class PFToolbar(object): if not state & BTN_HOVER: button.SetState(state | BTN_HOVER) self.hoverLabel = button.GetLabel() - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) + self.Parent.SetCursor(wx.Cursor(wx.CURSOR_HAND)) doRefresh = True else: if state & BTN_HOVER: @@ -133,7 +133,7 @@ class PFToolbar(object): bx += bwidth + self.padding if not changeCursor: - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self.Parent.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) return doRefresh def MouseClick(self, event): diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index a79587f04..d950dd6c7 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -79,7 +79,7 @@ class BaseImplantEditorView(wx.Panel): 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)) + childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=mktGrp.ID) if sMkt.marketGroupHasTypesCheck(mktGrp) is False: self.availableImplantsTree.AppendItem(childId, "dummy") @@ -142,10 +142,10 @@ class BaseImplantEditorView(wx.Panel): # if the dummy item is a market group, replace with actual market groups if text == "dummy": # Add 'real stoof!' instead - currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent), eager="children") + currentMktGrp = sMkt.getMarketGroup(tree.GetItemData(parent), eager="children") for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(childMktGrp)) - childId = tree.AppendItem(parent, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) + childId = tree.AppendItem(parent, childMktGrp.name, iconId, data=childMktGrp.ID) if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: tree.AppendItem(childId, "dummy") else: @@ -153,11 +153,11 @@ class BaseImplantEditorView(wx.Panel): # replace dummy with actual items if text == "itemdummy": - currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent)) + currentMktGrp = sMkt.getMarketGroup(tree.GetItemData(parent)) items = sMkt.getItemsByMarketGroup(currentMktGrp) for item in items: iconId = self.addMarketViewImage(item.icon.iconFile) - tree.AppendItem(parent, item.name, iconId, data=wx.TreeItemData(item)) + tree.AppendItem(parent, item.name, iconId, data=item) tree.SortChildren(parent) @@ -185,7 +185,7 @@ class BaseImplantEditorView(wx.Panel): nchilds = self.availableImplantsTree.GetChildrenCount(root) if nchilds == 0: - item = self.availableImplantsTree.GetPyData(root) + item = self.availableImplantsTree.GetItemData(root) self.addImplantToContext(item) else: event.Skip() diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 0ab0bde02..dc4bbe9b4 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -414,7 +414,7 @@ class SkillTreeView(wx.Panel): for id, name in sChar.getSkillsByName(search): iconId = self.skillBookImageId - childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(('skill', id))) + childId = tree.AppendItem(root, name, iconId, data=('skill', id)) level, dirty = sChar.getSkillLevel(char.ID, id) tree.SetItemText(childId, "Level %d" % int(level) if isinstance(level, float) else level, 1) if dirty: @@ -460,10 +460,10 @@ class SkillTreeView(wx.Panel): # Get the real intrestin' stuff sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() - data = tree.GetPyData(root) + data = tree.GetItemData(root) for id, name in sChar.getSkills(data[1]): iconId = self.skillBookImageId - childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(('skill', id))) + childId = tree.AppendItem(root, name, iconId, data=('skill', id)) level, dirty = sChar.getSkillLevel(char.ID, id) tree.SetItemText(childId, "Level %d" % int(level) if isinstance(level, float) else level, 1) if dirty: @@ -482,7 +482,7 @@ class SkillTreeView(wx.Panel): char = self.charEditor.entityEditor.getActiveEntity() sMkt = Market.getInstance() - id = self.skillTreeListCtrl.GetPyData(item)[1] + id = self.skillTreeListCtrl.GetItemData(item)[1] if char.name not in ("All 0", "All 5"): self.levelChangeMenu.selection = sMkt.getItem(id) self.PopupMenu(self.levelChangeMenu) @@ -496,7 +496,7 @@ class SkillTreeView(wx.Panel): sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() selection = self.skillTreeListCtrl.GetSelection() - dataType, skillID = self.skillTreeListCtrl.GetPyData(selection) + dataType, skillID = self.skillTreeListCtrl.GetItemData(selection) if level is not None: sChar.changeLevel(char.ID, skillID, level, persist=True) @@ -521,7 +521,7 @@ class SkillTreeView(wx.Panel): while child.IsOk(): # child = Skill category - dataType, id = self.skillTreeListCtrl.GetPyData(child) + dataType, id = self.skillTreeListCtrl.GetItemData(child) if dataType == 'skill': _setTreeSkillLevel(child, id) else: @@ -529,7 +529,7 @@ class SkillTreeView(wx.Panel): while grand.IsOk(): if self.skillTreeListCtrl.GetItemText(grand) != "dummy": - _, skillID = self.skillTreeListCtrl.GetPyData(grand) + _, skillID = self.skillTreeListCtrl.GetItemData(grand) _setTreeSkillLevel(grand, skillID) grand, cookie2 = self.skillTreeListCtrl.GetNextChild(child, cookie2) @@ -539,7 +539,7 @@ class SkillTreeView(wx.Panel): dirtyGroups = set([skill.item.group.ID for skill in dirtySkills]) parentID = self.skillTreeListCtrl.GetItemParent(selection) - parent = self.skillTreeListCtrl.GetPyData(parentID) + parent = self.skillTreeListCtrl.GetItemData(parentID) if parent: if parent[1] in dirtyGroups: diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 758aad624..82d49d101 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -158,7 +158,7 @@ class CrestFittings(wx.Frame): selection = self.fitView.fitSelection if not selection: return - data = self.fitTree.fittingsTreeCtrl.GetPyData(selection) + data = self.fitTree.fittingsTreeCtrl.GetItemData(selection) sPort = Port.getInstance() fits = sPort.importFitFromBuffer(data) self.mainFrame._openAfterImport(fits) @@ -168,7 +168,7 @@ class CrestFittings(wx.Frame): selection = self.fitView.fitSelection if not selection: return - data = json.loads(self.fitTree.fittingsTreeCtrl.GetPyData(selection)) + data = json.loads(self.fitTree.fittingsTreeCtrl.GetItemData(selection)) dlg = wx.MessageDialog(self, "Do you really want to delete %s (%s) from EVE?" % (data['name'], data['ship']['name']), @@ -407,7 +407,7 @@ class FittingsTreeView(wx.Panel): def displayFit(self, event): selection = self.fittingsTreeCtrl.GetSelection() - data = self.fittingsTreeCtrl.GetPyData(selection) + data = self.fittingsTreeCtrl.GetItemData(selection) if data is None: event.Skip() diff --git a/gui/itemStats.py b/gui/itemStats.py index 7072eecb5..2a88bb660 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -993,7 +993,7 @@ class ItemAffectedBy(wx.Panel): def spawnMenu(self, item): self.affectedBy.SelectItem(item) - stuff = self.affectedBy.GetPyData(item) + stuff = self.affectedBy.GetItemData(item) # String is set as data when we are dealing with attributes, not stuff containers if stuff is None or isinstance(stuff, str): return @@ -1028,7 +1028,7 @@ class ItemAffectedBy(wx.Panel): self.Freeze() for item in self.treeItems: - change = self.affectedBy.GetPyData(item) + change = self.affectedBy.GetItemData(item) display = self.affectedBy.GetItemText(item) self.affectedBy.SetItemText(item, change) self.affectedBy.SetPyData(item, display) From ba64f75f884a8d6f6b3adc324b3c9a9e0a8f1d85 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 03:44:38 -0400 Subject: [PATCH 024/212] Add some missing features to the gauge (and the background erase event, which apparently is needed to prevent flickering. Still need to access if this is something I need to be concerned about in other areas) --- gui/pyfa_gauge.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index 3aa74dc14..bc05c5ea7 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -73,9 +73,13 @@ class PyGauge(wx.Window): self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_TIMER, self.OnTimer) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) + def OnEraseBackground(self, event): + pass + def OnWindowEnter(self, event): self._show_remaining = True self.Refresh() @@ -99,11 +103,21 @@ class PyGauge(wx.Window): def SetFractionDigits(self, digits): self._fraction_digits = digits + def GetBarGradient(self): - raise NotImplemented() + if self._bar_gradient is None: + return None + + return self._bar_gradient[0] def SetBarGradient(self, gradient=None): - raise NotImplemented() + if gradient is None: + self._bar_gradient = None + else: + if not isinstance(gradient, list): + self._bar_gradient = [gradient] + else: + self._bar_gradient = list(gradient) def GetBorderPadding(self) -> int: return self._border_padding @@ -284,6 +298,7 @@ class PyGauge(wx.Window): dc.DrawBitmap(gradient_bitmap, r.left, r.top) # font stuff begins here + print(self.font) dc.SetFont(self.font) # determine shadow position From 3b546de0705421877b42f901beb815ae079d5813 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 03:51:19 -0400 Subject: [PATCH 025/212] More deprecated fixes, and re-enable a working stats view --- gui/builtinAdditionPanes/cargoView.py | 4 ++-- gui/builtinContextMenus/itemStats.py | 2 +- gui/builtinShipBrowser/categoryItem.py | 2 +- gui/builtinShipBrowser/fitItem.py | 2 +- gui/builtinShipBrowser/pfBitmapFrame.py | 2 +- gui/builtinStatsViews/capacitorViewFull.py | 4 ++-- gui/builtinStatsViews/firepowerViewFull.py | 2 +- gui/builtinStatsViews/miningyieldViewFull.py | 2 +- gui/builtinStatsViews/outgoingViewFull.py | 2 +- gui/builtinStatsViews/outgoingViewMinimal.py | 2 +- gui/builtinStatsViews/priceViewFull.py | 2 +- gui/builtinStatsViews/priceViewMinimal.py | 2 +- gui/builtinStatsViews/rechargeViewFull.py | 2 +- gui/builtinStatsViews/resistancesViewFull.py | 5 ++++- gui/builtinStatsViews/resourcesViewFull.py | 5 ++++- gui/builtinStatsViews/targetingMiscViewFull.py | 6 +++--- gui/builtinStatsViews/targetingMiscViewMinimal.py | 6 +++--- gui/builtinViews/fittingView.py | 8 ++++---- gui/graphFrame.py | 2 +- gui/mainFrame.py | 4 ++-- gui/marketBrowser.py | 2 +- gui/pyfa_gauge.py | 3 +-- 22 files changed, 38 insertions(+), 33 deletions(-) diff --git a/gui/builtinAdditionPanes/cargoView.py b/gui/builtinAdditionPanes/cargoView.py index 5ea6c56d6..91b566283 100644 --- a/gui/builtinAdditionPanes/cargoView.py +++ b/gui/builtinAdditionPanes/cargoView.py @@ -119,14 +119,14 @@ class CargoView(d.Display): module = fit.modules[modIdx] if dstRow != -1: # we're swapping with cargo - if mstate.CmdDown(): # if copying, append to cargo + if mstate.cmdDown: # if copying, append to cargo sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) else: # else, move / swap sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, dstRow) else: # dragging to blank spot, append sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) - if not mstate.CmdDown(): # if not copying, remove module + 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(), action="moddel", typeID=module.item.ID)) diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index 4e1b8187e..6221fa1e0 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -47,7 +47,7 @@ class ItemStats(ContextMenu): mstate = wx.GetMouseState() reuse = False - if mstate.CmdDown(): + if mstate.cmdDown: reuse = True if self.mainFrame.GetActiveStatsWindow() is None and reuse: diff --git a/gui/builtinShipBrowser/categoryItem.py b/gui/builtinShipBrowser/categoryItem.py index 66f30016d..28ffec3bd 100644 --- a/gui/builtinShipBrowser/categoryItem.py +++ b/gui/builtinShipBrowser/categoryItem.py @@ -20,7 +20,7 @@ class CategoryItem(SFBrowserItem): if categoryID: self.shipBmp = BitmapLoader.getBitmap("ship_small", "gui") else: - self.shipBmp = wx.EmptyBitmap(16, 16) + self.shipBmp = wx.Bitmap(16, 16) self.dropShadowBitmap = drawUtils.CreateDropShadowBitmap(self.shipBmp, 0.2) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index 053d8b595..cd702d980 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -520,7 +520,7 @@ class FitItem(SFItem.SFBrowserItem): self.AdjustControlSizePos(self.tcFitName, self.textStartx, self.toolbarx - self.editWidth - self.padding) tdc = wx.MemoryDC() - self.dragTLFBmp = wx.EmptyBitmap((self.toolbarx if self.toolbarx < 200 else 200), rect.height, 24) + self.dragTLFBmp = wx.Bitmap((self.toolbarx if self.toolbarx < 200 else 200), rect.height, 24) tdc.SelectObject(self.dragTLFBmp) tdc.Blit(0, 0, (self.toolbarx if self.toolbarx < 200 else 200), rect.height, mdc, 0, 0, wx.COPY) tdc.SelectObject(wx.NullBitmap) diff --git a/gui/builtinShipBrowser/pfBitmapFrame.py b/gui/builtinShipBrowser/pfBitmapFrame.py index 2bfd3e28c..618401a4f 100644 --- a/gui/builtinShipBrowser/pfBitmapFrame.py +++ b/gui/builtinShipBrowser/pfBitmapFrame.py @@ -46,7 +46,7 @@ class PFBitmapFrame(wx.Frame): def OnWindowPaint(self, event): rect = self.GetRect() - canvas = wx.EmptyBitmap(rect.width, rect.height) + canvas = wx.Bitmap(rect.width, rect.height) mdc = wx.BufferedPaintDC(self) mdc.SelectObject(canvas) mdc.DrawBitmap(self.bitmap, 0, 0) diff --git a/gui/builtinStatsViews/capacitorViewFull.py b/gui/builtinStatsViews/capacitorViewFull.py index 2c478b163..8ff7d6a13 100644 --- a/gui/builtinStatsViews/capacitorViewFull.py +++ b/gui/builtinStatsViews/capacitorViewFull.py @@ -45,7 +45,7 @@ class CapacitorViewFull(StatsView): panel = "full" - sizerCapacitor = wx.GridSizer(1, 2) + sizerCapacitor = wx.GridSizer(1, 2, 0, 0) contentSizer.Add(sizerCapacitor, 0, wx.EXPAND, 0) # Capacitor capacity and time baseBox = wx.BoxSizer(wx.HORIZONTAL) @@ -91,7 +91,7 @@ class CapacitorViewFull(StatsView): baseBox.Add(bitmap, 0, wx.ALIGN_CENTER) # Recharge - chargeSizer = wx.FlexGridSizer(2, 3) + chargeSizer = wx.FlexGridSizer(2, 3, 0, 0) baseBox.Add(chargeSizer, 0, wx.ALIGN_CENTER) chargeSizer.Add(wx.StaticText(parent, wx.ID_ANY, "+ "), 0, wx.ALIGN_CENTER) diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 33c6092d8..1ab60cce3 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -53,7 +53,7 @@ class FirepowerViewFull(StatsView): panel = "full" - sizerFirepower = wx.FlexGridSizer(1, 4) + sizerFirepower = wx.FlexGridSizer(1, 4, 0, 0) sizerFirepower.AddGrowableCol(1) contentSizer.Add(sizerFirepower, 0, wx.EXPAND, 0) diff --git a/gui/builtinStatsViews/miningyieldViewFull.py b/gui/builtinStatsViews/miningyieldViewFull.py index f35902653..4a4560cef 100644 --- a/gui/builtinStatsViews/miningyieldViewFull.py +++ b/gui/builtinStatsViews/miningyieldViewFull.py @@ -48,7 +48,7 @@ class MiningYieldViewFull(StatsView): panel = "full" - sizerMiningYield = wx.FlexGridSizer(1, 4) + sizerMiningYield = wx.FlexGridSizer(1, 4, 0, 0) sizerMiningYield.AddGrowableCol(1) contentSizer.Add(sizerMiningYield, 0, wx.EXPAND, 0) diff --git a/gui/builtinStatsViews/outgoingViewFull.py b/gui/builtinStatsViews/outgoingViewFull.py index a73363172..be24ee6af 100644 --- a/gui/builtinStatsViews/outgoingViewFull.py +++ b/gui/builtinStatsViews/outgoingViewFull.py @@ -44,7 +44,7 @@ class OutgoingViewFull(StatsView): parent = self.panel = contentPanel self.headerPanel = headerPanel - sizerOutgoing = wx.GridSizer(1, 4) + sizerOutgoing = wx.GridSizer(1, 4, 0, 0) contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0) diff --git a/gui/builtinStatsViews/outgoingViewMinimal.py b/gui/builtinStatsViews/outgoingViewMinimal.py index bb75f689d..21fe9bede 100644 --- a/gui/builtinStatsViews/outgoingViewMinimal.py +++ b/gui/builtinStatsViews/outgoingViewMinimal.py @@ -43,7 +43,7 @@ class OutgoingViewMinimal(StatsView): parent = self.panel = contentPanel self.headerPanel = headerPanel - sizerOutgoing = wx.GridSizer(1, 4) + sizerOutgoing = wx.GridSizer(1, 4, 0, 0) contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0) diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index b8fc25803..d5f8d84ca 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -47,7 +47,7 @@ class PriceViewFull(StatsView): headerContentSizer.Add(self.labelEMStatus) headerPanel.GetParent().AddToggleItem(self.labelEMStatus) - gridPrice = wx.GridSizer(2, 3) + gridPrice = wx.GridSizer(2, 3, 0, 0) contentSizer.Add(gridPrice, 0, wx.EXPAND | wx.ALL, 0) for _type in ("ship", "fittings", "drones", "cargoBay", "character", "total"): if _type in "ship": diff --git a/gui/builtinStatsViews/priceViewMinimal.py b/gui/builtinStatsViews/priceViewMinimal.py index 04746b1a5..85f7eb18b 100644 --- a/gui/builtinStatsViews/priceViewMinimal.py +++ b/gui/builtinStatsViews/priceViewMinimal.py @@ -47,7 +47,7 @@ class PriceViewMinimal(StatsView): headerContentSizer.Add(self.labelEMStatus) headerPanel.GetParent().AddToggleItem(self.labelEMStatus) - gridPrice = wx.GridSizer(1, 3) + gridPrice = wx.GridSizer(1, 3, 0, 0) contentSizer.Add(gridPrice, 0, wx.EXPAND | wx.ALL, 0) for _type in ("ship", "fittings", "total"): image = "%sPrice_big" % _type if _type != "ship" else "ship_big" diff --git a/gui/builtinStatsViews/rechargeViewFull.py b/gui/builtinStatsViews/rechargeViewFull.py index 767172560..eadcf537f 100644 --- a/gui/builtinStatsViews/rechargeViewFull.py +++ b/gui/builtinStatsViews/rechargeViewFull.py @@ -55,7 +55,7 @@ class RechargeViewFull(StatsView): self.panel = contentPanel self.headerPanel = headerPanel - sizerTankStats = wx.FlexGridSizer(3, 5) + sizerTankStats = wx.FlexGridSizer(3, 5, 0, 0) for i in range(4): sizerTankStats.AddGrowableCol(i + 1) diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index 7a6cc6ba9..3168df3b8 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -25,6 +25,7 @@ from gui.pyfa_gauge import PyGauge from gui.utils.numberFormatter import formatAmount import gui.mainFrame import gui.globalEvents as GE +from gui.utils import fonts EffectiveHpToggled, EFFECTIVE_HP_TOGGLED = wx.lib.newevent.NewEvent() @@ -122,6 +123,8 @@ class ResistancesViewFull(StatsView): continue currGColour = 0 + font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + 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) @@ -133,7 +136,7 @@ class ResistancesViewFull(StatsView): bc = pgColour[1] currGColour += 1 - lbl = PyGauge(contentPanel, wx.ID_ANY, 100) + lbl = PyGauge(contentPanel, font, (100, 25)) lbl.SetMinSize((48, 16)) lbl.SetBackgroundColour(wx.Colour(bc[0], bc[1], bc[2])) lbl.SetBarColour(wx.Colour(fc[0], fc[1], fc[2])) diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index 959127944..0f89fd925 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -24,6 +24,7 @@ from gui.bitmap_loader import BitmapLoader from gui.pyfa_gauge import PyGauge import gui.mainFrame from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED +from gui.utils import fonts from eos.saveddata.module import Hardpoint @@ -135,6 +136,8 @@ class ResourcesViewFull(StatsView): if type_ != "drones": sizer.AddSpacer(0) + gauge_font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + # PG, Cpu & drone stuff tooltipText = {"cpu": "CPU", "pg": "PowerGrid", "droneBay": "Drone bay", "fighterBay": "Fighter bay", "droneBandwidth": "Drone bandwidth", "cargoBay": "Cargo bay"} @@ -176,7 +179,7 @@ class ResourcesViewFull(StatsView): # Gauges modif. - Darriele - gauge = PyGauge(parent, wx.ID_ANY, 1) + gauge = PyGauge(parent, gauge_font) gauge.SetValueRange(0, 0) gauge.SetMinSize((self.getTextExtentW("1.999M/1.99M MW"), 23)) gauge.SetFractionDigits(2) diff --git a/gui/builtinStatsViews/targetingMiscViewFull.py b/gui/builtinStatsViews/targetingMiscViewFull.py index 4e7630fc9..23c5efd0e 100644 --- a/gui/builtinStatsViews/targetingMiscViewFull.py +++ b/gui/builtinStatsViews/targetingMiscViewFull.py @@ -48,13 +48,13 @@ class TargetingMiscViewFull(StatsView): self.panel = contentPanel self.headerPanel = headerPanel - gridTargetingMisc = wx.FlexGridSizer(1, 3) + gridTargetingMisc = wx.FlexGridSizer(1, 3, 0, 0) contentSizer.Add(gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0) gridTargetingMisc.AddGrowableCol(0) gridTargetingMisc.AddGrowableCol(2) # Targeting - gridTargeting = wx.FlexGridSizer(5, 2) + gridTargeting = wx.FlexGridSizer(5, 2, 0, 0) gridTargeting.AddGrowableCol(1) gridTargetingMisc.Add(gridTargeting, 0, wx.ALIGN_LEFT | wx.ALL, 5) @@ -79,7 +79,7 @@ class TargetingMiscViewFull(StatsView): # Misc gridTargetingMisc.Add(wx.StaticLine(contentPanel, wx.ID_ANY, style=wx.VERTICAL), 0, wx.EXPAND, 3) - gridMisc = wx.FlexGridSizer(5, 2) + gridMisc = wx.FlexGridSizer(5, 2, 0, 0) gridMisc.AddGrowableCol(1) gridTargetingMisc.Add(gridMisc, 0, wx.ALIGN_LEFT | wx.ALL, 5) diff --git a/gui/builtinStatsViews/targetingMiscViewMinimal.py b/gui/builtinStatsViews/targetingMiscViewMinimal.py index 78664a2e2..fbc819d68 100644 --- a/gui/builtinStatsViews/targetingMiscViewMinimal.py +++ b/gui/builtinStatsViews/targetingMiscViewMinimal.py @@ -48,13 +48,13 @@ class TargetingMiscViewMinimal(StatsView): self.panel = contentPanel self.headerPanel = headerPanel - gridTargetingMisc = wx.FlexGridSizer(1, 3) + gridTargetingMisc = wx.FlexGridSizer(1, 3, 0, 0) contentSizer.Add(gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0) gridTargetingMisc.AddGrowableCol(0) gridTargetingMisc.AddGrowableCol(2) # Targeting - gridTargeting = wx.FlexGridSizer(5, 2) + gridTargeting = wx.FlexGridSizer(5, 2, 0, 0) gridTargeting.AddGrowableCol(1) gridTargetingMisc.Add(gridTargeting, 0, wx.ALIGN_LEFT | wx.ALL, 5) @@ -79,7 +79,7 @@ class TargetingMiscViewMinimal(StatsView): # Misc gridTargetingMisc.Add(wx.StaticLine(contentPanel, wx.ID_ANY, style=wx.VERTICAL), 0, wx.EXPAND, 3) - gridMisc = wx.FlexGridSizer(5, 2) + gridMisc = wx.FlexGridSizer(5, 2, 0, 0) gridMisc.AddGrowableCol(1) gridTargetingMisc.Add(gridMisc, 0, wx.ALIGN_LEFT | wx.ALL, 5) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index b2cb2fe8a..7fa7adabc 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -605,7 +605,7 @@ class FittingView(d.Display): sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - ctrl = wx.GetMouseState().CmdDown() or wx.GetMouseState().MiddleDown() + ctrl = event.cmdDown or event.middleIsDown click = "ctrl" if ctrl is True else "right" if event.GetButton() == 3 else "left" sFit.toggleModulesState(fitID, self.mods[self.GetItemData(row)], mods, click) @@ -698,7 +698,7 @@ class FittingView(d.Display): if self.FVsnapshot: del self.FVsnapshot - tbmp = wx.EmptyBitmap(16, 16) + tbmp = wx.Bitmap(16, 16) tdc = wx.MemoryDC() tdc.SelectObject(tbmp) font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) @@ -774,7 +774,7 @@ class FittingView(d.Display): opts.m_labelText = name if imgId != -1: - opts.m_labelBitmap = wx.EmptyBitmap(isize, isize) + opts.m_labelBitmap = wx.Bitmap(isize, isize) width = render.DrawHeaderButton(self, tdc, (0, 0, 16, 16), sortArrow=wx.HDR_SORT_ICON_NONE, params=opts) @@ -790,7 +790,7 @@ class FittingView(d.Display): maxWidth += columnsWidths[i] mdc = wx.MemoryDC() - mbmp = wx.EmptyBitmap(maxWidth, maxRowHeight * rows + padding * 4 + headerSize) + mbmp = wx.Bitmap(maxWidth, maxRowHeight * rows + padding * 4 + headerSize) mdc.SelectObject(mbmp) diff --git a/gui/graphFrame.py b/gui/graphFrame.py index d8eb5c9ea..83cfd4a6b 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -143,7 +143,7 @@ class GraphFrame(wx.Frame): dummyBox = wx.BoxSizer(wx.VERTICAL) self.gridPanel.SetSizer(dummyBox) - self.gridSizer = wx.FlexGridSizer(0, 4) + self.gridSizer = wx.FlexGridSizer(0, 4, 0, 0) self.gridSizer.AddGrowableCol(1) dummyBox.Add(self.gridSizer, 0, wx.EXPAND) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index c54bf2969..1d9e3130f 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -200,8 +200,8 @@ class MainFrame(wx.Frame): cstatsSizer.Add(self.charSelection, 0, wx.EXPAND) # @todo pheonix: fix all stats stuff - # self.statsPane = StatsPane(self) - # cstatsSizer.Add(self.statsPane, 0, wx.EXPAND) + self.statsPane = StatsPane(self) + cstatsSizer.Add(self.statsPane, 0, wx.EXPAND) mainSizer.Add(cstatsSizer, 0, wx.EXPAND) diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index f097f4a09..c31c98df4 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -79,7 +79,7 @@ class MarketBrowser(wx.Panel): def toggleMetaButton(self, event): """Process clicks on toggle buttons""" - appendMeta = wx.GetMouseState().CmdDown() + appendMeta = event.cmdDown clickedBtn = event.EventObject if appendMeta: diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index bc05c5ea7..fc9ffd321 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -79,7 +79,7 @@ class PyGauge(wx.Window): def OnEraseBackground(self, event): pass - + def OnWindowEnter(self, event): self._show_remaining = True self.Refresh() @@ -298,7 +298,6 @@ class PyGauge(wx.Window): dc.DrawBitmap(gradient_bitmap, r.left, r.top) # font stuff begins here - print(self.font) dc.SetFont(self.font) # determine shadow position From 729c46ab00e4a182c9c919ae31ee88a1e12fe213 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 03:57:57 -0400 Subject: [PATCH 026/212] Revert "Remove EVT_ERASE_BACKGROUND events - pretty sure these were there for older versions of wx where the background wasn't erased properly" This does help prevent flickering on resizing. Need to research more before removing them This reverts commit 8c5c7fba29e4f072d1ef23ac30f99f36a4566728. --- gui/builtinMarketBrowser/pfSearchBox.py | 4 ++++ gui/builtinPreferenceViews/pyfaGaugePreferences.py | 4 ++++ gui/builtinShipBrowser/pfBitmapFrame.py | 4 ++++ gui/builtinShipBrowser/raceSelector.py | 4 ++++ gui/builtinShipBrowser/sfBrowserItem.py | 4 ++++ gui/chrome_tabs.py | 9 +++++++++ gui/display.py | 1 + gui/mainFrame.py | 4 ++++ gui/pyfa_gauge.py | 4 ++++ gui/utils/anim.py | 4 ++++ 10 files changed, 42 insertions(+) diff --git a/gui/builtinMarketBrowser/pfSearchBox.py b/gui/builtinMarketBrowser/pfSearchBox.py index 57c91cc0e..b696026f8 100644 --- a/gui/builtinMarketBrowser/pfSearchBox.py +++ b/gui/builtinMarketBrowser/pfSearchBox.py @@ -45,6 +45,7 @@ class PFSearchBox(wx.Window): 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) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) @@ -186,6 +187,9 @@ class PFSearchBox(wx.Window): self.resized = True self.Refresh() + def OnEraseBk(self, event): + pass + def UpdateElementsPos(self, dc): rect = self.GetRect() diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index e5996c666..c17002590 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -48,6 +48,10 @@ class PFGaugePreview(wx.Window): self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) self.Bind(wx.EVT_TIMER, self.OnTimer) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk) + + def OnEraseBk(self, event): + pass def OnTimer(self, event): if event.GetId() == self.timerID: diff --git a/gui/builtinShipBrowser/pfBitmapFrame.py b/gui/builtinShipBrowser/pfBitmapFrame.py index 618401a4f..8421ee6a7 100644 --- a/gui/builtinShipBrowser/pfBitmapFrame.py +++ b/gui/builtinShipBrowser/pfBitmapFrame.py @@ -11,6 +11,7 @@ class PFBitmapFrame(wx.Frame): self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) 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) @@ -44,6 +45,9 @@ class PFBitmapFrame(wx.Frame): self.direction = -1 self.timer.Start(5) + def OnWindowEraseBk(self, event): + pass + def OnWindowPaint(self, event): rect = self.GetRect() canvas = wx.Bitmap(rect.width, rect.height) diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index 404c4123b..cfc795ea9 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -77,6 +77,7 @@ class RaceSelector(wx.Window): self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) self.Bind(wx.EVT_TIMER, self.OnTimer) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnBackgroundErase) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) self.Bind(wx.EVT_MOTION, self.OnMouseMove) @@ -159,6 +160,9 @@ class RaceSelector(wx.Window): self.CalcButtonsBarPos() self.Refresh() + def OnBackgroundErase(self, event): + pass + def OnPaint(self, event): rect = self.GetRect() diff --git a/gui/builtinShipBrowser/sfBrowserItem.py b/gui/builtinShipBrowser/sfBrowserItem.py index 57979ae12..e0cef1495 100644 --- a/gui/builtinShipBrowser/sfBrowserItem.py +++ b/gui/builtinShipBrowser/sfBrowserItem.py @@ -251,6 +251,7 @@ 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) self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) @@ -292,6 +293,9 @@ class SFBrowserItem(wx.Window): def DrawItem(self, mdc): pass + def OnEraseBackground(self, event): + pass + def OnKeyUp(self, event): pass diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index dad6ac182..e65719867 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -719,6 +719,7 @@ class _TabsContainer(wx.Panel): self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) self.Bind(wx.EVT_MOTION, self.OnMotion) @@ -1150,6 +1151,9 @@ class _TabsContainer(wx.Panel): mdc.DrawBitmap(bmp, posx, posy, True) + def OnErase(self, event): + pass + def UpdateTabFX(self): """ Updates tab drop shadow bitmap """ self.tab_shadow.SetSize((self.tab_min_width, self.height + 1)) @@ -1299,6 +1303,7 @@ class PFNotebookPagePreview(wx.Frame): self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) 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) @@ -1358,6 +1363,10 @@ class PFNotebookPagePreview(wx.Frame): self.direction = -1 self.timer.Start(10) + + def OnWindowEraseBk(self,event): + pass + def OnWindowPaint(self, event): rect = self.GetRect() canvas = wx.Bitmap(rect.width, rect.height) diff --git a/gui/display.py b/gui/display.py index b088a89c8..312f5916b 100644 --- a/gui/display.py +++ b/gui/display.py @@ -37,6 +37,7 @@ class Display(wx.ListCtrl): self.columnsMinWidth = [] self.Bind(wx.EVT_LIST_COL_END_DRAG, self.resizeChecker) self.Bind(wx.EVT_LIST_COL_BEGIN_DRAG, self.resizeSkip) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() i = 0 diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 1d9e3130f..58f49b24e 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -102,10 +102,14 @@ class PFPanel(wx.Panel): 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): diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index fc9ffd321..fbfab3322 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -72,6 +72,7 @@ class PyGauge(wx.Window): self.SetToolTip(self._tooltip) self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_TIMER, self.OnTimer) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) @@ -196,6 +197,9 @@ class PyGauge(wx.Window): (self._value, self._max_range if self._max_range > 0.01 else 0)) + def OnEraseBackground(self, event): + pass + def OnPaint(self, event): dc = wx.BufferedPaintDC(self) rect = self.GetClientRect() diff --git a/gui/utils/anim.py b/gui/utils/anim.py index 643a91fc7..8791b273d 100644 --- a/gui/utils/anim.py +++ b/gui/utils/anim.py @@ -18,6 +18,7 @@ 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) @@ -46,6 +47,9 @@ class LoadAnimation(wx.Window): self.Refresh() + def OnEraseBackground(self, event): + pass + def OnPaint(self, event): rect = self.GetClientRect() dc = wx.BufferedPaintDC(self) From f16e14e0b4e9f946d3eed34161265f7fef407b6e Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Wed, 14 Jun 2017 15:05:12 -0400 Subject: [PATCH 027/212] Fix gauges again, convert remaining IconFromBitmap > Icon --- gui/builtinStatsViews/resistancesViewFull.py | 2 +- gui/builtinStatsViews/resourcesViewFull.py | 2 +- gui/characterEditor.py | 2 +- gui/graphFrame.py | 2 +- gui/itemStats.py | 2 +- gui/preferenceDialog.py | 2 +- gui/propertyEditor.py | 4 ++-- gui/pyfa_gauge.py | 2 -- 8 files changed, 8 insertions(+), 10 deletions(-) diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index 3168df3b8..684a0e617 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -136,7 +136,7 @@ class ResistancesViewFull(StatsView): bc = pgColour[1] currGColour += 1 - lbl = PyGauge(contentPanel, font, (100, 25)) + lbl = PyGauge(contentPanel, font, 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])) diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index 0f89fd925..2ab61cf2b 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -179,7 +179,7 @@ class ResourcesViewFull(StatsView): # Gauges modif. - Darriele - gauge = PyGauge(parent, gauge_font) + gauge = PyGauge(parent, gauge_font, 1) gauge.SetValueRange(0, 0) gauge.SetMinSize((self.getTextExtentW("1.999M/1.99M MW"), 23)) gauge.SetFractionDigits(2) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index dc4bbe9b4..088f63156 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -131,7 +131,7 @@ class CharacterEditor(wx.Frame): wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="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")) + i = wx.Icon(BitmapLoader.getBitmap("character_small", "gui")) self.SetIcon(i) self.mainFrame = parent diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 83cfd4a6b..a0aa52876 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -101,7 +101,7 @@ class GraphFrame(wx.Frame): wx.Frame.__init__(self, parent, title="pyfa: Graph Generator", style=style, size=(520, 390)) - i = wx.IconFromBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) + i = wx.Icon(BitmapLoader.getBitmap("graphs_small", "gui")) self.SetIcon(i) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.CreateStatusBar() diff --git a/gui/itemStats.py b/gui/itemStats.py index 2a88bb660..f26b6fd88 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -98,7 +98,7 @@ class ItemStatsDialog(wx.Dialog): 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.SetIcon(wx.Icon(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 "")) diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index f442ff5db..20277bb58 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -27,7 +27,7 @@ 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") - i = wx.IconFromBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) + i = wx.Icon(BitmapLoader.getBitmap("preferences_small", "gui")) self.SetIcon(i) mainSizer = wx.BoxSizer(wx.VERTICAL) diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index 7cf012f08..798e1453e 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -30,7 +30,7 @@ class AttributeEditor(wx.Frame): 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")) + i = wx.Icon(BitmapLoader.getBitmap("fit_rename_small", "gui")) self.SetIcon(i) self.mainFrame = parent @@ -48,7 +48,7 @@ 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")) + i = wx.Icon(BitmapLoader.getBitmap("fit_rename_small", "gui")) self.SetIcon(i) self.mainFrame = parent diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index fbfab3322..b41cab8d2 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -72,9 +72,7 @@ class PyGauge(wx.Window): self.SetToolTip(self._tooltip) self.Bind(wx.EVT_PAINT, self.OnPaint) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_TIMER, self.OnTimer) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) From f24c2ddd22d02c36ba8976ac0910661de6331c2d Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sun, 2 Jul 2017 14:12:00 -0400 Subject: [PATCH 028/212] Get skill list in character editor showing. Mostly doesn't work still --- gui/characterEditor.py | 75 +++++++++++++++++++++++------------------- gui/contextMenu.py | 2 +- service/character.py | 6 ++-- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 088f63156..55fb10157 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -19,7 +19,7 @@ # noinspection PyPackageRequirements import wx - +import wx.dataview from .utils.floatspin import FloatSpin # noinspection PyPackageRequirements import wx.lib.newevent @@ -150,12 +150,12 @@ class CharacterEditor(wx.Frame): self.viewsNBContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) self.sview = SkillTreeView(self.viewsNBContainer) - self.iview = ImplantEditorView(self.viewsNBContainer) - self.aview = APIView(self.viewsNBContainer) + # self.iview = ImplantEditorView(self.viewsNBContainer) + # self.aview = APIView(self.viewsNBContainer) self.viewsNBContainer.AddPage(self.sview, "Skills") - self.viewsNBContainer.AddPage(self.iview, "Implants") - self.viewsNBContainer.AddPage(self.aview, "API") + # self.viewsNBContainer.AddPage(self.iview, "Implants") + # self.viewsNBContainer.AddPage(self.aview, "API") mainSizer.Add(self.viewsNBContainer, 1, wx.EXPAND | wx.ALL, 5) @@ -306,29 +306,31 @@ class SkillTreeView(wx.Panel): self.searchTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.populateSkillTreeSkillSearch, self.searchTimer) - tree = self.skillTreeListCtrl = TreeListCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) + tree = self.skillTreeListCtrl = TreeListCtrl(self, wx.ID_ANY, style=wx.dataview.TL_DEFAULT_STYLE) 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")) - tree.AddColumn("Skill") - tree.AddColumn("Level") - tree.SetMainColumn(0) + tree.AppendColumn("Skill") + tree.AppendColumn("Level") + #tree.SetMainColumn(0) - self.root = tree.AddRoot("Skills") - tree.SetItemText(self.root, "Levels", 1) + self.root = tree.GetRootItem() + # self.root = tree.AppendItem(root, "Skills") + # + # tree.SetItemText(self.root, 1, "Levels") - tree.SetColumnWidth(0, 500) + #tree.SetColumnWidth(0, 300) self.btnSecStatus = wx.Button(self, wx.ID_ANY, "Sec Status: {0:.2f}".format(char.secStatus or 0.0)) self.btnSecStatus.Bind(wx.EVT_BUTTON, self.onSecStatus) self.populateSkillTree() - tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) - tree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.scheduleMenu) + tree.Bind(wx.dataview.EVT_TREELIST_ITEM_EXPANDING, self.expandLookup) + tree.Bind(wx.dataview.EVT_TREELIST_ITEM_CONTEXT_MENU, self.scheduleMenu) bSizerButtons = wx.BoxSizer(wx.HORIZONTAL) @@ -410,15 +412,16 @@ class SkillTreeView(wx.Panel): root = self.root tree = self.skillTreeListCtrl - tree.DeleteChildren(root) + tree.DeleteAllItems() for id, name in sChar.getSkillsByName(search): iconId = self.skillBookImageId childId = tree.AppendItem(root, name, iconId, data=('skill', id)) level, dirty = sChar.getSkillLevel(char.ID, id) - tree.SetItemText(childId, "Level %d" % int(level) if isinstance(level, float) else level, 1) - if dirty: - tree.SetItemTextColour(childId, wx.BLUE) + tree.SetItemText(childId, 1, "Level %d" % int(level) if isinstance(level, float) else level) + # @todo: pheonix + # if dirty: + # tree.SetItemTextColour(childId, wx.BLUE) def populateSkillTree(self, event=None): sChar = Character.getInstance() @@ -436,26 +439,27 @@ class SkillTreeView(wx.Panel): imageId = self.skillBookImageId root = self.root tree = self.skillTreeListCtrl - tree.DeleteChildren(root) + tree.DeleteAllItems() for id, name in groups: - childId = tree.AppendItem(root, name, imageId) - tree.SetPyData(childId, ('group', id)) + childId = tree.AppendItem(root, name, imageId, data=('group', id)) tree.AppendItem(childId, "dummy") - if id in dirtyGroups: - tree.SetItemTextColour(childId, wx.BLUE) + # @todo: pheonix + # if id in dirtyGroups: + # tree.(childId, wx.BLUE) - tree.SortChildren(root) + # @todo: pheonix + #tree.SortChildren(root) if event: event.Skip() def expandLookup(self, event): - root = event.Item + root = event.GetItem() tree = self.skillTreeListCtrl - child, cookie = tree.GetFirstChild(root) + child = tree.GetFirstChild(root) if tree.GetItemText(child) == "dummy": - tree.Delete(child) + tree.DeleteItem(child) # Get the real intrestin' stuff sChar = Character.getInstance() @@ -465,19 +469,22 @@ class SkillTreeView(wx.Panel): iconId = self.skillBookImageId childId = tree.AppendItem(root, name, iconId, data=('skill', id)) level, dirty = sChar.getSkillLevel(char.ID, id) - tree.SetItemText(childId, "Level %d" % int(level) if isinstance(level, float) else level, 1) - if dirty: - tree.SetItemTextColour(childId, wx.BLUE) + tree.SetItemText(childId, 1, "Level %d" % int(level) if isinstance(level, float) else level) - tree.SortChildren(root) + # @todo: pheonix + # if dirty: + # tree.SetItemTextColour(childId, wx.BLUE) + + #tree.SortChildren(root) def scheduleMenu(self, event): event.Skip() - wx.CallAfter(self.spawnMenu, event.Item) + wx.CallAfter(self.spawnMenu, event.GetItem()) def spawnMenu(self, item): - self.skillTreeListCtrl.SelectItem(item) - if self.skillTreeListCtrl.GetChildrenCount(item) > 0: + self.skillTreeListCtrl.Select(item) + thing = self.skillTreeListCtrl.GetFirstChild(item).IsOk() + if thing: return char = self.charEditor.entityEditor.getActiveEntity() diff --git a/gui/contextMenu.py b/gui/contextMenu.py index a82a78025..464d028bf 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -112,7 +112,7 @@ class ContextMenu(object): else: rootItem.SetBitmap(bitmap) - rootMenu.AppendItem(rootItem) + rootMenu.Append(rootItem) empty = False diff --git a/service/character.py b/service/character.py index cf2233fa9..0a36bb42a 100644 --- a/service/character.py +++ b/service/character.py @@ -267,7 +267,7 @@ class Character(object): for grp in cat.groups: if grp.published: groups.append((grp.ID, grp.name)) - return groups + return sorted(groups, key=lambda x: x[1]) @staticmethod def getSkills(groupID): @@ -276,7 +276,7 @@ class Character(object): for skill in group.items: if skill.published is True: skills.append((skill.ID, skill.name)) - return skills + return sorted(skills, key=lambda x: x[1]) @staticmethod def getSkillsByName(text): @@ -285,7 +285,7 @@ class Character(object): for skill in items: if skill.published is True: skills.append((skill.ID, skill.name)) - return skills + return sorted(skills, key=lambda x: x[1]) @staticmethod def setAlphaClone(char, cloneID): From 57783fe80f5255895cba5a9c14618b43fcf29da4 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sun, 2 Jul 2017 15:28:42 -0400 Subject: [PATCH 029/212] More work on character editor --- eos/saveddata/character.py | 2 +- gui/characterEditor.py | 49 ++++++++++++++++++-------------------- service/network.py | 2 +- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index d98b456ad..213457925 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -345,7 +345,7 @@ class Skill(HandledItem): def setLevel(self, level, persist=False): - if (level < 0 or level > 5) and level is not None: + if level is not None and (level < 0 or level > 5): raise ValueError(str(level) + " is not a valid value for level") if hasattr(self, "_Skill__ro") and self.__ro is True: diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 55fb10157..e9a60bb16 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -20,6 +20,7 @@ # noinspection PyPackageRequirements import wx import wx.dataview +import wx.lib.agw.hyperlink from .utils.floatspin import FloatSpin # noinspection PyPackageRequirements import wx.lib.newevent @@ -150,12 +151,12 @@ class CharacterEditor(wx.Frame): self.viewsNBContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) self.sview = SkillTreeView(self.viewsNBContainer) - # self.iview = ImplantEditorView(self.viewsNBContainer) - # self.aview = APIView(self.viewsNBContainer) + self.iview = ImplantEditorView(self.viewsNBContainer) + self.aview = APIView(self.viewsNBContainer) self.viewsNBContainer.AddPage(self.sview, "Skills") - # self.viewsNBContainer.AddPage(self.iview, "Implants") - # self.viewsNBContainer.AddPage(self.aview, "API") + self.viewsNBContainer.AddPage(self.iview, "Implants") + self.viewsNBContainer.AddPage(self.aview, "API") mainSizer.Add(self.viewsNBContainer, 1, wx.EXPAND | wx.ALL, 5) @@ -448,9 +449,6 @@ class SkillTreeView(wx.Panel): # if id in dirtyGroups: # tree.(childId, wx.BLUE) - # @todo: pheonix - #tree.SortChildren(root) - if event: event.Skip() @@ -475,8 +473,6 @@ class SkillTreeView(wx.Panel): # if dirty: # tree.SetItemTextColour(childId, wx.BLUE) - #tree.SortChildren(root) - def scheduleMenu(self, event): event.Skip() wx.CallAfter(self.spawnMenu, event.GetItem()) @@ -516,31 +512,33 @@ class SkillTreeView(wx.Panel): # level setting. We don't want to refresh tree, as that will lose all expanded categories and users location # within the tree. Thus, we loop through the tree and refresh the info. # @todo: when collapsing branch, remove the data. This will make this loop more performant - child, cookie = self.skillTreeListCtrl.GetFirstChild(self.root) + + child = self.skillTreeListCtrl.GetFirstChild(self.root) def _setTreeSkillLevel(treeItem, skillID): lvl, dirty = sChar.getSkillLevel(char.ID, skillID) self.skillTreeListCtrl.SetItemText(treeItem, - "Level {}".format(int(lvl)) if not isinstance(lvl, str) else lvl, - 1) - if not dirty: - self.skillTreeListCtrl.SetItemTextColour(treeItem, None) + 1, + "Level {}".format(int(lvl)) if not isinstance(lvl, str) else lvl) + # @todo: pheonix + # if not dirty: + # self.skillTreeListCtrl.SetItemTextColour(treeItem, None) while child.IsOk(): # child = Skill category dataType, id = self.skillTreeListCtrl.GetItemData(child) + if dataType == 'skill': _setTreeSkillLevel(child, id) else: - grand, cookie2 = self.skillTreeListCtrl.GetFirstChild(child) - + grand = self.skillTreeListCtrl.GetFirstChild(child) while grand.IsOk(): if self.skillTreeListCtrl.GetItemText(grand) != "dummy": _, skillID = self.skillTreeListCtrl.GetItemData(grand) _setTreeSkillLevel(grand, skillID) - grand, cookie2 = self.skillTreeListCtrl.GetNextChild(child, cookie2) + grand = self.skillTreeListCtrl.GetNextSibling(grand) - child, cookie = self.skillTreeListCtrl.GetNextChild(self.root, cookie) + child = self.skillTreeListCtrl.GetNextSibling(child) dirtySkills = sChar.getDirtySkills(char.ID) dirtyGroups = set([skill.item.group.ID for skill in dirtySkills]) @@ -548,9 +546,10 @@ class SkillTreeView(wx.Panel): parentID = self.skillTreeListCtrl.GetItemParent(selection) parent = self.skillTreeListCtrl.GetItemData(parentID) - if parent: - if parent[1] in dirtyGroups: - self.skillTreeListCtrl.SetItemTextColour(parentID, None) + # @todo: pheonix + # if parent: + # if parent[1] in dirtyGroups: + # self.skillTreeListCtrl.SetItemTextColour(parentID, None) event.Skip() @@ -691,8 +690,7 @@ class APIView(wx.Panel): 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) + self.hlEveAPI = wx.lib.agw.hyperlink.HyperLinkCtrl(self, wx.ID_ANY, label=self.apiUrlCreatePredefined) pmainSizer.Add(self.hlEveAPI, 0, wx.ALL, 2) self.stAPITip2 = wx.StaticText(self, wx.ID_ANY, "Or, you can choose an existing key from:", wx.DefaultPosition, @@ -700,8 +698,7 @@ class APIView(wx.Panel): 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) + self.hlEveAPI2 = wx.lib.agw.hyperlink.HyperLinkCtrl(self, wx.ID_ANY, label=self.apiUrlKeyList) pmainSizer.Add(self.hlEveAPI2, 0, wx.ALL, 2) self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged) @@ -764,7 +761,7 @@ class APIView(wx.Panel): self.stStatus.SetLabel(msg) except Exception as e: pyfalog.error(e) - self.stStatus.SetLabel("Error:\n%s" % e.message) + self.stStatus.SetLabel("Error:\n%s" % e) else: self.charChoice.Clear() for charName in list: diff --git a/service/network.py b/service/network.py index 0099300f9..924e2940b 100644 --- a/service/network.py +++ b/service/network.py @@ -113,7 +113,7 @@ class Network(object): # another option could be installing a default opener: # urllib2.install_opener(urllib2.build_opener()) - request = urllib.request.Request(url, headers=headers, data=urllib.parse.urlencode(data) if data else None) + request = urllib.request.Request(url, headers=headers, data=urllib.parse.urlencode(data).encode("utf-8") if data else None) try: return urllib.request.urlopen(request) except urllib.error.HTTPError as error: From 987c55ed8f16112d5eafade8af3efb9fec8d8dba Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sun, 2 Jul 2017 16:32:39 -0400 Subject: [PATCH 030/212] Get preferences dialog up and running --- gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py index 2165ce114..4df2ca7ca 100644 --- a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py +++ b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py @@ -8,7 +8,7 @@ from gui.bitmap_loader import BitmapLoader import gui.mainFrame from service.settings import HTMLExportSettings - +import wx.lib.agw.hyperlink class PFHTMLExportPref(PreferenceView): title = "HTML Export" @@ -37,10 +37,9 @@ 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(), - 'file:///{}'.format(self.HTMLExportSettings.getPath()), + self.PathLinkCtrl = wx.lib.agw.hyperlink.HyperLinkCtrl(panel, wx.ID_ANY, self.HTMLExportSettings.getPath(), wx.DefaultPosition, wx.DefaultSize, - wx.HL_ALIGN_LEFT | wx.NO_BORDER | wx.HL_CONTEXTMENU) + URL='file:///{}'.format(self.HTMLExportSettings.getPath()),) mainSizer.Add(self.PathLinkCtrl, 0, wx.ALL | wx.EXPAND, 5) self.fileSelectDialog = wx.FileDialog(None, "Save Fitting As...", From de5a734919537e3a7c96c50e6ec0542ff2926f88 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sun, 2 Jul 2017 16:42:22 -0400 Subject: [PATCH 031/212] About dialog (looks like shit x_x) --- gui/mainFrame.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 58f49b24e..08650027d 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -79,7 +79,7 @@ from time import gmtime, strftime import threading import webbrowser - +import wx.adv if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): from service.crest import Crest from service.crest import CrestModes @@ -357,7 +357,7 @@ class MainFrame(wx.Frame): def ShowAboutBox(self, evt): v = sys.version_info - info = wx.AboutDialogInfo() + info = wx.adv.AboutDialogInfo() info.Name = "pyfa" info.Version = gui.aboutData.versionString @@ -384,7 +384,7 @@ class MainFrame(wx.Frame): else: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") - wx.AboutBox(info) + wx.adv.AboutBox(info) def showCharacterEditor(self, event): dlg = CharacterEditor(self) From 7ae41b71b274e30b4b389b628f5b2f82870877a8 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sun, 2 Jul 2017 23:08:02 -0400 Subject: [PATCH 032/212] Fix some deprecated things and a bug or two --- eos/saveddata/character.py | 2 +- .../changeAffectingSkills.py | 4 +- gui/builtinContextMenus/metaSwap.py | 2 +- gui/itemStats.py | 45 +++++++++---------- gui/utils/floatspin.py | 8 +++- service/eveapi.py | 6 ++- 6 files changed, 35 insertions(+), 32 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 213457925..cb29f79a4 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -339,7 +339,7 @@ class Skill(HandledItem): elif self.character.name == "All 0": self.activeLevel = self.__level = 0 elif self.character.alphaClone: - return min(self.activeLevel, self.character.alphaClone.getSkillLevel(self)) or 0 + return min(self.activeLevel or 0, self.character.alphaClone.getSkillLevel(self) or 0) return self.activeLevel or 0 diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index 523557379..148c18218 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -91,10 +91,10 @@ class ChangeAffectingSkills(ContextMenu): for i in range(-1, 6): levelItem = self.addSkill(rootMenu if msw else grandSub, skill, i) - grandSub.AppendItem(levelItem) + grandSub.Append(levelItem) if (not skill.learned and i == -1) or (skill.learned and skill.level == i): levelItem.Check(True) - sub.AppendItem(skillItem) + sub.Append(skillItem) return sub diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py index d0e510af4..31804aa43 100644 --- a/gui/builtinContextMenus/metaSwap.py +++ b/gui/builtinContextMenus/metaSwap.py @@ -119,7 +119,7 @@ class MetaSwap(ContextMenu): mitem = wx.MenuItem(rootMenu, id, item.name) bindmenu.Bind(wx.EVT_MENU, self.handleModule, mitem) self.moduleLookup[id] = item - m.AppendItem(mitem) + m.Append(mitem) return m def handleModule(self, event): diff --git a/gui/itemStats.py b/gui/itemStats.py index f26b6fd88..d1eb50d1e 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -206,18 +206,12 @@ class ItemStatsContainer(wx.Panel): self.properties = ItemProperties(self.nbContainer, stuff, item, context) self.nbContainer.AddPage(self.properties, "Properties") - self.nbContainer.Bind(wx.EVT_LEFT_DOWN, self.mouseHit) self.SetSizer(mainSizer) self.Layout() def __del__(self): pass - def mouseHit(self, event): - tab, _ = self.nbContainer.HitTest(event.Position) - if tab != -1: - self.nbContainer.SetSelection(tab) - class AutoListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter): def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): @@ -463,7 +457,7 @@ class ItemParams(wx.Panel): else: attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) - index = self.paramList.InsertImageStringItem(sys.maxsize, attrName, attrIcon) + index = self.paramList.InsertItem(sys.maxsize, attrName, attrIcon) idNameMap[idCount] = attrName self.paramList.SetItemData(index, idCount) idCount += 1 @@ -482,11 +476,12 @@ class ItemParams(wx.Panel): else: valueUnitDefault = formatAmount(valueDefault, 3, 0, 0) - self.paramList.SetStringItem(index, 1, valueUnit) + self.paramList.SetItem(index, 1, valueUnit) 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.SetItem(index, 2, valueUnitDefault) + # @todo: pheonix, this lamda used cmp() which no longer exists in py3. Probably a better way to do this in the + # long run, take a look + self.paramList.SortItems(lambda id1, id2: (idNameMap[id1]>idNameMap[id2])-(idNameMap[id1] 0: attributes = [] @@ -1362,7 +1357,7 @@ class ItemAffectedBy(wx.Panel): saved = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized) treeitem = self.affectedBy.AppendItem(child, display, attrIcon) - self.affectedBy.SetPyData(treeitem, saved) + self.affectedBy.SetItemData(treeitem, saved) self.treeItems.append(treeitem) @@ -1447,13 +1442,13 @@ class ItemProperties(wx.Panel): valueUnit = str(value) - self.paramList.SetStringItem(index, 1, valueUnit) + self.paramList.SetItem(index, 1, valueUnit) except: # TODO: Add logging to this. # We couldn't get a property for some reason. Skip it for now. continue - self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2])) + self.paramList.SortItems(lambda id1, id2: (idNameMap[id1]>idNameMap[id2])-(idNameMap[id1]b)-(a 0 n, leftover = divmod(x, y) - c = cmp(leftover << 1, y) + c = _cmp(leftover << 1, y) # c < 0 <-> leftover < y/2, etc if c > 0 or (c == 0 and (n & 1) == 1): n = n + 1 diff --git a/service/eveapi.py b/service/eveapi.py index e64315bd6..260b8afc1 100644 --- a/service/eveapi.py +++ b/service/eveapi.py @@ -722,6 +722,9 @@ class Element(object): _fmt = "%s:%s".__mod__ +def _cmp(self, a, b): + return (a>b)-(a Date: Sun, 2 Jul 2017 23:45:28 -0400 Subject: [PATCH 033/212] fix fix fix --- gui/builtinShipBrowser/fitItem.py | 2 +- gui/characterEditor.py | 2 +- gui/errorDialog.py | 2 +- gui/mainFrame.py | 2 +- gui/patternEditor.py | 2 +- gui/preferenceDialog.py | 4 ++-- gui/resistsEditor.py | 2 +- gui/setEditor.py | 2 +- gui/updateDialog.py | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index cd702d980..e0577c52d 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -382,7 +382,7 @@ class FitItem(SFItem.SFBrowserItem): if self.dragging and self.dragged: self.OnMouseCaptureLost(event) - targetWnd = wx.FindWindowAtPointer() + targetWnd, _ = wx.FindWindowAtPointer() if not targetWnd: return diff --git a/gui/characterEditor.py b/gui/characterEditor.py index e9a60bb16..20555d488 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -827,7 +827,7 @@ class SecStatusDialog(wx.Dialog): def __init__(self, parent, sec): wx.Dialog.__init__(self, parent, title="Set Security Status", size=(275, 175)) - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) bSizer1 = wx.BoxSizer(wx.VERTICAL) diff --git a/gui/errorDialog.py b/gui/errorDialog.py index f0a8c3bce..1fedea00e 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -52,7 +52,7 @@ class ErrorFrame(wx.Frame): "information about how this was triggered. Please contact the developers with the\n" \ "information provided through the EVE Online forums or file a GitHub issue." - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) if 'wxMSW' in wx.PlatformInfo: self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 08650027d..61c012e72 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -1025,7 +1025,7 @@ class MainFrame(wx.Frame): # Find a widget to be selected in the tree. Use either the # one under the cursor, if any, or this frame. - wnd = wx.FindWindowAtPointer() + wnd, _ = wx.FindWindowAtPointer() if not wnd: wnd = self InspectionTool().Show(wnd, True) diff --git a/gui/patternEditor.py b/gui/patternEditor.py index ae27bf924..3bed203a2 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -92,7 +92,7 @@ class DmgPatternEditorDlg(wx.Dialog): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Damage Pattern Editor", size=wx.Size(400, 240)) self.block = False - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index 20277bb58..4a1c79a21 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -69,9 +69,9 @@ class PreferenceDialog(wx.Dialog): minHeight = 550 bestFit = self.GetBestVirtualSize() if minHeight > bestFit[1]: - self.SetSizeWH(650, minHeight) + self.SetSize(650, minHeight) else: - self.SetSizeWH(650, bestFit[1]) + self.SetSize(650, bestFit[1]) self.Layout() diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index 0e88a10a4..b6997e19d 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -90,7 +90,7 @@ class ResistsEditorDlg(wx.Dialog): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Target Resists Editor", size=wx.Size(350, 240)) self.block = False - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) diff --git a/gui/setEditor.py b/gui/setEditor.py index e8a17c9a3..d7f5b71d4 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -117,7 +117,7 @@ class ImplantSetEditorDlg(wx.Dialog): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Implant Set Editor", size=wx.Size(640, 600)) self.block = False - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 4e9281a90..7cb793dc5 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -31,7 +31,7 @@ class UpdateDialog(wx.Dialog): self.UpdateSettings = svc_UpdateSettings.getInstance() self.releaseInfo = release - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) From 0527a506ac73ab7e013326df878bdc1ecf524901 Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Mon, 3 Jul 2017 01:46:46 -0400 Subject: [PATCH 034/212] fix broken events preventing additions panels to not work --- eos/gamedata.py | 2 +- eos/saveddata/drone.py | 2 +- gui/builtinAdditionPanes/commandView.py | 5 ++++- gui/builtinAdditionPanes/droneView.py | 3 +++ gui/builtinAdditionPanes/fighterView.py | 2 ++ gui/builtinAdditionPanes/implantView.py | 2 ++ gui/builtinAdditionPanes/notesView.py | 2 ++ gui/builtinAdditionPanes/projectedView.py | 2 ++ 8 files changed, 17 insertions(+), 3 deletions(-) diff --git a/eos/gamedata.py b/eos/gamedata.py index 051ad6164..03d64957c 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -68,7 +68,7 @@ class Effect(EqBase): if not self.__generated: self.__generateHandler() - pyfalog.debug("Generating effect: {0} ({1}) [runTime: {2}]", self.name, self.effectID, self.runTime) + #pyfalog.debug("Generating effect: {0} ({1}) [runTime: {2}]", self.name, self.effectID, self.runTime) return self.__handler diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index d5ef1c37a..28012f64f 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -102,7 +102,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def cycleTime(self): - return max(self.getModifiedItemAttr("duration"), 0) + return max(self.getModifiedItemAttr("duration", 0), 0) @property def dealsDamage(self): diff --git a/gui/builtinAdditionPanes/commandView.py b/gui/builtinAdditionPanes/commandView.py index 697953995..a02dd70f4 100644 --- a/gui/builtinAdditionPanes/commandView.py +++ b/gui/builtinAdditionPanes/commandView.py @@ -66,7 +66,6 @@ class CommandView(d.Display): self.lastFitId = None - self.mainFrame.Bind(GE.FIT_CHANGED, CommandFits.populateFits) self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) self.Bind(wx.EVT_LEFT_DOWN, self.click) self.Bind(wx.EVT_RIGHT_DOWN, self.click) @@ -134,6 +133,8 @@ class CommandView(d.Display): sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) + CommandFits.populateFits(event) + self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) # Clear list and get out if current fitId is None @@ -165,6 +166,8 @@ class CommandView(d.Display): self.update(stuff) + event.Skip() + def get(self, row): numFits = len(self.fits) diff --git a/gui/builtinAdditionPanes/droneView.py b/gui/builtinAdditionPanes/droneView.py index 2fd15a1e7..41327618b 100644 --- a/gui/builtinAdditionPanes/droneView.py +++ b/gui/builtinAdditionPanes/droneView.py @@ -21,6 +21,7 @@ import wx import gui.globalEvents as GE +import gui.mainFrame from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED from gui.display import Display from gui.builtinViewColumns.state import State @@ -66,6 +67,8 @@ class DroneView(Display): self.hoveredRow = None self.hoveredColumn = None + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) self.mainFrame.Bind(ITEM_SELECTED, self.addItem) self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) diff --git a/gui/builtinAdditionPanes/fighterView.py b/gui/builtinAdditionPanes/fighterView.py index 4291e421d..e68b56820 100644 --- a/gui/builtinAdditionPanes/fighterView.py +++ b/gui/builtinAdditionPanes/fighterView.py @@ -108,6 +108,8 @@ class FighterView(wx.Panel): self.Refresh() + event.Skip() + class FighterDisplay(d.Display): DEFAULT_COLS = ["State", diff --git a/gui/builtinAdditionPanes/implantView.py b/gui/builtinAdditionPanes/implantView.py index e779f5672..24acf313b 100644 --- a/gui/builtinAdditionPanes/implantView.py +++ b/gui/builtinAdditionPanes/implantView.py @@ -71,6 +71,8 @@ class ImplantView(wx.Panel): self.rbFit.Enable(fit is not None) self.rbChar.Enable(fit is not None) + event.Skip() + def OnRadioSelect(self, event): fitID = self.mainFrame.getActiveFit() sFit = Fit.getInstance() diff --git a/gui/builtinAdditionPanes/notesView.py b/gui/builtinAdditionPanes/notesView.py index 5c44a26c2..35a75b5a8 100644 --- a/gui/builtinAdditionPanes/notesView.py +++ b/gui/builtinAdditionPanes/notesView.py @@ -40,6 +40,8 @@ class NotesView(wx.Panel): self.lastFitId = event.fitID self.editNotes.SetValue(fit.notes or "") + event.Skip() + def onText(self, event): # delay the save so we're not writing to sqlite on every keystroke self.saveTimer.Stop() # cancel the existing timer diff --git a/gui/builtinAdditionPanes/projectedView.py b/gui/builtinAdditionPanes/projectedView.py index b209da974..991e7d636 100644 --- a/gui/builtinAdditionPanes/projectedView.py +++ b/gui/builtinAdditionPanes/projectedView.py @@ -222,6 +222,8 @@ class ProjectedView(d.Display): self.update(stuff) + event.Skip() + def get(self, row): numMods = len(self.modules) numDrones = len(self.drones) From 961b389b40c4dd35faa49e6a9848c47febee67e1 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 6 Jul 2017 23:41:55 -0400 Subject: [PATCH 035/212] test --- gui/chrome_tabs.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index e65719867..86a99a6d9 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -1100,15 +1100,15 @@ class _TabsContainer(wx.Panel): else: mdc = wx.BufferedPaintDC(self) - if 'wxMac' in wx.PlatformInfo: - color = wx.Colour(0, 0, 0) - brush = wx.Brush(color) - # @todo: what needs to be changed with wxPheonix? - from Carbon.Appearance import kThemeBrushDialogBackgroundActive - brush.MacSetTheme(kThemeBrushDialogBackgroundActive) - else: - color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE) - brush = wx.Brush(color) + # if 'wxMac' in wx.PlatformInfo: + # color = wx.Colour(0, 0, 0) + # brush = wx.Brush(color) + # # @todo: what needs to be changed with wxPheonix? + # from Carbon.Appearance import kThemeBrushDialogBackgroundActive + # brush.MacSetTheme(kThemeBrushDialogBackgroundActive) + # else: + color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE) + brush = wx.Brush(color) if "wxGTK" not in wx.PlatformInfo: mdc.SetBackground(brush) From 3b716e6e5e3f53cfcb900a676b6eb61cca630de2 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 7 Jul 2017 00:22:58 -0400 Subject: [PATCH 036/212] fix py2app setup file --- setup-osx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup-osx.py b/setup-osx.py index b27a5df2b..8e2d55de7 100644 --- a/setup-osx.py +++ b/setup-osx.py @@ -11,7 +11,7 @@ DATA_FILES = ['eve.db', 'README.md', 'LICENSE', 'imgs', requests.certs.where()] OPTIONS = { 'argv_emulation': False, 'iconfile': 'dist_assets/mac/pyfa.icns', - 'packages': ['eos', 'gui', 'service', 'utils'] + 'packages': ['eos', 'gui', 'service', 'utils', 'sqlalchemy'] } setup( From 2afc8b1abe04f2a2cb9b1f1c059abb81bd79803b Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 25 Sep 2017 01:58:31 -0400 Subject: [PATCH 037/212] Get rack headers bold again (courtesy of @RobinD42 - https://github.com/wxWidgets/Phoenix/issues/525) --- gui/builtinViews/fittingView.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 7fa7adabc..c07982886 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -646,7 +646,7 @@ class FittingView(d.Display): slot = Slot.getValue(slotType) slotMap[slot] = fit.getSlotsFree(slot) < 0 - font = (self.GetClassDefaultAttributes()).font + font = wx.Font(self.GetClassDefaultAttributes().font) for i, mod in enumerate(self.mods): self.SetItemBackgroundColour(i, self.GetBackgroundColour()) @@ -662,11 +662,10 @@ class FittingView(d.Display): if isinstance(mod, Rack) and \ sFit.serviceFittingOptions["rackSlots"] and \ sFit.serviceFittingOptions["rackLabels"]: - # @ todo pheonix: SetWeight is causing a hard crash, possibly a wxPython bug? - #font.SetWeight(wx.FONTWEIGHT_BOLD) + font.SetWeight(wx.FONTWEIGHT_BOLD) self.SetItemFont(i, font) else: - #font.SetWeight(wx.FONTWEIGHT_NORMAL) + font.SetWeight(wx.FONTWEIGHT_NORMAL) self.SetItemFont(i, font) self.Thaw() From 56f34873a6654cb76da5a1911805d542aa507866 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 8 Oct 2017 22:51:06 -0400 Subject: [PATCH 038/212] bleh --- gui/chrome_tabs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index 86a99a6d9..603c14b9a 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -347,6 +347,7 @@ class _TabRenderer: width = max(width, self.min_width) height = max(height, self.min_height) + self.disabled = False self.text = text self.tab_size = (width, height) self.closeable = closeable From ce3b94696a768143ea14b5956322a90e45d558cc Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 4 Nov 2017 18:20:00 -0400 Subject: [PATCH 039/212] Make sure getModifiedItemAttr always returns an int, unless otherwise wanting None In py2, you could compare None to an int and it would always be less than. Unfortunately in py3, this is no longer the case. Returning getModifiedItemAttr as 0 allows us to not do a huge refactor. --- eos/modifiedAttributeDict.py | 8 ++++---- eos/saveddata/character.py | 2 +- eos/saveddata/drone.py | 3 ++- eos/saveddata/module.py | 2 +- service/fit.py | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index 6b3837f69..49026635f 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -28,23 +28,23 @@ cappingAttrKeyCache = {} class ItemAttrShortcut(object): - def getModifiedItemAttr(self, key, default=None): + def getModifiedItemAttr(self, key, default=0): return_value = self.itemModifiedAttributes.get(key) if return_value is None and default is not None: return_value = default - return return_value + return return_value if default is not None else None class ChargeAttrShortcut(object): - def getModifiedChargeAttr(self, key, default=None): + def getModifiedChargeAttr(self, key, default=0): return_value = self.chargeModifiedAttributes.get(key) if return_value is None and default is not None: return_value = default - return return_value + return return_value if default is not None else None class ModifiedAttributeDict(collections.MutableMapping): diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index cb29f79a4..b9f3e2a4b 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -385,7 +385,7 @@ class Skill(HandledItem): if key in self.item.attributes: return self.item.attributes[key].value else: - return None + return 0 def calculateModifiedAttributes(self, fit, runTime): if self.__suppressed: # or not self.learned - removed for GH issue 101 diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index 28012f64f..36c969029 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -73,7 +73,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.__itemModifiedAttributes.overrides = self.__item.overrides self.__chargeModifiedAttributes = ModifiedAttributeDict() - chargeID = self.getModifiedItemAttr("entityMissileTypeID") + # pheonix todo: check the attribute itself, not the modified. this will always return 0 now. + chargeID = self.getModifiedItemAttr("entityMissileTypeID", None) if chargeID is not None: charge = eos.db.getItem(int(chargeID)) self.__charge = charge diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index f0eb42921..fcec22dd4 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -213,7 +213,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): armorRep = self.getModifiedItemAttr("armorDamageAmount") or 0 shieldRep = self.getModifiedItemAttr("shieldBonus") or 0 if not cycles or (not armorRep and not shieldRep): - return None + return 0 hp = round((armorRep + shieldRep) * cycles) return hp diff --git a/service/fit.py b/service/fit.py index 307aa7141..4c85d6cbb 100644 --- a/service/fit.py +++ b/service/fit.py @@ -255,7 +255,7 @@ class Fit(object): 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 """ - pyfalog.debug("Getting fit for fit ID: {0}", fitID) + # pyfalog.debug("Getting fit for fit ID: {0}", fitID) if fitID is None: return None fit = eos.db.getFit(fitID) From e0b92198b0a4a2eddd28276d0f4fc0f748a967a7 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 4 Nov 2017 18:20:54 -0400 Subject: [PATCH 040/212] Fix deprecation warnings --- gui/builtinContextMenus/commandFits.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/builtinContextMenus/commandFits.py b/gui/builtinContextMenus/commandFits.py index 8c73f9b8d..06ef53eae 100644 --- a/gui/builtinContextMenus/commandFits.py +++ b/gui/builtinContextMenus/commandFits.py @@ -81,9 +81,9 @@ class CommandFits(ContextMenu): for fit in sorted(typeDict[ship], key=lambda x: x.name): fitItem = self.addFit(rootMenu if msw else grandSub, fit, False) - grandSub.AppendItem(fitItem) + grandSub.Append(fitItem) - sub.AppendItem(shipItem) + sub.Append(shipItem) return sub From 0e1e4cad6d4078df7f7b4fe4d5bb92bfa0210018 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 4 Nov 2017 18:24:18 -0400 Subject: [PATCH 041/212] Fix a few more deprecation warnings --- gui/builtinContextMenus/moduleAmmoPicker.py | 16 ++++++++-------- gui/builtinContextMenus/tabbedFits.py | 4 ++-- gui/builtinContextMenus/whProjector.py | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 7b1f86169..3ab75bca8 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -170,15 +170,15 @@ class ModuleAmmoPicker(ContextMenu): 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.Append(self.addCharge(rootMenu if msw else sub, base)) - sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) + sub.Append(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) + m.Append(item) self.addSeperator(m, "Short Range") elif hardpoint == Hardpoint.MISSILE and moduleName != 'Festival Launcher': @@ -203,23 +203,23 @@ class ModuleAmmoPicker(ContextMenu): sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) self.addSeperator(sub, "Less Damage") item.SetSubMenu(sub) - m.AppendItem(item) + m.Append(item) if charge.name not in ("Light Defender Missile I", "Heavy Defender Missile I"): - sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) + sub.Append(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)) + m.Append(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.Append(self.addCharge(rootMenu if msw else m, charge)) - m.AppendItem(self.addCharge(rootMenu if msw else m, None)) + m.Append(self.addCharge(rootMenu if msw else m, None)) return m def handleAmmoSwitch(self, event): diff --git a/gui/builtinContextMenus/tabbedFits.py b/gui/builtinContextMenus/tabbedFits.py index f95a0229e..c55a90b35 100644 --- a/gui/builtinContextMenus/tabbedFits.py +++ b/gui/builtinContextMenus/tabbedFits.py @@ -38,7 +38,7 @@ class TabbedFits(ContextMenu): else: bindmenu = m - for page in self.mainFrame.fitMultiSwitch.pages: + for page in self.mainFrame.fitMultiSwitch._pages: if isinstance(page, BlankPage): continue fit = sFit.getFit(page.activeFitID, basic=True) @@ -46,7 +46,7 @@ class TabbedFits(ContextMenu): mitem = wx.MenuItem(rootMenu, id, "{}: {}".format(fit.ship.item.name, fit.name)) bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem) self.fitLookup[id] = fit - m.AppendItem(mitem) + m.Append(mitem) return m diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py index a677ceabf..4fab1fc93 100644 --- a/gui/builtinContextMenus/whProjector.py +++ b/gui/builtinContextMenus/whProjector.py @@ -34,7 +34,7 @@ class WhProjector(ContextMenu): subItem = wx.MenuItem(sub, wx.ID_ANY, swType) grandSub = wx.Menu() subItem.SetSubMenu(grandSub) - sub.AppendItem(subItem) + sub.Append(subItem) for swData in sorted(effdata[swType], key=lambda tpl: tpl[2]): wxid = ContextMenu.nextID() From 3b0c8b6117b8b5878d4e9fb8e5d4b043c8ce6b41 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 4 Nov 2017 18:32:26 -0400 Subject: [PATCH 042/212] Fix Racks having a CPU and Power value of 0 --- gui/builtinViewColumns/attributeDisplay.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index 6b5422f1d..7ad8743d7 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -72,7 +72,7 @@ class AttributeDisplay(ViewColumn): def getText(self, mod): if hasattr(mod, "item"): - attr = mod.getModifiedItemAttr(self.info.name) + attr = mod.getModifiedItemAttr(self.info.name, None) else: if self.direct: info = self.directInfo @@ -80,6 +80,9 @@ class AttributeDisplay(ViewColumn): else: attr = mod.getAttribute(self.info.name) + if attr is None: + return "" + if self.info.name == "volume": str_ = (formatAmount(attr, 3, 0, 3)) if hasattr(mod, "amount"): @@ -89,7 +92,7 @@ class AttributeDisplay(ViewColumn): if isinstance(attr, (float, int)): attr = (formatAmount(attr, 3, 0, 3)) - return attr if attr is not None else "" + return attr def getImageId(self, mod): return -1 From 6a382c4445149cc36a282ff0240e91b4072c7786 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 5 Nov 2017 01:51:05 -0400 Subject: [PATCH 043/212] More tweaks to getModifiedItemAttr and fix deprecation warning --- eos/modifiedAttributeDict.py | 10 ++-------- eos/saveddata/drone.py | 4 ++-- eos/saveddata/fighter.py | 8 ++++---- eos/saveddata/module.py | 23 +++++++++++------------ gui/builtinMarketBrowser/marketTree.py | 2 +- gui/builtinViewColumns/maxRange.py | 2 +- 6 files changed, 21 insertions(+), 28 deletions(-) diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index 49026635f..e522c4487 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -31,20 +31,14 @@ class ItemAttrShortcut(object): def getModifiedItemAttr(self, key, default=0): return_value = self.itemModifiedAttributes.get(key) - if return_value is None and default is not None: - return_value = default - - return return_value if default is not None else None + return return_value or default class ChargeAttrShortcut(object): def getModifiedChargeAttr(self, key, default=0): return_value = self.chargeModifiedAttributes.get(key) - if return_value is None and default is not None: - return_value = default - - return return_value if default is not None else None + return return_value or default class ModifiedAttributeDict(collections.MutableMapping): diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index 36c969029..cf9309de2 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -168,7 +168,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): "energyDestabilizationRange", "empFieldRange", "ecmBurstRange", "maxRange") for attr in attrs: - maxRange = self.getModifiedItemAttr(attr) + maxRange = self.getModifiedItemAttr(attr, None) if maxRange is not None: return maxRange if self.charge is not None: @@ -184,7 +184,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def falloff(self): attrs = ("falloff", "falloffEffectiveness") for attr in attrs: - falloff = self.getModifiedItemAttr(attr) + falloff = self.getModifiedItemAttr(attr, None) if falloff is not None: return falloff diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py index 004e86149..695024540 100644 --- a/eos/saveddata/fighter.py +++ b/eos/saveddata/fighter.py @@ -199,12 +199,12 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): "energyDestabilizationRange", "empFieldRange", "ecmBurstRange", "maxRange") for attr in attrs: - maxRange = self.getModifiedItemAttr(attr) + maxRange = self.getModifiedItemAttr(attr, None) if maxRange is not None: return maxRange if self.charge is not None: - delay = self.getModifiedChargeAttr("explosionDelay") - speed = self.getModifiedChargeAttr("maxVelocity") + delay = self.getModifiedChargeAttr("explosionDelay", None) + speed = self.getModifiedChargeAttr("maxVelocity", None) if delay is not None and speed is not None: return delay / 1000.0 * speed @@ -215,7 +215,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def falloff(self): attrs = ("falloff", "falloffEffectiveness") for attr in attrs: - falloff = self.getModifiedItemAttr(attr) + falloff = self.getModifiedItemAttr(attr, None) if falloff is not None: return falloff diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index fcec22dd4..41b8ba96d 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -251,7 +251,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): "ecmBurstRange", "warpScrambleRange", "cargoScanRange", "shipScanRange", "surveyScanRange") for attr in attrs: - maxRange = self.getModifiedItemAttr(attr) + maxRange = self.getModifiedItemAttr(attr, None) if maxRange is not None: return maxRange if self.charge is not None: @@ -280,7 +280,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def falloff(self): attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff") for attr in attrs: - falloff = self.getModifiedItemAttr(attr) + falloff = self.getModifiedItemAttr(attr, None) if falloff is not None: return falloff @@ -409,19 +409,19 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): fitsOnType = set() fitsOnGroup = set() - shipType = self.getModifiedItemAttr("fitsToShipType") + shipType = self.getModifiedItemAttr("fitsToShipType", None) if shipType is not None: fitsOnType.add(shipType) for attr in list(self.itemModifiedAttributes.keys()): if attr.startswith("canFitShipType"): - shipType = self.getModifiedItemAttr(attr) + shipType = self.getModifiedItemAttr(attr, None) if shipType is not None: fitsOnType.add(shipType) for attr in list(self.itemModifiedAttributes.keys()): if attr.startswith("canFitShipGroup"): - shipGroup = self.getModifiedItemAttr(attr) + shipGroup = self.getModifiedItemAttr(attr, None) if shipGroup is not None: fitsOnGroup.add(shipGroup) @@ -453,7 +453,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return False # Check max group fitted - max = self.getModifiedItemAttr("maxGroupFitted") + max = self.getModifiedItemAttr("maxGroupFitted", None) if max is not None: current = 0 if self.owner != fit else -1 for mod in fit.modules: @@ -466,11 +466,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): # 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: + if fit.ship.getModifiedItemAttr('turretSlotsLeft') - fit.getHardpointsUsed(Hardpoint.TURRET) < 1: return False elif self.hardpoint == Hardpoint.MISSILE: - if (fit.ship.getModifiedItemAttr('launcherSlotsLeft') or 0) - fit.getHardpointsUsed( - Hardpoint.MISSILE) < 1: + if fit.ship.getModifiedItemAttr('launcherSlotsLeft') - fit.getHardpointsUsed(Hardpoint.MISSILE) < 1: return False return True @@ -500,7 +499,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return True # Check if the local module is over it's max limit; if it's not, we're fine - maxGroupActive = self.getModifiedItemAttr("maxGroupActive") + maxGroupActive = self.getModifiedItemAttr("maxGroupActive", None) if maxGroupActive is None and projectedOnto is None: return True @@ -548,7 +547,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): chargeGroup = charge.groupID for i in range(5): - itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) + itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None) if itemChargeGroup is None: continue if itemChargeGroup == chargeGroup: @@ -559,7 +558,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def getValidCharges(self): validCharges = set() for i in range(5): - itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) + itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None) if itemChargeGroup is not None: g = eos.db.getGroup(int(itemChargeGroup), eager=("items.icon", "items.attributes")) if g is None: diff --git a/gui/builtinMarketBrowser/marketTree.py b/gui/builtinMarketBrowser/marketTree.py index 06b74d88a..ae8b77a85 100644 --- a/gui/builtinMarketBrowser/marketTree.py +++ b/gui/builtinMarketBrowser/marketTree.py @@ -88,7 +88,7 @@ class MarketTree(wx.TreeCtrl): for i in range(len(jumpList) - 1, -1, -1): target = jumpList[i] child, cookie = self.GetFirstChild(item) - while self.GetItemPyData(child) != target: + while self.GetItemData(child) != target: child, cookie = self.GetNextChild(item, cookie) item = child diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py index 728e2b9bc..ef420c344 100644 --- a/gui/builtinViewColumns/maxRange.py +++ b/gui/builtinViewColumns/maxRange.py @@ -58,7 +58,7 @@ class MaxRange(ViewColumn): if isinstance(stuff, Mode): return "" - maxRange = stuff.maxRange if hasattr(stuff, "maxRange") else stuff.getModifiedItemAttr("maxRange") + maxRange = stuff.maxRange if hasattr(stuff, "maxRange") else stuff.getModifiedItemAttr("maxRange", None) falloff = stuff.falloff if falloff: falloff = "+%sm" % formatAmount(falloff, 3, 0, 3) From 5ae7805bb10dcc0f7b453fbb8404b9f216097dc6 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 5 Nov 2017 01:54:19 -0400 Subject: [PATCH 044/212] Fix ammo picker --- gui/builtinContextMenus/moduleAmmoPicker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 3ab75bca8..47a6fc87d 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -58,9 +58,9 @@ class ModuleAmmoPicker(ContextMenu): def turretSorter(self, charge): damage = 0 - range_ = (self.module.getModifiedItemAttr("maxRange") or 0) * \ + range_ = (self.module.getModifiedItemAttr("maxRange")) * \ (charge.getAttribute("weaponRangeMultiplier") or 1) - falloff = (self.module.getModifiedItemAttr("falloff") or 0) * \ + falloff = (self.module.getModifiedItemAttr("falloff")) * \ (charge.getAttribute("fallofMultiplier") or 1) for type_ in self.DAMAGE_TYPES: d = charge.getAttribute("%sDamage" % type_) @@ -137,7 +137,7 @@ class ModuleAmmoPicker(ContextMenu): 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: + if hardpoint == Hardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount", None) is None: self.addSeperator(m, "Long Range") items = [] range_ = None From 0f5ae8d9b652c4261de8e2233a4cf025f604d7b1 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 5 Nov 2017 01:03:14 -0500 Subject: [PATCH 045/212] Fix delete fix (renamed MiddleDown to MiddleIsDown) --- gui/builtinShipBrowser/fitItem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index e0577c52d..4aea370d7 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -337,7 +337,7 @@ class FitItem(SFItem.SFBrowserItem): return # to prevent accidental deletion, give dialog confirmation unless shift is depressed - if wx.GetMouseState().ShiftDown() or wx.GetMouseState().MiddleDown(): + if wx.GetMouseState().ShiftDown() or wx.GetMouseState().MiddleIsDown(): self.deleteFit() else: dlg = wx.MessageDialog( From bd0de82a8ef6ab5b9545ea4c01d721d70157c1b9 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 5 Nov 2017 01:10:22 -0500 Subject: [PATCH 046/212] remove repo version of FloatSpin, go with wx bundled version --- gui/characterEditor.py | 5 +- gui/utils/floatspin.py | 1666 ---------------------------------------- 2 files changed, 4 insertions(+), 1667 deletions(-) delete mode 100644 gui/utils/floatspin.py diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 20555d488..ce2841764 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -21,7 +21,7 @@ import wx import wx.dataview import wx.lib.agw.hyperlink -from .utils.floatspin import FloatSpin + # noinspection PyPackageRequirements import wx.lib.newevent # noinspection PyPackageRequirements @@ -36,6 +36,9 @@ from service.character import Character from service.network import AuthenticationError, TimeoutError from service.market import Market from logbook import Logger + +from wx.lib.agw.floatspin import FloatSpin + pyfalog = Logger(__name__) diff --git a/gui/utils/floatspin.py b/gui/utils/floatspin.py deleted file mode 100644 index 2a636433a..000000000 --- a/gui/utils/floatspin.py +++ /dev/null @@ -1,1666 +0,0 @@ -# --------------------------------------------------------------------------- # -# FLOATSPIN Control wxPython IMPLEMENTATION -# Python Code By: -# -# Andrea Gavana, @ 16 Nov 2005 -# Latest Revision: 03 Jan 2014, 23.00 GMT -# -# -# TODO List/Caveats -# -# 1. Ay Idea? -# -# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please -# Write To Me At: -# -# andrea.gavana@gmail.com -# andrea.gavana@maerskoil.com -# -# Or, Obviously, To The wxPython Mailing List!!! -# -# -# End Of Comments -# --------------------------------------------------------------------------- # - - -""" -:class:`FloatSpin` implements a floating point :class:`SpinCtrl`. - - -Description -=========== - -:class:`FloatSpin` implements a floating point :class:`SpinCtrl`. It is built using a custom -:class:`PyControl`, composed by a :class:`TextCtrl` and a :class:`SpinButton`. In order to -correctly handle floating points numbers without rounding errors or non-exact -floating point representations, :class:`FloatSpin` uses the great :class:`FixedPoint` class -from Tim Peters. - -What you can do: - -- Set the number of representative digits for your floating point numbers; -- Set the floating point format (``%f``, ``%F``, ``%e``, ``%E``, ``%g``, ``%G``); -- Set the increment of every ``EVT_FLOATSPIN`` event; -- Set minimum, maximum values for :class:`FloatSpin` as well as its range; -- Change font and colour for the underline :class:`TextCtrl`. - - -Usage -===== - -Usage example:: - - import wx - import wx.lib.agw.floatspin as FS - - class MyFrame(wx.Frame): - - def __init__(self, parent): - - wx.Frame.__init__(self, parent, -1, "FloatSpin Demo") - - panel = wx.Panel(self) - - floatspin = FS.FloatSpin(panel, -1, pos=(50, 50), min_val=0, max_val=1, - increment=0.01, value=0.1, agwStyle=FS.FS_LEFT) - floatspin.SetFormat("%f") - floatspin.SetDigits(2) - - - # our normal wxApp-derived class, as usual - - app = wx.App(0) - - frame = MyFrame(None) - app.SetTopWindow(frame) - frame.Show() - - app.MainLoop() - - - -Events -====== - -:class:`FloatSpin` catches 3 different types of events: - -1) Spin events: events generated by spinning up/down the spinbutton; -2) Char events: playing with up/down arrows of the keyboard increase/decrease - the value of :class:`FloatSpin`; -3) Mouse wheel event: using the wheel will change the value of :class:`FloatSpin`. - -In addition, there are some other functionalities: - -- It remembers the initial value as a default value, call meth:~FloatSpin.SetToDefaultValue`, or - press ``Esc`` to return to it; -- ``Shift`` + arrow = 2 * increment (or ``Shift`` + mouse wheel); -- ``Ctrl`` + arrow = 10 * increment (or ``Ctrl`` + mouse wheel); -- ``Alt`` + arrow = 100 * increment (or ``Alt`` + mouse wheel); -- Combinations of ``Shift``, ``Ctrl``, ``Alt`` increment the :class:`FloatSpin` value by the - product of the factors; -- ``PgUp`` & ``PgDn`` = 10 * increment * the product of the ``Shift``, ``Ctrl``, ``Alt`` - factors; -- ``Space`` sets the control's value to it's last valid state. - - -Window Styles -============= - -This class supports the following window styles: - -=============== =========== ================================================== -Window Styles Hex Value Description -=============== =========== ================================================== -``FS_READONLY`` 0x1 Sets :class:`FloatSpin` as read-only control. -``FS_LEFT`` 0x2 Horizontally align the underlying :class:`TextCtrl` on the left. -``FS_CENTRE`` 0x4 Horizontally align the underlying :class:`TextCtrl` on center. -``FS_RIGHT`` 0x8 Horizontally align the underlying :class:`TextCtrl` on the right. -=============== =========== ================================================== - - -Events Processing -================= - -This class processes the following events: - -================= ================================================== -Event Name Description -================= ================================================== -``EVT_FLOATSPIN`` Emitted when the user changes the value of :class:`FloatSpin`, either with the mouse or with the keyboard. -================= ================================================== - - -License And Version -=================== - -:class:`FloatSpin` control is distributed under the wxPython license. - -Latest revision: Andrea Gavana @ 03 Jan 2014, 23.00 GMT - -Version 0.9 - - -Backward Incompatibilities -========================== - -Modifications to allow `min_val` or `max_val` to be ``None`` done by: - -James Bigler, -SCI Institute, University of Utah, -March 14, 2007 - -:note: Note that the changes I made will break backward compatibility, - because I changed the contructor's parameters from `min` / `max` to - `min_val` / `max_val` to be consistent with the other functions and to - eliminate any potential confusion with the built in `min` and `max` - functions. - -You specify open ranges like this (you can equally do this in the -constructor):: - - SetRange(min_val=1, max_val=None) # [1, ] - SetRange(min_val=None, max_val=0) # [ , 0] - -or no range:: - - SetRange(min_val=None, max_val=None) # [ , ] - -""" - -# ---------------------------------------------------------------------- -# Beginning Of FLOATSPIN wxPython Code -# ---------------------------------------------------------------------- - -import wx -import locale -from math import ceil, floor - -# Set The Styles For The Underline wx.TextCtrl -FS_READONLY = 1 -""" Sets :class:`FloatSpin` as read-only control. """ -FS_LEFT = 2 -""" Horizontally align the underlying :class:`TextCtrl` on the left. """ -FS_CENTRE = 4 -""" Horizontally align the underlying :class:`TextCtrl` on center. """ -FS_RIGHT = 8 -""" Horizontally align the underlying :class:`TextCtrl` on the right. """ - -# Define The FloatSpin Event -wxEVT_FLOATSPIN = wx.NewEventType() - -# -----------------------------------# -# FloatSpinEvent -# -----------------------------------# - -EVT_FLOATSPIN = wx.PyEventBinder(wxEVT_FLOATSPIN, 1) -""" Emitted when the user changes the value of :class:`FloatSpin`, either with the mouse or""" \ -""" with the keyboard. """ - - -def _cmp(self, a, b): - return (a>b)-(a 0: - self.SetValue(self._value + self._increment * modifier) - self.DoSendEvent() - - else: - - self.SetValue(self._value - self._increment * modifier) - self.DoSendEvent() - - def OnSize(self, event): - """ - Handles the ``wx.EVT_SIZE`` event for :class:`FloatSpin`. - - :param `event`: a :class:`SizeEvent` event to be processed. - - :note: This method resizes the text control and reposition the spin button when - resized. - """ - # start Philip Semanchuk addition - event_width = event.GetSize().width - - self._textctrl.SetPosition((self._text_left, self._text_top)) - - text_width, text_height = self._textctrl.GetSizeTuple() - - spin_width, _ = self._spinbutton.GetSizeTuple() - - text_width = event_width - (spin_width + self._gap + self._text_left) - - self._textctrl.SetSize(wx.Size(text_width, event.GetSize().height)) - - # The spin button is always snug against the right edge of the - # control. - self._spinbutton.SetPosition((event_width - spin_width, self._spin_top)) - - event.Skip() - # end Philip Semanchuk addition - - def ReplaceDoubleZero(self, strs): - """ - Replaces the (somewhat) python ugly `+e000` with `+e00`. - - :param `strs`: a string (possibly) containing a `+e00` substring. - """ - - if self._textformat not in ["%g", "%e", "%E", "%G"]: - return strs - - if strs.find("e+00") >= 0: - strs = strs.replace("e+00", "e+0") - elif strs.find("e-00") >= 0: - strs = strs.replace("e-00", "e-0") - elif strs.find("E+00") >= 0: - strs = strs.replace("E+00", "E+0") - elif strs.find("E-00") >= 0: - strs = strs.replace("E-00", "E-0") - - return strs - - def SetValue(self, value): - """ - Sets the :class:`FloatSpin` value. - - :param `value`: the new value. - """ - if not self._textctrl or not self.InRange(value): - return - - if self._snapticks and self._increment != 0.0: - - finite, snap_value = self.IsFinite(value) - - if not finite: # FIXME What To Do About A Failure? - - if (snap_value - floor(snap_value) < ceil(snap_value) - snap_value): - value = self._defaultvalue + floor(snap_value) * self._increment - else: - value = self._defaultvalue + ceil(snap_value) * self._increment - - decimal = locale.localeconv()["decimal_point"] - strs = ("%100." + str(self._digits) + self._textformat[1]) % value - strs = strs.replace(".", decimal) - strs = strs.strip() - strs = self.ReplaceDoubleZero(strs) - - if value != self._value or strs != self._textctrl.GetValue(): - self._textctrl.SetValue(strs) - self._textctrl.DiscardEdits() - self._value = value - - def GetValue(self): - """ Returns the :class:`FloatSpin` value. """ - - return float(self._value) - - def SetRangeDontClampValue(self, min_val, max_val): - """ - Sets the allowed range. - - :param `min_val`: the minimum value for :class:`FloatSpin`. If it is ``None`` it is - ignored; - :param `max_val`: the maximum value for :class:`FloatSpin`. If it is ``None`` it is - ignored. - - :note: This method doesn't modify the current value. - """ - - if (min_val != None): - self._min = FixedPoint(str(min_val), 20) - else: - self._min = None - if (max_val != None): - self._max = FixedPoint(str(max_val), 20) - else: - self._max = None - - def SetRange(self, min_val, max_val): - """ - Sets the allowed range. - - :param `min_val`: the minimum value for :class:`FloatSpin`. If it is ``None`` it is - ignored; - :param `max_val`: the maximum value for :class:`FloatSpin`. If it is ``None`` it is - ignored. - - :note: This method doesn't modify the current value. - - :note: You specify open ranges like this (you can equally do this in the - constructor):: - - SetRange(min_val=1, max_val=None) - SetRange(min_val=None, max_val=0) - - - or no range:: - - SetRange(min_val=None, max_val=None) - - """ - - self.SetRangeDontClampValue(min_val, max_val) - - value = self.ClampValue(self._value) - if (value != self._value): - self.SetValue(value) - - def ClampValue(self, var): - """ - Clamps `var` between `_min` and `_max` depending if the range has - been specified. - - :param `var`: the value to be clamped. - - :return: A clamped copy of `var`. - """ - - if (self._min != None): - if (var < self._min): - var = self._min - return var - - if (self._max != None): - if (var > self._max): - var = self._max - - return var - - def SetIncrement(self, increment): - """ - Sets the increment for every ``EVT_FLOATSPIN`` event. - - :param `increment`: a floating point number specifying the :class:`FloatSpin` increment. - """ - - if increment < 1. / 10.0 ** self._digits: - raise Exception("\nERROR: Increment Should Be Greater Or Equal To 1/(10**digits).") - - self._increment = FixedPoint(str(increment), 20) - self.SetValue(self._value) - - def GetIncrement(self): - """ Returns the increment for every ``EVT_FLOATSPIN`` event. """ - - return self._increment - - def SetDigits(self, digits=-1): - """ - Sets the number of digits to show. - - :param `digits`: the number of digits to show. If `digits` < 0, :class:`FloatSpin` - tries to calculate the best number of digits based on input values passed - in the constructor. - """ - - if digits < 0: - incr = str(self._increment) - if incr.find(".") < 0: - digits = 0 - else: - digits = len(incr[incr.find(".") + 1:]) - - self._digits = digits - - self.SetValue(self._value) - - def GetDigits(self): - """ Returns the number of digits shown. """ - - return self._digits - - def SetFormat(self, fmt="%f"): - """ - Set the string format to use. - - :param `fmt`: the new string format to use. One of the following strings: - - ====== ================================= - Format Description - ====== ================================= - 'e' Floating point exponential format (lowercase) - 'E' Floating point exponential format (uppercase) - 'f' Floating point decimal format - 'F' Floating point decimal format - 'g' Floating point format. Uses lowercase exponential format if exponent is less than -4 or not less than precision, decimal format otherwise - 'G' Floating point format. Uses uppercase exponential format if exponent is less than -4 or not less than precision, decimal format otherwise - ====== ================================= - - """ - - if fmt not in ["%f", "%g", "%e", "%E", "%F", "%G"]: - raise Exception('\nERROR: Bad Float Number Format: ' + repr(fmt) + '. It Should Be ' \ - 'One Of "%f", "%g", "%e", "%E", "%F", "%G"') - - self._textformat = fmt - - if self._digits < 0: - self.SetDigits() - - self.SetValue(self._value) - - def GetFormat(self): - """ - Returns the string format in use. - - :see: :meth:`~FloatSpin.SetFormat` for a list of valid string formats. - """ - - return self._textformat - - def SetDefaultValue(self, defaultvalue): - """ - Sets the :class:`FloatSpin` default value. - - :param `defaultvalue`: a floating point value representing the new default - value for :class:`FloatSpin`. - """ - - if self.InRange(defaultvalue): - self._defaultvalue = FixedPoint(str(defaultvalue), 20) - - def GetDefaultValue(self): - """ Returns the :class:`FloatSpin` default value. """ - - return self._defaultvalue - - def IsDefaultValue(self): - """ Returns whether the current value is the default value or not. """ - - return self._value == self._defaultvalue - - def SetToDefaultValue(self): - """ Sets :class:`FloatSpin` value to its default value. """ - - self.SetValue(self._defaultvalue) - - def SetSnapToTicks(self, forceticks=True): - """ - Force the value to always be divisible by the increment. Initially ``False``. - - :param `forceticks`: ``True`` to force the snap to ticks option, ``False`` otherwise. - - :note: This uses the default value as the basis, you will get strange results - for very large differences between the current value and default value - when the increment is very small. - """ - - if self._snapticks != forceticks: - self._snapticks = forceticks - self.SetValue(self._value) - - def GetSnapToTicks(self): - """ Returns whether the snap to ticks option is active or not. """ - - return self._snapticks - - def OnFocus(self, event): - """ - Handles the ``wx.EVT_SET_FOCUS`` event for :class:`FloatSpin`. - - :param `event`: a :class:`FocusEvent` event to be processed. - """ - - if self._textctrl: - self._textctrl.SetFocus() - - event.Skip() - - def OnKillFocus(self, event): - """ - Handles the ``wx.EVT_KILL_FOCUS`` event for :class:`FloatSpin`. - - :param `event`: a :class:`FocusEvent` event to be processed. - """ - - self.SyncSpinToText(True) - event.Skip() - - def SyncSpinToText(self, send_event=True, force_valid=True): - """ - Synchronize the underlying :class:`TextCtrl` with :class:`SpinButton`. - - :param `send_event`: ``True`` to send a ``EVT_FLOATSPIN`` event, ``False`` - otherwise; - :param `force_valid`: ``True`` to force a valid value (i.e. inside the - provided range), ``False`` otherwise. - """ - - if not self._textctrl: - return - - curr = self._textctrl.GetValue() - curr = curr.strip() - decimal = locale.localeconv()["decimal_point"] - curr = curr.replace(decimal, ".") - - if curr: - try: - curro = float(curr) - curr = FixedPoint(curr, 20) - except: - self.SetValue(self._value) - return - - if force_valid or not self.HasRange() or self.InRange(curr): - - if force_valid and self.HasRange(): - curr = self.ClampValue(curr) - - if self._value != curr: - self.SetValue(curr) - - if send_event: - self.DoSendEvent() - - elif force_valid: - - # textctrl is out of sync, discard and reset - self.SetValue(self.GetValue()) - - def SetFont(self, font=None): - """ - Sets the underlying :class:`TextCtrl` font. - - :param `font`: a valid instance of :class:`Font`. - """ - - if font is None: - font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) - - if not self._textctrl: - return False - - return self._textctrl.SetFont(font) - - def GetFont(self): - """ Returns the underlying :class:`TextCtrl` font. """ - - if not self._textctrl: - return self.GetFont() - - return self._textctrl.GetFont() - - def GetMin(self): - """ - Returns the minimum value for :class:`FloatSpin`. It can be a - number or ``None`` if no minimum is present. - """ - - return self._min - - def GetMax(self): - """ - Returns the maximum value for :class:`FloatSpin`. It can be a - number or ``None`` if no minimum is present. - """ - - return self._max - - def HasRange(self): - """ Returns whether :class:`FloatSpin` range has been set or not. """ - - return (self._min != None) or (self._max != None) - - def InRange(self, value): - """ - Returns whether a value is inside :class:`FloatSpin` range. - - :param `value`: the value to test. - """ - - if (not self.HasRange()): - return True - if (self._min != None): - if (value < self._min): - return False - if (self._max != None): - if (value > self._max): - return False - return True - - def GetTextCtrl(self): - """ Returns the underlying :class:`TextCtrl`. """ - - return self._textctrl - - def IsFinite(self, value): - """ - Tries to determine if a value is finite or infinite/NaN. - - :param `value`: the value to test. - """ - - try: - snap_value = (value - self._defaultvalue) / self._increment - finite = True - except: - finite = False - snap_value = None - - return finite, snap_value - - -# Class FixedPoint, version 0.0.4. -# Released to the public domain 28-Mar-2001, -# by Tim Peters (tim.one@home.com). - -# Provided as-is; use at your own risk; no warranty; no promises; enjoy! - - -# 28-Mar-01 ver 0.0,4 -# Use repr() instead of str() inside __str__, because str(long) changed -# since this was first written (used to produce trailing "L", doesn't -# now). -# -# 09-May-99 ver 0,0,3 -# Repaired __sub__(FixedPoint, string); was blowing up. -# Much more careful conversion of float (now best possible). -# Implemented exact % and divmod. -# -# 14-Oct-98 ver 0,0,2 -# Added int, long, frac. Beefed up docs. Removed DECIMAL_POINT -# and MINUS_SIGN globals to discourage bloating this class instead -# of writing formatting wrapper classes (or subclasses) -# -# 11-Oct-98 ver 0,0,1 -# posted to c.l.py - -__version__ = 0, 0, 4 - -# The default value for the number of decimal digits carried after the -# decimal point. This only has effect at compile-time. -DEFAULT_PRECISION = 2 -""" The default value for the number of decimal digits carried after the decimal point. This only has effect at compile-time. """ - - -class FixedPoint(object): - """ - FixedPoint objects support decimal arithmetic with a fixed number of - digits (called the object's precision) after the decimal point. The - number of digits before the decimal point is variable & unbounded. - - The precision is user-settable on a per-object basis when a FixedPoint - is constructed, and may vary across FixedPoint objects. The precision - may also be changed after construction via `FixedPoint.set_precision(p)`. - Note that if the precision of a FixedPoint is reduced via :meth:`FixedPoint.set_precision() `, - information may be lost to rounding. - - Example:: - - >>> x = FixedPoint("5.55") # precision defaults to 2 - >>> print x - 5.55 - >>> x.set_precision(1) # round to one fraction digit - >>> print x - 5.6 - >>> print FixedPoint("5.55", 1) # same thing setting to 1 in constructor - 5.6 - >>> repr(x) # returns constructor string that reproduces object exactly - "FixedPoint('5.6', 1)" - >>> - - - When :class:`FixedPoint` objects of different precision are combined via + - * /, - the result is computed to the larger of the inputs' precisions, which also - becomes the precision of the resulting :class:`FixedPoint` object. Example:: - - >>> print FixedPoint("3.42") + FixedPoint("100.005", 3) - 103.425 - >>> - - - When a :class:`FixedPoint` is combined with other numeric types (ints, floats, - strings representing a number) via + - * /, then similarly the computation - is carried out using -- and the result inherits -- the :class:`FixedPoint`'s - precision. Example:: - - >>> print FixedPoint(1) / 7 - 0.14 - >>> print FixedPoint(1, 30) / 7 - 0.142857142857142857142857142857 - >>> - - - The string produced by `str(x)` (implictly invoked by `print`) always - contains at least one digit before the decimal point, followed by a - decimal point, followed by exactly `x.get_precision()` digits. If `x` is - negative, `str(x)[0] == "-"`. - - The :class:`FixedPoint` constructor can be passed an int, long, string, float, - :class:`FixedPoint`, or any object convertible to a float via `float()` or to a - long via `long()`. Passing a precision is optional; if specified, the - precision must be a non-negative int. There is no inherent limit on - the size of the precision, but if very very large you'll probably run - out of memory. - - Note that conversion of floats to :class:`FixedPoint` can be surprising, and - should be avoided whenever possible. Conversion from string is exact - (up to final rounding to the requested precision), so is greatly - preferred. Example:: - - >>> print FixedPoint(1.1e30) - 1099999999999999993725589651456.00 - >>> print FixedPoint("1.1e30") - 1100000000000000000000000000000.00 - >>> - - - """ - - # the exact value is self.n / 10**self.p; - # self.n is a long; self.p is an int - - def __init__(self, value=0, precision=DEFAULT_PRECISION): - """ - Default class constructor. - - :param `value`: the initial value; - :param `precision`: must be an int >= 0, and defaults to ``DEFAULT_PRECISION``. - """ - - self.n = self.p = 0 - self.set_precision(precision) - p = self.p - - if isinstance(value, type("42.3e5")): - n, exp = _string2exact(value) - # exact value is n*10**exp = n*10**(exp+p)/10**p - effective_exp = exp + p - if effective_exp > 0: - n = n * _tento(effective_exp) - elif effective_exp < 0: - n = _roundquotient(n, _tento(-effective_exp)) - self.n = n - return - - if isinstance(value, type(42)) or isinstance(value, type(42)): - self.n = int(value) * _tento(p) - return - - if isinstance(value, FixedPoint): - temp = value.copy() - temp.set_precision(p) - self.n, self.p = temp.n, temp.p - return - - if isinstance(value, type(42.0)): - # XXX ignoring infinities and NaNs and overflows for now - import math - f, e = math.frexp(abs(value)) - assert f == 0 or 0.5 <= f < 1.0 - # |value| = f * 2**e exactly - - # Suck up CHUNK bits at a time; 28 is enough so that we suck - # up all bits in 2 iterations for all known binary double- - # precision formats, and small enough to fit in an int. - CHUNK = 28 - top = 0 - # invariant: |value| = (top + f) * 2**e exactly - while f: - f = math.ldexp(f, CHUNK) - digit = int(f) - assert digit >> CHUNK == 0 - top = (top << CHUNK) | digit - f = f - digit - assert 0.0 <= f < 1.0 - e = e - CHUNK - - # now |value| = top * 2**e exactly - # want n such that n / 10**p = top * 2**e, or - # n = top * 10**p * 2**e - top = top * _tento(p) - if e >= 0: - n = top << e - else: - n = _roundquotient(top, 1 << -e) - if value < 0: - n = -n - self.n = n - return - - if isinstance(value, type(42 - 42j)): - raise TypeError("can't convert complex to FixedPoint: " + - repr(value)) - - # can we coerce to a float? - yes = 1 - try: - asfloat = float(value) - except: - yes = 0 - if yes: - self.__init__(asfloat, p) - return - - # similarly for long - yes = 1 - try: - aslong = int(value) - except: - yes = 0 - if yes: - self.__init__(aslong, p) - return - - raise TypeError("can't convert to FixedPoint: " + repr(value)) - - def get_precision(self): - """ - Return the precision of this :class:`FixedPoint`. - - :note: The precision is the number of decimal digits carried after - the decimal point, and is an int >= 0. - """ - - return self.p - - def set_precision(self, precision=DEFAULT_PRECISION): - """ - Change the precision carried by this :class:`FixedPoint` to `precision`. - - :param `precision`: must be an int >= 0, and defaults to - ``DEFAULT_PRECISION``. - - :note: If `precision` is less than this :class:`FixedPoint`'s current precision, - information may be lost to rounding. - """ - - try: - p = int(precision) - except: - raise TypeError("precision not convertable to int: " + - repr(precision)) - if p < 0: - raise ValueError("precision must be >= 0: " + repr(precision)) - - if p > self.p: - self.n = self.n * _tento(p - self.p) - elif p < self.p: - self.n = _roundquotient(self.n, _tento(self.p - p)) - self.p = p - - def __str__(self): - - n, p = self.n, self.p - i, f = divmod(abs(n), _tento(p)) - if p: - frac = repr(f)[:-1] - frac = "0" * (p - len(frac)) + frac - else: - frac = "" - return "-"[:n < 0] + \ - repr(i)[:-1] + \ - "." + frac - - def __repr__(self): - - return "FixedPoint" + repr((str(self), self.p)) - - def copy(self): - """ Create a copy of the current :class:`FixedPoint`. """ - - return _mkFP(self.n, self.p) - - __copy__ = __deepcopy__ = copy - - def __cmp__(self, other): - - if (other == None): - return 1 - xn, yn, p = _norm(self, other) - return _cmp(xn, yn) - - def __hash__(self): - # caution! == values must have equal hashes, and a FixedPoint - # is essentially a rational in unnormalized form. There's - # really no choice here but to normalize it, so hash is - # potentially expensive. - n, p = self.__reduce() - - # Obscurity: if the value is an exact integer, p will be 0 now, - # so the hash expression reduces to hash(n). So FixedPoints - # that happen to be exact integers hash to the same things as - # their int or long equivalents. This is Good. But if a - # FixedPoint happens to have a value exactly representable as - # a float, their hashes may differ. This is a teensy bit Bad. - return hash(n) ^ hash(p) - - def __bool__(self): - return self.n != 0 - - def __neg__(self): - return _mkFP(-self.n, self.p) - - def __abs__(self): - if self.n >= 0: - return self.copy() - else: - return -self - - def __add__(self, other): - n1, n2, p = _norm(self, other) - # n1/10**p + n2/10**p = (n1+n2)/10**p - return _mkFP(n1 + n2, p) - - __radd__ = __add__ - - def __sub__(self, other): - if not isinstance(other, FixedPoint): - other = FixedPoint(other, self.p) - return self.__add__(-other) - - def __rsub__(self, other): - return (-self) + other - - def __mul__(self, other): - n1, n2, p = _norm(self, other) - # n1/10**p * n2/10**p = (n1*n2/10**p)/10**p - return _mkFP(_roundquotient(n1 * n2, _tento(p)), p) - - __rmul__ = __mul__ - - def __div__(self, other): - n1, n2, p = _norm(self, other) - if n2 == 0: - raise ZeroDivisionError("FixedPoint division") - if n2 < 0: - n1, n2 = -n1, -n2 - # n1/10**p / (n2/10**p) = n1/n2 = (n1*10**p/n2)/10**p - return _mkFP(_roundquotient(n1 * _tento(p), n2), p) - - def __rdiv__(self, other): - n1, n2, p = _norm(self, other) - return _mkFP(n2, p) / self - - def __divmod__(self, other): - n1, n2, p = _norm(self, other) - if n2 == 0: - raise ZeroDivisionError("FixedPoint modulo") - # floor((n1/10**p)/(n2*10**p)) = floor(n1/n2) - q = n1 / n2 - # n1/10**p - q * n2/10**p = (n1 - q * n2)/10**p - return q, _mkFP(n1 - q * n2, p) - - def __rdivmod__(self, other): - n1, n2, p = _norm(self, other) - return divmod(_mkFP(n2, p), self) - - def __mod__(self, other): - return self.__divmod__(other)[1] - - def __rmod__(self, other): - n1, n2, p = _norm(self, other) - return _mkFP(n2, p).__mod__(self) - - # caution! float can lose precision - def __float__(self): - n, p = self.__reduce() - return float(n) / float(_tento(p)) - - # XXX should this round instead? - # XXX note e.g. long(-1.9) == -1L and long(1.9) == 1L in Python - # XXX note that __int__ inherits whatever __long__ does, - # XXX and .frac() is affected too - def __long__(self): - answer = abs(self.n) / _tento(self.p) - if self.n < 0: - answer = -answer - return answer - - def __int__(self): - return int(self.__long__()) - - def frac(self): - """ - Returns fractional portion as a :class:`FixedPoint`. - - :note: In :class:`FixedPoint`, - - this equality holds true:: - - x = x.frac() + long(x) - - - """ - return self - int(self) - - # return n, p s.t. self == n/10**p and n % 10 != 0 - def __reduce(self): - n, p = self.n, self.p - if n == 0: - p = 0 - while p and n % 10 == 0: - p = p - 1 - n = n / 10 - return n, p - - -# return 10L**n - -def _tento(n, cache={}): - try: - return cache[n] - except KeyError: - answer = cache[n] = 10 ** n - return answer - - -# return xn, yn, p s.t. -# p = max(x.p, y.p) -# x = xn / 10**p -# y = yn / 10**p -# -# x must be FixedPoint to begin with; if y is not FixedPoint, -# it inherits its precision from x. -# -# Note that this is called a lot, so default-arg tricks are helpful. - -def _norm(x, y, isinstance=isinstance, FixedPoint=FixedPoint, - _tento=_tento): - assert isinstance(x, FixedPoint) - if not isinstance(y, FixedPoint): - y = FixedPoint(y, x.p) - xn, yn = x.n, y.n - xp, yp = x.p, y.p - if xp > yp: - yn = yn * _tento(xp - yp) - p = xp - elif xp < yp: - xn = xn * _tento(yp - xp) - p = yp - else: - p = xp # same as yp - return xn, yn, p - - -def _mkFP(n, p, FixedPoint=FixedPoint): - f = FixedPoint() - f.n = n - f.p = p - return f - - -# divide x by y, rounding to int via nearest-even -# y must be > 0 -# XXX which rounding modes are useful? - -def _roundquotient(x, y): - assert y > 0 - n, leftover = divmod(x, y) - c = _cmp(leftover << 1, y) - # c < 0 <-> leftover < y/2, etc - if c > 0 or (c == 0 and (n & 1) == 1): - n = n + 1 - return n - - -# crud for parsing strings -import re - -# There's an optional sign at the start, and an optional exponent -# at the end. The exponent has an optional sign and at least one -# digit. In between, must have either at least one digit followed -# by an optional fraction, or a decimal point followed by at least -# one digit. Yuck. - -_parser = re.compile(r""" - \s* - (?P[-+])? - ( - (?P\d+) (\. (?P\d*))? - | - \. (?P\d+) - ) - ([eE](?P[-+]? \d+))? - \s* $ -""", re.VERBOSE).match - -del re - - -# return n, p s.t. float string value == n * 10**p exactly - -def _string2exact(s): - m = _parser(s) - if m is None: - raise ValueError("can't parse as number: " + repr(s)) - - exp = m.group('exp') - if exp is None: - exp = 0 - else: - exp = int(exp) - - intpart = m.group('int') - if intpart is None: - intpart = "0" - fracpart = m.group('onlyfrac') - else: - fracpart = m.group('frac') - if fracpart is None or fracpart == "": - fracpart = "0" - assert intpart - assert fracpart - - i, f = int(intpart), int(fracpart) - nfrac = len(fracpart) - i = i * _tento(nfrac) + f - exp = exp - nfrac - - if m.group('sign') == "-": - i = -i - - return i, exp From cebca64e5e565140f164adfdf77e93b49f85ecf0 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 5 Nov 2017 01:28:57 -0500 Subject: [PATCH 047/212] Fix CREST stuff --- gui/crestFittings.py | 6 ++++-- gui/mainFrame.py | 2 +- service/crest.py | 21 +++++++++++---------- service/server.py | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 82d49d101..968e5e18b 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -15,6 +15,7 @@ from gui.display import Display import gui.globalEvents as GE from logbook import Logger +import calendar pyfalog = Logger(__name__) if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): @@ -112,7 +113,8 @@ class CrestFittings(wx.Frame): def updateCacheStatus(self, event): t = time.gmtime(self.cacheTime - time.time()) - if t < 0: + + if calendar.timegm(t) < 0: # calendar.timegm gets seconds until time given self.cacheTimer.Stop() else: sTime = time.strftime("%H:%M:%S", t) @@ -222,7 +224,7 @@ class ExportToEve(wx.Frame): self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoLogin) self.Bind(wx.EVT_CLOSE, self.OnClose) - self.SetSizer(hSizer) + self.SetSizer(mainSizer) self.SetStatusBar(self.statusbar) self.Layout() diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 61c012e72..d03d2fe76 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -614,7 +614,7 @@ class MainFrame(wx.Frame): char = sCrest.implicitCharacter if char: t = time.gmtime(char.eve.expires - time.time()) - sTime = time.strftime("%H:%M:%S", t if t >= 0 else 0) + sTime = time.strftime("%H:%M:%S", t) newTitle = "%s | %s - %s" % (self.title, char.name, sTime) self.SetTitle(newTitle) diff --git a/service/crest.py b/service/crest.py index 27f5b6c5e..ff1765298 100644 --- a/service/crest.py +++ b/service/crest.py @@ -75,14 +75,15 @@ class Crest(object): self.state = None self.ssoTimer = None + self.eve_options = { + '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 + } + # 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')), - api_key=self.settings.get('clientSecret') if self.settings.get('mode') == CrestModes.USER else None, - redirect_uri=self.clientCallback, - testing=self.isTestServer - ) + self.eve = EVE(**self.eve_options) self.implicitCharacter = None @@ -131,7 +132,7 @@ class Crest(object): char = eos.db.getCrestCharacter(charID) if char and not hasattr(char, "eve"): - char.eve = copy.deepcopy(self.eve) + char.eve = EVE(**self.eve_options) char.eve.temptoken_authorize(refresh_token=char.refresh_token) self.charCache[charID] = char return char @@ -188,7 +189,7 @@ class Crest(object): pyfalog.debug("Handling CREST login with: {0}", message) if 'access_token' in message: # implicit - eve = copy.deepcopy(self.eve) + eve = EVE(**self.eve_options) eve.temptoken_authorize( access_token=message['access_token'][0], expires_in=int(message['expires_in'][0]) @@ -207,7 +208,7 @@ class Crest(object): wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.IMPLICIT)) elif 'code' in message: - eve = copy.deepcopy(self.eve) + eve = EVE(**self.eve_options) eve.authorize(message['code'][0]) eve() info = eve.whoami() diff --git a/service/server.py b/service/server.py index 3f28c5cf2..76c5a964e 100644 --- a/service/server.py +++ b/service/server.py @@ -95,7 +95,7 @@ class AuthHandler(http.server.BaseHTTPRequestHandler): finally: self.send_response(200) self.end_headers() - self.wfile.write(HTML.format(msg)) + self.wfile.write(str.encode(HTML.format(msg))) if step2: # Only stop once if we've received something in the querystring From 0cc646bab9443d57689573c431bc9881a928362c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 5 Nov 2017 02:29:58 -0500 Subject: [PATCH 048/212] Fix gauge resetting to 0 each time (should figure out how to utilize the existing set range and value, but for now this works) --- gui/pyfa_gauge.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index b41cab8d2..8a1ef61a5 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -186,17 +186,30 @@ class PyGauge(wx.Window): def SetValueRange(self, value, range, reinit=False): """ Set both value and range of the gauge. """ + range_ = float(range) - self.SetRange(range, reinit, animate=False) - self.SetValue(value, animate=False) + if range_ <= 0: + self._max_range = 0.01 + else: + self._max_range = range_ + + value = float(value) + + self._value = value + if value < 0: + self._value = float(0) + + if reinit is False: + self._old_percentage = self._percentage + self._percentage = (self._value / self._max_range) * 100 + + else: + self._old_percentage = self._percentage + self._percentage = 0 self.Animate() self._tooltip.SetTip("%.2f/%.2f" % - (self._value, - self._max_range if self._max_range > 0.01 else 0)) - - def OnEraseBackground(self, event): - pass + (self._value, self._max_range if float(self._max_range) > 0.01 else 0)) def OnPaint(self, event): dc = wx.BufferedPaintDC(self) From 266398b1de407535b5395ee8fefc4207e66f61e1 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 12 Nov 2017 00:06:03 -0500 Subject: [PATCH 049/212] Get some things working for pyinstaller --- config.py | 9 +++-- eos/db/migrations/__init__.py | 27 +++++++++++++- eos/gamedata.py | 2 +- gui/pyfa_gauge.py | 4 +- pyfa.py | 1 + pyfa.spec | 66 +++++++++++++++++++++++++++++++++ service/conversions/__init__.py | 29 ++++++++++++++- 7 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 pyfa.spec diff --git a/config.py b/config.py index 50b0344d5..f097385a4 100644 --- a/config.py +++ b/config.py @@ -86,9 +86,9 @@ def defPaths(customSavePath): __createDirs(savePath) - if isFrozen(): - os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8') - os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8') + #if isFrozen(): + # os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem") + # os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") # The database where we store all the fits etc saveDB = os.path.join(savePath, "saveddata.db") @@ -109,6 +109,9 @@ def defPaths(customSavePath): eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False" eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False" + print(eos.config.saveddata_connectionstring) + print(eos.config.gamedata_connectionstring) + # initialize the settings from service.settings import EOSSettings eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever diff --git a/eos/db/migrations/__init__.py b/eos/db/migrations/__init__.py index 94b33409a..1dd4508a5 100644 --- a/eos/db/migrations/__init__.py +++ b/eos/db/migrations/__init__.py @@ -15,7 +15,32 @@ updates = {} appVersion = 0 prefix = __name__ + "." -for importer, modname, ispkg in pkgutil.iter_modules(__path__, prefix): + +# load modules to work based with and without pyinstaller +# from: https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py +# see: https://github.com/pyinstaller/pyinstaller/issues/1905 + +# load modules using iter_modules() +# (should find all filters in normal build, but not pyinstaller) +module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)] + +print ("module names from iter_modules: ", module_names) + +# special handling for PyInstaller +importers = map(pkgutil.get_importer, __path__) +toc = set() +for i in importers: + if hasattr(i, 'toc'): + toc |= i.toc + +for elm in toc: + if elm.startswith(prefix): + module_names.append(elm) + +print("module names after get_importer toc: ", module_names) + +for modname in module_names: + print(modname) # loop through python files, extracting update number and function, and # adding it to a list modname_tail = modname.rsplit('.', 1)[-1] diff --git a/eos/gamedata.py b/eos/gamedata.py index 03d64957c..a6a03714e 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -525,7 +525,7 @@ class MarketGroup(EqBase): def __repr__(self): return "MarketGroup(ID={}, name={}, parent={}) at {}".format( self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self)) - ).encode('utf8') + ) class MetaGroup(EqBase): diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index 8a1ef61a5..b6b514015 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -394,7 +394,7 @@ if __name__ == "__main__": yield x x += jump - class TestPanel(wx.Panel): + class MyPanel(wx.Panel): def __init__(self, parent, size=(500, 500)): wx.Panel.__init__(self, parent, size=size) box = wx.BoxSizer(wx.VERTICAL) @@ -432,7 +432,7 @@ if __name__ == "__main__": wx.Frame.__init__(self, None, title=title, size=size) self.statusbar = self.CreateStatusBar() main_sizer = wx.BoxSizer(wx.VERTICAL) - panel = TestPanel(self, size=size) + panel = MyPanel(self, size=size) main_sizer.Add(panel) self.SetSizer(main_sizer) diff --git a/pyfa.py b/pyfa.py index 7f0b99140..efa7d5e8c 100755 --- a/pyfa.py +++ b/pyfa.py @@ -184,6 +184,7 @@ parser.add_option("-l", "--logginglevel", action="store", dest="logginglevel", h if __name__ == "__main__": # Configure paths + print ('starting') if options.rootsavedata is True: config.saveInRoot = True diff --git a/pyfa.spec b/pyfa.spec new file mode 100644 index 000000000..efebfd674 --- /dev/null +++ b/pyfa.spec @@ -0,0 +1,66 @@ +# -*- mode: python -*- + +import os +from itertools import chain + +block_cipher = None + +added_files = [ + ( 'imgs/gui/*.png', 'imgs/gui' ), + ( 'imgs/gui/*.gif', 'imgs/gui' ), + ( 'imgs/icons/*.png', 'imgs/icons' ), + ( 'imgs/renders/*.png', 'imgs/renders' ), + ( 'dist_assets/win/pyfa.ico', '.' ), + ( 'dist_assets/cacert.pem', '.' ), + ( 'eve.db', '.' ), + ( 'README.md', '.' ), + ( 'LICENSE', '.' ), + ] + +import_these = [] + +# Walk directories that do dynamic importing +paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') +for root, folders, files in chain.from_iterable(os.walk(path) for path in paths): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + import_these.append(mod_name) + +a = Analysis([r'C:\Users\Ryan\Sync\Git\blitzmann\Pyfa\pyfa.py'], + pathex=[r'C:\Users\Ryan\Sync\Git\blitzmann\Pyfa'], + binaries=[], + datas=added_files, + hiddenimports=import_these, + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + debug=False, + console=True, + strip=False, + upx=True, + name='pyfa', + icon='dist_assets/win/pyfa.ico', + ) + +coll = COLLECT( + exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + name='pyfa', + icon='dist_assets/win/pyfa.ico', + ) \ No newline at end of file diff --git a/service/conversions/__init__.py b/service/conversions/__init__.py index c9ee794c3..baab9200e 100644 --- a/service/conversions/__init__.py +++ b/service/conversions/__init__.py @@ -9,6 +9,8 @@ elsewhere (in which case can be accessed with packs[name]) import pkgutil + + # init parent dict all = {} @@ -17,7 +19,32 @@ packs = {} prefix = __name__ + "." -for importer, modname, ispkg in pkgutil.iter_modules(__path__, prefix): + +# load modules to work based with and without pyinstaller +# from: https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py +# see: https://github.com/pyinstaller/pyinstaller/issues/1905 + +# load modules using iter_modules() +# (should find all filters in normal build, but not pyinstaller) +module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)] + +print ("module names from iter_modules: ", module_names) + +# special handling for PyInstaller +importers = map(pkgutil.get_importer, __path__) +toc = set() +for i in importers: + if hasattr(i, 'toc'): + toc |= i.toc + +for elm in toc: + if elm.startswith(prefix): + module_names.append(elm) + +print("module names after get_importer toc: ", module_names) + +for modname in module_names: + print (modname) conversionPack = __import__(modname, fromlist="dummy") all.update(conversionPack.CONVERSIONS) modname_tail = modname.rsplit('.', 1)[-1] From 21838f2d9af373b325d0d491416e806c0c6a3ff7 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 12 Nov 2017 02:15:15 -0500 Subject: [PATCH 050/212] Fix issue with pyinstaller not bundling required DLLs when building on Windows 10 / Python >= 3.5 --- pyfa.spec | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyfa.spec b/pyfa.spec index efebfd674..f330a16c7 100644 --- a/pyfa.spec +++ b/pyfa.spec @@ -31,7 +31,12 @@ for root, folders, files in chain.from_iterable(os.walk(path) for path in paths) import_these.append(mod_name) a = Analysis([r'C:\Users\Ryan\Sync\Git\blitzmann\Pyfa\pyfa.py'], - pathex=[r'C:\Users\Ryan\Sync\Git\blitzmann\Pyfa'], + pathex=[ + r'C:\Users\Ryan\Sync\Git\blitzmann\Pyfa', + # Need this, see https://github.com/pyinstaller/pyinstaller/issues/1566 + # To get this, download and install windows 10 SDK + # If not building on Windows 10, this might be optional + r'C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86'], binaries=[], datas=added_files, hiddenimports=import_these, From de646cf252c7659c48a6e6e734d51ff8b016efcb Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 12 Nov 2017 02:25:58 -0500 Subject: [PATCH 051/212] Have a noticeable message show up (currently prints for everything, should restrict to only beta builds) --- pyfa.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pyfa.py b/pyfa.py index efa7d5e8c..379f67de9 100755 --- a/pyfa.py +++ b/pyfa.py @@ -39,6 +39,26 @@ except ImportError: pyfalog = Logger(__name__) +ascii_text = ''' +++++++++++++++++++++++++++++++++++++++++++++++++++ + __ + / _| + _ __ _ _ | |_ __ _ + | '_ \ | | | || _|/ _` | + | |_) || |_| || | | (_| | + | .__/ \__, ||_| \__,_| + | | __/ | + |_| |___/ + +You are running a alpha/beta version of pyfa. +If you run into problems, please let me know at: +https://github.com/pyfa-org/Pyfa/issues + +++++++++++++++++++++++++++++++++++++++++++++++++++ +''' + +print(ascii_text) + class PassThroughOptionParser(OptionParser): """ An unknown option pass-through implementation of OptionParser. From d0235f6d93cd13aaee598aafe99611eaa4c89107 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 12 Nov 2017 12:00:24 -0500 Subject: [PATCH 052/212] Fix market browser meta filtering --- gui/marketBrowser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index c31c98df4..5ec608219 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -79,10 +79,10 @@ class MarketBrowser(wx.Panel): def toggleMetaButton(self, event): """Process clicks on toggle buttons""" - appendMeta = event.cmdDown + mstate = wx.GetMouseState() clickedBtn = event.EventObject - if appendMeta: + if mstate.cmdDown: activeBtns = [btn for btn in self.metaButtons if btn.GetValue()] if activeBtns: clickedBtn.setUserSelection(clickedBtn.GetValue()) From df7dc30e7e7f5aba1df0b33149bae8e8ae84ba8e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 12 Nov 2017 12:57:36 -0500 Subject: [PATCH 053/212] Write out git version information to a file, use in about box, remove all the other stuff until I can format it correctly --- .gitignore | 1 + config.py | 8 +++++++- gui/mainFrame.py | 50 ++++++++++++++++++++++++------------------------ pyfa.spec | 9 ++++++++- 4 files changed, 41 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 41586d1b5..c3530eab7 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,4 @@ ENV/ # Pycharm project settings .idea eos.iml +gitversion diff --git a/config.py b/config.py index f097385a4..05317583d 100644 --- a/config.py +++ b/config.py @@ -51,11 +51,17 @@ def getPyfaRoot(): return root +def getGitVersion(): + with open(os.path.join(pyfaPath, 'gitversion')) as f: + version = f.readline() + return version + + def getDefaultSave(): return os.path.expanduser(os.path.join("~", ".pyfa")) -def defPaths(customSavePath): +def defPaths(customSavePath=None): global debug global pyfaPath global savePath diff --git a/gui/mainFrame.py b/gui/mainFrame.py index d03d2fe76..a856c391b 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -359,31 +359,31 @@ class MainFrame(wx.Frame): v = sys.version_info info = wx.adv.AboutDialogInfo() info.Name = "pyfa" - info.Version = gui.aboutData.versionString - - try: - import matplotlib - matplotlib_version = matplotlib.__version__ - except: - matplotlib_version = None - - 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" + gamedata_version + - "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + - "\nwxPython: \t" + wx.__version__ + - "\nSQLAlchemy: \t" + sqlalchemy.__version__ + - "\nmatplotlib: \t {}".format(matplotlib_version if matplotlib_version else "Not Installed"), - 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") + info.Version = config.getGitVersion() # gui.aboutData.versionString + # + # try: + # import matplotlib + # matplotlib_version = matplotlib.__version__ + # except: + # matplotlib_version = None + # + # 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" + gamedata_version + + # "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + + # "\nwxPython: \t" + wx.__version__ + + # "\nSQLAlchemy: \t" + sqlalchemy.__version__ + + # "\nmatplotlib: \t {}".format(matplotlib_version if matplotlib_version else "Not Installed"), + # 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.adv.AboutBox(info) def showCharacterEditor(self, event): diff --git a/pyfa.spec b/pyfa.spec index f330a16c7..7671e380d 100644 --- a/pyfa.spec +++ b/pyfa.spec @@ -2,6 +2,13 @@ import os from itertools import chain +import subprocess + +label = subprocess.check_output([ + "git", "describe", "--tags"]).strip() + +with open('gitversion', 'w+') as f: + f.write(label.decode()) block_cipher = None @@ -15,6 +22,7 @@ added_files = [ ( 'eve.db', '.' ), ( 'README.md', '.' ), ( 'LICENSE', '.' ), + ( 'gitversion', '.' ), ] import_these = [] @@ -32,7 +40,6 @@ for root, folders, files in chain.from_iterable(os.walk(path) for path in paths) a = Analysis([r'C:\Users\Ryan\Sync\Git\blitzmann\Pyfa\pyfa.py'], pathex=[ - r'C:\Users\Ryan\Sync\Git\blitzmann\Pyfa', # Need this, see https://github.com/pyinstaller/pyinstaller/issues/1566 # To get this, download and install windows 10 SDK # If not building on Windows 10, this might be optional From e9be07f2812db822f1587ab43aab6e6492d1a030 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 12 Nov 2017 13:17:16 -0500 Subject: [PATCH 054/212] Rename pyfa.spec to pyfa-win.spec --- pyfa.spec => pyfa-win.spec | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pyfa.spec => pyfa-win.spec (100%) diff --git a/pyfa.spec b/pyfa-win.spec similarity index 100% rename from pyfa.spec rename to pyfa-win.spec From 1fc9b2941d20707847160b15b06b20c2abd7baeb Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 12 Nov 2017 13:43:19 -0500 Subject: [PATCH 055/212] Add spac file for mac --- pyfa-mac.spec | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 pyfa-mac.spec diff --git a/pyfa-mac.spec b/pyfa-mac.spec new file mode 100644 index 000000000..d10728185 --- /dev/null +++ b/pyfa-mac.spec @@ -0,0 +1,71 @@ +# -*- mode: python -*- + +import os +from itertools import chain +import subprocess + +label = subprocess.check_output([ + "git", "describe", "--tags"]).strip() + +with open('gitversion', 'w+') as f: + f.write(label.decode()) + +block_cipher = None + +added_files = [ + ( 'imgs/gui/*.png', 'imgs/gui' ), + ( 'imgs/gui/*.gif', 'imgs/gui' ), + ( 'imgs/icons/*.png', 'imgs/icons' ), + ( 'imgs/renders/*.png', 'imgs/renders' ), + ( 'dist_assets/win/pyfa.ico', '.' ), + ( 'dist_assets/cacert.pem', '.' ), + ( 'eve.db', '.' ), + ( 'README.md', '.' ), + ( 'LICENSE', '.' ), + ( 'gitversion', '.' ), + ] + +import_these = [] + +# Walk directories that do dynamic importing +paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') +for root, folders, files in chain.from_iterable(os.walk(path) for path in paths): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + import_these.append(mod_name) + +a = Analysis([r'pyfa.py'], + pathex=[], + binaries=[], + datas=added_files, + hiddenimports=import_these, + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + name='pyfa', + debug=False, + strip=False, + upx=True, + runtime_tmpdir=None, + console=False , + icon='dist_assets/mac/pyfa.icns', + ) + +app = BUNDLE(exe, + name='pyfa.app', + icon=None, + bundle_identifier=None) \ No newline at end of file From 8cbb3276591c70ffd148c25219b701c6e7a6615e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 12 Nov 2017 23:58:30 -0500 Subject: [PATCH 056/212] Fix bug that caused race image to not appear in fitting tab --- gui/chrome_tabs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index 603c14b9a..66a3b7855 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -297,7 +297,7 @@ class ChromeNotebook(wx.Panel): def SetPageIcon(self, i, icon, refresh=True): tab = self.tabs_container.tabs[i] - tab.tabImg = icon + tab.tab_img = icon if refresh: self.tabs_container.AdjustTabsSize() self.Refresh() From cc9b6ea04b974eb8dd804f3bf0a48fe6d2ee5f95 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 13 Nov 2017 00:18:24 -0500 Subject: [PATCH 057/212] More deprecation fixes --- gui/builtinContextMenus/commandFits.py | 2 +- gui/builtinContextMenus/damagePattern.py | 2 +- gui/builtinContextMenus/fighterAbilities.py | 2 +- gui/builtinContextMenus/implantSets.py | 2 +- gui/builtinContextMenus/tacticalMode.py | 2 +- gui/builtinContextMenus/targetResists.py | 8 ++++---- gui/builtinContextMenus/whProjector.py | 2 +- gui/crestFittings.py | 2 +- gui/itemStats.py | 8 ++++---- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/gui/builtinContextMenus/commandFits.py b/gui/builtinContextMenus/commandFits.py index 06ef53eae..bc2342e85 100644 --- a/gui/builtinContextMenus/commandFits.py +++ b/gui/builtinContextMenus/commandFits.py @@ -64,7 +64,7 @@ class CommandFits(ContextMenu): for fit in sorted(self.__class__.commandFits, key=lambda x: x.name): print(fit) menuItem = self.addFit(rootMenu if msw else sub, fit, True) - sub.AppendItem(menuItem) + sub.Append(menuItem) else: typeDict = {} diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index 710447a50..bf725e233 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -96,7 +96,7 @@ class DamagePattern(ContextMenu): # Items that have a parent for pattern in self.subMenus[self.m[i]]: - sub.AppendItem(self.addPattern(rootMenu if msw else sub, pattern)) + sub.Append(self.addPattern(rootMenu if msw else sub, pattern)) return sub diff --git a/gui/builtinContextMenus/fighterAbilities.py b/gui/builtinContextMenus/fighterAbilities.py index 6f6bc8051..76eab5c1e 100644 --- a/gui/builtinContextMenus/fighterAbilities.py +++ b/gui/builtinContextMenus/fighterAbilities.py @@ -44,7 +44,7 @@ class FighterAbility(ContextMenu): if not ability.effect.isImplemented: continue menuItem = self.addAbility(rootMenu if msw else sub, ability) - sub.AppendItem(menuItem) + sub.Append(menuItem) menuItem.Check(ability.active) return sub diff --git a/gui/builtinContextMenus/implantSets.py b/gui/builtinContextMenus/implantSets.py index d553a6f8d..d338a1930 100644 --- a/gui/builtinContextMenus/implantSets.py +++ b/gui/builtinContextMenus/implantSets.py @@ -61,7 +61,7 @@ class ImplantSets(ContextMenu): mitem = wx.MenuItem(rootMenu, id, set.name) bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem) self.idmap[id] = set - m.AppendItem(mitem) + m.Append(mitem) return m diff --git a/gui/builtinContextMenus/tacticalMode.py b/gui/builtinContextMenus/tacticalMode.py index 8b87dd10b..e09b26940 100644 --- a/gui/builtinContextMenus/tacticalMode.py +++ b/gui/builtinContextMenus/tacticalMode.py @@ -49,7 +49,7 @@ class TacticalMode(ContextMenu): for mode in self.modes: menuItem = self.addMode(rootMenu if msw else sub, mode) - sub.AppendItem(menuItem) + sub.Append(menuItem) menuItem.Check(self.currMode.item == mode.item) return sub diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index 677605f31..391608fed 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -87,12 +87,12 @@ class TargetResists(ContextMenu): else: self.singles.append(pattern) - sub.AppendItem(self.addPattern(rootMenu if msw else sub, None)) # Add reset + sub.Append(self.addPattern(rootMenu if msw else sub, None)) # Add reset sub.AppendSeparator() # Single items, no parent for pattern in self.singles: - sub.AppendItem(self.addPattern(rootMenu if msw else sub, pattern)) + sub.Append(self.addPattern(rootMenu if msw else sub, pattern)) # Items that have a parent for menuName, patterns in list(self.subMenus.items()): @@ -108,8 +108,8 @@ 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 + grandSub.Append(self.addPattern(rootMenu if msw else grandSub, pattern)) + sub.Append(item) # finally, append parent item to root menu return sub diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py index 4fab1fc93..bafca94ab 100644 --- a/gui/builtinContextMenus/whProjector.py +++ b/gui/builtinContextMenus/whProjector.py @@ -45,7 +45,7 @@ class WhProjector(ContextMenu): rootMenu.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem) else: grandSub.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem) - grandSub.AppendItem(grandSubItem) + grandSub.Append(grandSubItem) return sub def handleSelection(self, event): diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 968e5e18b..13df6fd2f 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -403,7 +403,7 @@ class FittingsTreeView(wx.Panel): shipID = tree.AppendItem(root, name) for fit in fits: fitId = tree.AppendItem(shipID, fit['name']) - tree.SetPyData(fitId, json.dumps(fit)) + tree.SetItemData(fitId, json.dumps(fit)) tree.SortChildren(root) diff --git a/gui/itemStats.py b/gui/itemStats.py index d1eb50d1e..741482caa 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -776,7 +776,7 @@ class ItemDependents(wx.Panel): self.SetSizer(mainSizer) self.root = self.reqTree.AddRoot("WINRARZOR") - self.reqTree.SetPyData(self.root, None) + self.reqTree.SetItemData(self.root, None) self.imageList = wx.ImageList(16, 16) self.reqTree.SetImageList(self.imageList) @@ -1026,7 +1026,7 @@ class ItemAffectedBy(wx.Panel): change = self.affectedBy.GetItemData(item) display = self.affectedBy.GetItemText(item) self.affectedBy.SetItemText(item, change) - self.affectedBy.SetPyData(item, display) + self.affectedBy.SetItemData(item, display) self.Thaw() @@ -1171,7 +1171,7 @@ class ItemAffectedBy(wx.Panel): # this is the attribute node child = self.affectedBy.AppendItem(parent, display, attrIcon) - self.affectedBy.SetPyData(child, saved) + self.affectedBy.SetItemData(child, saved) self.treeItems.append(child) items = attributes[attrName] @@ -1203,7 +1203,7 @@ class ItemAffectedBy(wx.Panel): # this is the Module node, the attribute will be attached to this display = "%s %s %.2f %s" % (displayStr, attrModifier, attrAmount, penalized) treeItem = self.affectedBy.AppendItem(child, display, itemIcon) - self.affectedBy.SetPyData(treeItem, afflictor) + self.affectedBy.SetItemData(treeItem, afflictor) def buildModuleView(self, root): """ From 979bc494f04f03d1df545bfb3af121eee76edb2f Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 13 Nov 2017 22:29:58 -0500 Subject: [PATCH 058/212] deprecation --- gui/builtinViews/entityEditor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index 544e7a178..54828734c 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -3,9 +3,9 @@ import wx from gui.bitmap_loader import BitmapLoader -class BaseValidator(wx.PyValidator): +class BaseValidator(wx.Validator): def __init__(self): - wx.PyValidator.__init__(self) + wx.Validator.__init__(self) def Validate(self, win): raise NotImplementedError() From 1978f5cb923b388cc06df4bf6b6f2560caea2c57 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 13 Nov 2017 22:45:12 -0500 Subject: [PATCH 059/212] Test validator stuff --- wxthing.py | 211 +++++++++++++++++------------------------------------ 1 file changed, 67 insertions(+), 144 deletions(-) diff --git a/wxthing.py b/wxthing.py index 7623a2d42..59eaaf75f 100644 --- a/wxthing.py +++ b/wxthing.py @@ -1,159 +1,82 @@ -# A very simple Drag and Drop Example -# provided with no warranty whatsoever for any purpose, ever - -# A very simple Drag and Drop Example -# provided with no warranty whatsoever for any purpose, ever - -# This code creates a Text Control from which Text can be dragged, -# a Text Control to which Text can be dragged (from the first Text Control or from other applications), -# and a Text Control to which Files can be dragged from outside this application. -# While the later two windows can receive data from outside the application, the first window -# does not appear to be able to provide text to other applications. Please feel free to fix -# this if you know how, as I think that would be more useful as an example. - -# It is designed to demonstrate the fundamentals of very simple drag-and-drop operations. - -""" This mini-app is designed to demonstrate simple Drag and Drop functioning in wx.Python """ - -__author__ = 'David Woods, Wisconsin Center for Education Research ' - -# Import wx.Python import wx -# Declare GUI Constants -MENU_FILE_EXIT = wx.NewId() -DRAG_SOURCE = wx.NewId() -# Define Text Drop Target class -class TextDropTarget(wx.TextDropTarget): - """ This object implements Drop Target functionality for Text """ - def __init__(self, obj): - """ Initialize the Drop Target, passing in the Object Reference to - indicate what should receive the dropped text """ - # Initialize the wx.TextDropTarget Object - wx.TextDropTarget.__init__(self) - # Store the Object Reference for dropped text - self.obj = obj +# Custom class that forces the TextEntryDialog to accept a Validator on the TextCtrl +class TextEntryValidatedDialog(wx.TextEntryDialog): + def __init__(self, parent, validator=None, *args, **kargs): + wx.TextEntryDialog.__init__(self, parent, *args, **kargs) + self.parent = parent - def OnDropText(self, x, y, data): - """ Implement Text Drop """ - # When text is dropped, write it into the object specified - self.obj.WriteText(data + '\n\n') + self.txtctrl = self.FindWindowById(3000) + if validator: + self.txtctrl.SetValidator(validator()) -# Define File Drop Target class -class FileDropTarget(wx.FileDropTarget): - """ This object implements Drop Target functionality for Files """ - def __init__(self, obj): - """ Initialize the Drop Target, passing in the Object Reference to - indicate what should receive the dropped files """ - # Initialize the wxFileDropTarget Object - wx.FileDropTarget.__init__(self) - # Store the Object Reference for dropped files - self.obj = obj +# Define a base Validator class that all other Validators will extend +class BaseValidator(wx.Validator): + def __init__(self): + wx.Validator.__init__(self) - def OnDropFiles(self, x, y, filenames): - """ Implement File Drop """ - # For Demo purposes, this function appends a list of the files dropped at the end of the widget's text - # Move Insertion Point to the end of the widget's text - self.obj.SetInsertionPointEnd() - # append a list of the file names dropped - self.obj.WriteText("%d file(s) dropped at %d, %d:\n" % (len(filenames), x, y)) - for file in filenames: - self.obj.WriteText(file + '\n') - self.obj.WriteText('\n') + def Validate(self, win): + raise NotImplementedError() + + def TransferToWindow(self): + return True + + def TransferFromWindow(self): + return True + +# Define my Custom Validator +class MyTextValidator(BaseValidator): + def __init__(self): + BaseValidator.__init__(self) + + def Clone(self): + return MyTextValidator() + + def Validate(self, win): + print("Validating!") + textCtrl = self.GetWindow() + text = textCtrl.GetValue().strip() + try: + if len(text) == 0: + raise ValueError("You must supply a value!") + elif text == "error": + raise ValueError("Simulate another error!") + return True + except ValueError as e: + wx.MessageBox("{}".format(e), "Error") + textCtrl.SetFocus() + return False +class MyForm(wx.Frame): + def __init__(self): + wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial") -class MainWindow(wx.Frame): - """ This window displays the GUI Widgets. """ - def __init__(self,parent,id,title): - wx.Frame.__init__(self,parent, wx.ID_ANY, title, size = (800,600), style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE) - self.SetBackgroundColour(wx.WHITE) + # Add a panel so it looks the correct on all platforms + panel = wx.Panel(self, wx.ID_ANY) + self.index = 0 - # Menu Bar - # Create a MenuBar - menuBar = wx.MenuBar() - # Build a Menu Object to go into the Menu Bar - menu1 = wx.Menu() - menu1.Append(MENU_FILE_EXIT, "E&xit", "Quit Application") - # Place the Menu Item in the Menu Bar - menuBar.Append(menu1, "&File") - # Place the Menu Bar on the ap - self.SetMenuBar(menuBar) - #Define Events for the Menu Items - wx.EVT_MENU(self, MENU_FILE_EXIT, self.CloseWindow) + btn = wx.Button(panel, label="Pop Dialog") + btn.Bind(wx.EVT_BUTTON, self.pop_dialog) - # GUI Widgets - # Define a Text Control from which Text can be dragged for dropping - # Label the control - wx.StaticText(self, -1, "Text Drag Source (left-click to select, right-click to drag)", (10, 1)) - # Create a Text Control - self.text = wx.TextCtrl(self, DRAG_SOURCE, "", pos=(10,15), size=(350,500), style = wx.TE_MULTILINE|wx.HSCROLL) - # Make this control a Text Drop Target - # Create a Text Drop Target object - dt1 = TextDropTarget(self.text) - # Link the Drop Target Object to the Text Control - self.text.SetDropTarget(dt1) - # Put some text in the control as a starting place to have something to copy - for x in range(20): - self.text.WriteText("This is line %d of some text to drag.\n" % x) - # Define Right-Click as start of Drag - wx.EVT_RIGHT_DOWN(self.text, self.OnDragInit) + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5) + panel.SetSizer(sizer) - # Define a Text Control to recieve Dropped Text - # Label the control - wx.StaticText(self, -1, "Text Drop Target", (370, 1)) - # Create a read-only Text Control - self.text2 = wx.TextCtrl(self, -1, "", pos=(370,15), size=(410,235), style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY) - # Make this control a Text Drop Target - # Create a Text Drop Target object - dt2 = TextDropTarget(self.text2) - # Link the Drop Target Object to the Text Control - self.text2.SetDropTarget(dt2) + def pop_dialog(self, event): + dlg = TextEntryValidatedDialog(self, MyTextValidator, + "Enter some text here (or \"error\" if you want to simulate a validation failure", + "Thing") + dlg.txtctrl.SetInsertionPointEnd() + dlg.CenterOnParent() - # Define a Text Control to receive Dropped Files - # Label the control - wx.StaticText(self, -1, "File Drop Target (from off application only)", (370, 261)) - # Create a read-only Text Control - self.text3 = wx.TextCtrl(self, -1, "", pos=(370,275), size=(410,235), style = wx.TE_MULTILINE|wx.HSCROLL|wx.TE_READONLY) - # Make this control a File Drop Target - # Create a File Drop Target object - dt3 = FileDropTarget(self.text3) - # Link the Drop Target Object to the Text Control - self.text3.SetDropTarget(dt3) - - # Display the Window - self.Show(True) + if dlg.ShowModal() == wx.ID_OK: + print("Entered Value: "+dlg.txtctrl.GetValue().strip()) - def CloseWindow(self, event): - """ Close the Window """ - self.Close() - - def OnDragInit(self, event): - """ Begin a Drag Operation """ - # Create a Text Data Object, which holds the text that is to be dragged - tdo = wx.TextDataObject(self.text.GetStringSelection()) - # Create a Drop Source Object, which enables the Drag operation - tds = wx.DropSource(self.text) - # Associate the Data to be dragged with the Drop Source Object - tds.SetData(tdo) - # Initiate the Drag Operation - tds.DoDragDrop(True) - - - -class MyApp(wx.App): - """ Define the Drag and Drop Example Application """ - def OnInit(self): - """ Initialize the Application """ - # Declare the Main Application Window - frame = MainWindow(None, -1, "Drag and Drop Example") - # Show the Application as the top window - self.SetTopWindow(frame) - return True - - -# Declare the Application and start the Main Loop -app = MyApp(0) -app.MainLoop() \ No newline at end of file +if __name__ == "__main__": + app = wx.App(False) + frame = MyForm() + frame.Show() + app.MainLoop() \ No newline at end of file From 90e338b9698ad525835269caadef7d6843904e3b Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 14 Nov 2017 00:07:12 -0500 Subject: [PATCH 060/212] Fix for broken Entity Editor Validation --- gui/builtinViews/entityEditor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index 54828734c..c50f57e40 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -22,7 +22,10 @@ class TextEntryValidatedDialog(wx.TextEntryDialog): wx.TextEntryDialog.__init__(self, parent, *args, **kargs) self.parent = parent - self.txtctrl = self.FindWindowById(3000) + # Find first TextCtrl in the TextEntryDialog + # See https://github.com/wxWidgets/Phoenix/issues/611 + self.txtctrl = next(x for x in self.Children if isinstance(x, wx.TextCtrl)) + if validator: self.txtctrl.SetValidator(validator()) From ce8f1d385d3ff2ab233482bea766e95c886a1daa Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 15 Nov 2017 21:43:36 -0500 Subject: [PATCH 061/212] Better fix for broken Entity Editor Validation --- gui/builtinViews/entityEditor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index c50f57e40..b706c4a76 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -22,9 +22,8 @@ class TextEntryValidatedDialog(wx.TextEntryDialog): wx.TextEntryDialog.__init__(self, parent, *args, **kargs) self.parent = parent - # Find first TextCtrl in the TextEntryDialog # See https://github.com/wxWidgets/Phoenix/issues/611 - self.txtctrl = next(x for x in self.Children if isinstance(x, wx.TextCtrl)) + self.txtctrl = self.FindWindowById(3000, self) if validator: self.txtctrl.SetValidator(validator()) From e848cec815a431ef61f5ee23d62a90b0d0cb6c0d Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 16 Nov 2017 01:20:38 -0500 Subject: [PATCH 062/212] Fix dirty skill indicator (* instead of blue text). Need to get a different icon working for those. Remove debugging prints() --- eos/db/migrations/__init__.py | 5 ----- gui/characterEditor.py | 26 ++++++++++++++------------ service/conversions/__init__.py | 8 -------- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/eos/db/migrations/__init__.py b/eos/db/migrations/__init__.py index 1dd4508a5..e9797c352 100644 --- a/eos/db/migrations/__init__.py +++ b/eos/db/migrations/__init__.py @@ -24,8 +24,6 @@ prefix = __name__ + "." # (should find all filters in normal build, but not pyinstaller) module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)] -print ("module names from iter_modules: ", module_names) - # special handling for PyInstaller importers = map(pkgutil.get_importer, __path__) toc = set() @@ -37,10 +35,7 @@ for elm in toc: if elm.startswith(prefix): module_names.append(elm) -print("module names after get_importer toc: ", module_names) - for modname in module_names: - print(modname) # loop through python files, extracting update number and function, and # adding it to a list modname_tail = modname.rsplit('.', 1)[-1] diff --git a/gui/characterEditor.py b/gui/characterEditor.py index ce2841764..f54a868d4 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -420,12 +420,13 @@ class SkillTreeView(wx.Panel): for id, name in sChar.getSkillsByName(search): iconId = self.skillBookImageId - childId = tree.AppendItem(root, name, iconId, data=('skill', id)) level, dirty = sChar.getSkillLevel(char.ID, id) + + if dirty: + name = "* " + name + + childId = tree.AppendItem(root, name, iconId, data=('skill', id)) tree.SetItemText(childId, 1, "Level %d" % int(level) if isinstance(level, float) else level) - # @todo: pheonix - # if dirty: - # tree.SetItemTextColour(childId, wx.BLUE) def populateSkillTree(self, event=None): sChar = Character.getInstance() @@ -446,11 +447,11 @@ class SkillTreeView(wx.Panel): tree.DeleteAllItems() for id, name in groups: + if id in dirtyGroups: + name = "* " + name + childId = tree.AppendItem(root, name, imageId, data=('group', id)) tree.AppendItem(childId, "dummy") - # @todo: pheonix - # if id in dirtyGroups: - # tree.(childId, wx.BLUE) if event: event.Skip() @@ -468,13 +469,14 @@ class SkillTreeView(wx.Panel): data = tree.GetItemData(root) for id, name in sChar.getSkills(data[1]): iconId = self.skillBookImageId - childId = tree.AppendItem(root, name, iconId, data=('skill', id)) level, dirty = sChar.getSkillLevel(char.ID, id) - tree.SetItemText(childId, 1, "Level %d" % int(level) if isinstance(level, float) else level) - # @todo: pheonix - # if dirty: - # tree.SetItemTextColour(childId, wx.BLUE) + if dirty: + name = "* " + name + + childId = tree.AppendItem(root, name, iconId, data=('skill', id)) + + tree.SetItemText(childId, 1, "Level %d" % int(level) if isinstance(level, float) else level) def scheduleMenu(self, event): event.Skip() diff --git a/service/conversions/__init__.py b/service/conversions/__init__.py index baab9200e..b3263339c 100644 --- a/service/conversions/__init__.py +++ b/service/conversions/__init__.py @@ -9,15 +9,12 @@ elsewhere (in which case can be accessed with packs[name]) import pkgutil - - # init parent dict all = {} # init container to store the separate conversion packs in case we need them packs = {} - prefix = __name__ + "." # load modules to work based with and without pyinstaller @@ -28,8 +25,6 @@ prefix = __name__ + "." # (should find all filters in normal build, but not pyinstaller) module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)] -print ("module names from iter_modules: ", module_names) - # special handling for PyInstaller importers = map(pkgutil.get_importer, __path__) toc = set() @@ -41,10 +36,7 @@ for elm in toc: if elm.startswith(prefix): module_names.append(elm) -print("module names after get_importer toc: ", module_names) - for modname in module_names: - print (modname) conversionPack = __import__(modname, fromlist="dummy") all.update(conversionPack.CONVERSIONS) modname_tail = modname.rsplit('.', 1)[-1] From d956cb7861b2670aaa553379db099895b045aa5e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 16 Nov 2017 01:31:28 -0500 Subject: [PATCH 063/212] use a different image to denote changed skills --- gui/characterEditor.py | 23 ++++++++++++----------- imgs/gui/skill_small_red.png | Bin 0 -> 697 bytes 2 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 imgs/gui/skill_small_red.png diff --git a/gui/characterEditor.py b/gui/characterEditor.py index f54a868d4..c5b6b7169 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -316,6 +316,7 @@ class SkillTreeView(wx.Panel): self.imageList = wx.ImageList(16, 16) tree.SetImageList(self.imageList) self.skillBookImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) + self.skillBookDirtyImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small_red", "gui")) tree.AppendColumn("Skill") tree.AppendColumn("Level") @@ -423,7 +424,7 @@ class SkillTreeView(wx.Panel): level, dirty = sChar.getSkillLevel(char.ID, id) if dirty: - name = "* " + name + iconId = self.skillBookDirtyImageId childId = tree.AppendItem(root, name, iconId, data=('skill', id)) tree.SetItemText(childId, 1, "Level %d" % int(level) if isinstance(level, float) else level) @@ -441,14 +442,14 @@ class SkillTreeView(wx.Panel): self.btnSecStatus.Enable() groups = sChar.getSkillGroups() - imageId = self.skillBookImageId root = self.root tree = self.skillTreeListCtrl tree.DeleteAllItems() for id, name in groups: + imageId = self.skillBookImageId if id in dirtyGroups: - name = "* " + name + imageId = self.skillBookDirtyImageId childId = tree.AppendItem(root, name, imageId, data=('group', id)) tree.AppendItem(childId, "dummy") @@ -472,7 +473,7 @@ class SkillTreeView(wx.Panel): level, dirty = sChar.getSkillLevel(char.ID, id) if dirty: - name = "* " + name + iconId = self.skillBookDirtyImageId childId = tree.AppendItem(root, name, iconId, data=('skill', id)) @@ -525,9 +526,9 @@ class SkillTreeView(wx.Panel): self.skillTreeListCtrl.SetItemText(treeItem, 1, "Level {}".format(int(lvl)) if not isinstance(lvl, str) else lvl) - # @todo: pheonix - # if not dirty: - # self.skillTreeListCtrl.SetItemTextColour(treeItem, None) + + if not dirty: + self.skillTreeListCtrl.SetItemImage(treeItem, self.skillBookImageId) while child.IsOk(): # child = Skill category @@ -551,10 +552,10 @@ class SkillTreeView(wx.Panel): parentID = self.skillTreeListCtrl.GetItemParent(selection) parent = self.skillTreeListCtrl.GetItemData(parentID) - # @todo: pheonix - # if parent: - # if parent[1] in dirtyGroups: - # self.skillTreeListCtrl.SetItemTextColour(parentID, None) + if parent: + if parent[1] in dirtyGroups: + self.skillTreeListCtrl.SetItemImage(parentID, self.skillBookImageId) + event.Skip() diff --git a/imgs/gui/skill_small_red.png b/imgs/gui/skill_small_red.png new file mode 100644 index 0000000000000000000000000000000000000000..7a156c781b5d017b03694e3783b800033f4a6ace GIT binary patch literal 697 zcmV;q0!ICbP)Y3Y3$kCzT@P z_dno$U|=^B;xV73}iC&b#~I} zcH{DRxKyt{2VM@i-R72INcj5;0vOT~iFmbCl8MPlW)lf6IGwl@1+UE}8bYvHt-g7c z%|1E_DF8M=2!RC0T_MKowOX&`^)k1-OxdzHY&6hJlbm6o9Ug{3WR|+q>8t&!D%^@9RUyzVOO%~Xso3q@@9+1xe7?b0F8AXU0B|Iwv2QdGX_DRB%g(N>thL_VDz7TU&D(*%(;>e<_;F{auwAcj zW_A5LkOY>2Z>@o|02R0bTyOT Date: Thu, 16 Nov 2017 01:35:26 -0500 Subject: [PATCH 064/212] Fix None comparison operation --- eos/saveddata/character.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index b9f3e2a4b..e859b53cf 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -357,7 +357,7 @@ class Skill(HandledItem): start = time.time() for item, rlevel in self.item.requiredFor.items(): if item.group.category.ID == 16: # Skill category - if level < rlevel: + if level is None or level < rlevel: skill = self.character.getSkill(item.ID) # print "Removing skill: {}, Dependant level: {}, Required level: {}".format(skill, level, rlevel) skill.setLevel(None, persist) From a52b9e58e97b28bcc17a1c3cb49eb1516afef002 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 18 Nov 2017 19:01:05 -0500 Subject: [PATCH 065/212] Testing some stuff out --- gui/builtinViews/fittingView.py | 2 + gui/devTools.py | 67 +++++++++++++++++++++++++++++++++ gui/graphFrame.py | 2 +- gui/mainFrame.py | 5 +++ gui/mainMenuBar.py | 3 ++ 5 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 gui/devTools.py diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index c07982886..22d06fa8d 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -166,6 +166,8 @@ class FittingView(d.Display): self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) self.parent.Bind(EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) + print("------------------ new fitting view -------------------") + print(self) def OnLeaveWindow(self, event): self.SetToolTip(None) diff --git a/gui/devTools.py b/gui/devTools.py new file mode 100644 index 000000000..b51ce3065 --- /dev/null +++ b/gui/devTools.py @@ -0,0 +1,67 @@ +# ============================================================================= +# 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 . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx +from logbook import Logger +import gc + +pyfalog = Logger(__name__) + + +class DevTools(wx.Dialog): + DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") + + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Damage Pattern Editor", size=wx.Size(400, 240)) + + self.block = False + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) + + mainSizer = wx.BoxSizer(wx.VERTICAL) + + self.id_get = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition) + mainSizer.Add(self.id_get, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + self.idBtn = wx.Button(self, wx.ID_ANY, "Print object", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.idBtn, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + self.idBtn.Bind(wx.EVT_BUTTON, self.objects_by_id) + + self.SetSizer(mainSizer) + + self.Layout() + self.CenterOnParent() + self.Show() + + + + def objects_by_id(self, evt): + input = self.id_get.GetValue() + if input.startswith("0x"): + input = int(input, 16) + + print("Finding {} ({})".format(str(input), hex(input))) + + for obj in gc.get_objects(): + if id(obj) == input: + print(obj) + print(bool(obj)) + break + else: + print(None) diff --git a/gui/graphFrame.py b/gui/graphFrame.py index a0aa52876..a4d5fdd75 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -171,7 +171,7 @@ class GraphFrame(wx.Frame): def close(self, event): self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) - self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw) + print(self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw)) event.Skip() def getView(self): diff --git a/gui/mainFrame.py b/gui/mainFrame.py index a856c391b..cbc2dafde 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -54,6 +54,7 @@ from gui.characterSelection import CharacterSelection from gui.patternEditor import DmgPatternEditorDlg from gui.resistsEditor import ResistsEditorDlg from gui.setEditor import ImplantSetEditorDlg +from gui.devTools import DevTools from gui.preferenceDialog import PreferenceDialog from gui.graphFrame import GraphFrame from gui.copySelectDialog import CopySelectDialog @@ -386,6 +387,9 @@ class MainFrame(wx.Frame): # info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") wx.adv.AboutBox(info) + def showDevTools(self, event): + DevTools(self) + def showCharacterEditor(self, event): dlg = CharacterEditor(self) dlg.Show() @@ -472,6 +476,7 @@ class MainFrame(wx.Frame): # Widgets Inspector if config.debug: self.Bind(wx.EVT_MENU, self.openWXInspectTool, id=self.widgetInspectMenuID) + self.Bind(wx.EVT_MENU, self.showDevTools, id=menuBar.devToolsId) # About self.Bind(wx.EVT_MENU, self.ShowAboutBox, id=wx.ID_ABOUT) # Char editor diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 1cd680f63..aa85e0883 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -59,6 +59,7 @@ class MainMenuBar(wx.MenuBar): self.toggleOverridesId = wx.NewId() self.importDatabaseDefaultsId = wx.NewId() self.toggleIgnoreRestrictionID = wx.NewId() + self.devToolsId = wx.NewId() if 'wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0): wx.ID_COPY = wx.NewId() @@ -170,6 +171,8 @@ class MainMenuBar(wx.MenuBar): if config.debug: helpMenu.Append(self.mainFrame.widgetInspectMenuID, "Open Widgets Inspect tool", "Open Widgets Inspect tool") + helpMenu.Append(self.devToolsId, "Open Dev Tools", + "Dev Tools") self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) From 0f9455769959ac3ac001b4274b23fa4716e24aba Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 21 Nov 2017 00:05:52 -0500 Subject: [PATCH 066/212] Use wx.AutoBufferedPaintDC instead of wx.BufferedPaintDC (helps with drawing on linux) --- gui/builtinMarketBrowser/pfSearchBox.py | 4 +++- gui/builtinPreferenceViews/pyfaGaugePreferences.py | 3 ++- gui/builtinShipBrowser/pfBitmapFrame.py | 4 +++- gui/builtinShipBrowser/raceSelector.py | 4 +++- gui/builtinShipBrowser/sfBrowserItem.py | 4 ++-- gui/chrome_tabs.py | 11 +++++------ gui/devTools.py | 12 ++++++++++++ gui/mainFrame.py | 1 + gui/pyfa_gauge.py | 3 ++- gui/utils/anim.py | 4 +++- 10 files changed, 36 insertions(+), 14 deletions(-) diff --git a/gui/builtinMarketBrowser/pfSearchBox.py b/gui/builtinMarketBrowser/pfSearchBox.py index b696026f8..8d0871f6a 100644 --- a/gui/builtinMarketBrowser/pfSearchBox.py +++ b/gui/builtinMarketBrowser/pfSearchBox.py @@ -59,6 +59,8 @@ class PFSearchBox(wx.Window): self.EditBox.Bind(wx.EVT_TEXT, self.OnText) self.EditBox.Bind(wx.EVT_TEXT_ENTER, self.OnTextEnter) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + self.SetMinSize(size) def OnText(self, event): @@ -224,7 +226,7 @@ class PFSearchBox(wx.Window): self.EditBox.SetSize((self.cancelButtonX - self.padding - self.editX, -1)) def OnPaint(self, event): - dc = wx.BufferedPaintDC(self) + dc = wx.AutoBufferedPaintDC(self) bkColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) sepColor = colorUtils.GetSuitable(bkColor, 0.2) diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index c17002590..6e8d4ced2 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -49,6 +49,7 @@ class PFGaugePreview(wx.Window): self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) self.Bind(wx.EVT_TIMER, self.OnTimer) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) def OnEraseBk(self, event): pass @@ -110,7 +111,7 @@ class PFGaugePreview(wx.Window): def OnPaint(self, event): rect = self.GetClientRect() - dc = wx.BufferedPaintDC(self) + dc = wx.AutoBufferedPaintDC(self) dc.SetBackground(wx.Brush(self.bkColor)) dc.Clear() diff --git a/gui/builtinShipBrowser/pfBitmapFrame.py b/gui/builtinShipBrowser/pfBitmapFrame.py index 8421ee6a7..1b5631dac 100644 --- a/gui/builtinShipBrowser/pfBitmapFrame.py +++ b/gui/builtinShipBrowser/pfBitmapFrame.py @@ -19,6 +19,8 @@ class PFBitmapFrame(wx.Frame): self.transp = 0 self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + self.SetTransparent(0) self.Refresh() @@ -51,7 +53,7 @@ class PFBitmapFrame(wx.Frame): def OnWindowPaint(self, event): rect = self.GetRect() canvas = wx.Bitmap(rect.width, rect.height) - mdc = wx.BufferedPaintDC(self) + mdc = wx.AutoBufferedPaintDC(self) mdc.SelectObject(canvas) mdc.DrawBitmap(self.bitmap, 0, 0) mdc.SetPen(wx.Pen("#000000", width=1)) diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index cfc795ea9..5931e3aad 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -85,6 +85,8 @@ class RaceSelector(wx.Window): self.Layout() + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + def OnMouseMove(self, event): mx, my = event.GetPosition() @@ -170,7 +172,7 @@ class RaceSelector(wx.Window): # bkColor = colorUtils.GetSuitable(windowColor, 0.1) sepColor = colorUtils.GetSuitable(windowColor, 0.2) - mdc = wx.BufferedPaintDC(self) + mdc = wx.AutoBufferedPaintDC(self) bkBitmap = drawUtils.RenderGradientBar(windowColor, rect.width, rect.height, 0.1, 0.1, 0.2, 2) mdc.DrawBitmap(bkBitmap, 0, 0, True) diff --git a/gui/builtinShipBrowser/sfBrowserItem.py b/gui/builtinShipBrowser/sfBrowserItem.py index e0cef1495..6145bf264 100644 --- a/gui/builtinShipBrowser/sfBrowserItem.py +++ b/gui/builtinShipBrowser/sfBrowserItem.py @@ -241,7 +241,7 @@ class PFToolbar(object): class SFBrowserItem(wx.Window): 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.SetBackgroundStyle(wx.BG_STYLE_PAINT) self.highlighted = False self.selected = False self.bkBitmap = None @@ -281,7 +281,7 @@ class SFBrowserItem(wx.Window): wx.Window.Refresh(self) def OnPaint(self, event): - mdc = wx.BufferedPaintDC(self) + mdc = wx.AutoBufferedPaintDC(self) self.RenderBackground() diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index 66a3b7855..8e5982221 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -727,6 +727,8 @@ class _TabsContainer(wx.Panel): self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + def OnSysColourChanged(self, event): for tab in self.tabs: tab.InitTab() @@ -1096,10 +1098,7 @@ class _TabsContainer(wx.Panel): self.Refresh() def OnPaint(self, event): - if "wxGTK" in wx.PlatformInfo: - mdc = wx.AutoBufferedPaintDC(self) - else: - mdc = wx.BufferedPaintDC(self) + mdc = wx.AutoBufferedPaintDC(self) # if 'wxMac' in wx.PlatformInfo: # color = wx.Colour(0, 0, 0) @@ -1299,7 +1298,7 @@ class PFNotebookPagePreview(wx.Frame): size=wx.DefaultSize, style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) - + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) self.title = title self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) @@ -1371,7 +1370,7 @@ class PFNotebookPagePreview(wx.Frame): def OnWindowPaint(self, event): rect = self.GetRect() canvas = wx.Bitmap(rect.width, rect.height) - mdc = wx.BufferedPaintDC(self) + mdc = wx.AudoBufferedPaintDC(self) mdc.SelectObject(canvas) color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) mdc.SetBackground(wx.Brush(color)) diff --git a/gui/devTools.py b/gui/devTools.py index b51ce3065..89d9a6f97 100644 --- a/gui/devTools.py +++ b/gui/devTools.py @@ -43,6 +43,11 @@ class DevTools(wx.Dialog): self.idBtn.Bind(wx.EVT_BUTTON, self.objects_by_id) + self.gcCollect = wx.Button(self, wx.ID_ANY, "GC Collect", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.gcCollect, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + self.gcCollect.Bind(wx.EVT_BUTTON, self.gc_collect) + self.SetSizer(mainSizer) self.Layout() @@ -62,6 +67,13 @@ class DevTools(wx.Dialog): if id(obj) == input: print(obj) print(bool(obj)) + print(str(len(gc.get_referents(obj))) + " references") + break else: print(None) + + def gc_collect(self, evt): + print(gc.collect()) + print(gc.get_debug()) + print(gc.get_stats()) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index cbc2dafde..124aa5646 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -1016,6 +1016,7 @@ class MainFrame(wx.Frame): del self.waitDialog def openGraphFrame(self, event): + if not self.graphFrame: self.graphFrame = GraphFrame(self) diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index b6b514015..6e464066b 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -75,6 +75,7 @@ class PyGauge(wx.Window): self.Bind(wx.EVT_TIMER, self.OnTimer) self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) def OnEraseBackground(self, event): pass @@ -212,7 +213,7 @@ class PyGauge(wx.Window): (self._value, self._max_range if float(self._max_range) > 0.01 else 0)) def OnPaint(self, event): - dc = wx.BufferedPaintDC(self) + dc = wx.AutoBufferedPaintDC(self) rect = self.GetClientRect() dc.SetBackground(wx.Brush(self.GetBackgroundColour())) diff --git a/gui/utils/anim.py b/gui/utils/anim.py index 8791b273d..0f3333dc1 100644 --- a/gui/utils/anim.py +++ b/gui/utils/anim.py @@ -24,6 +24,8 @@ class LoadAnimation(wx.Window): self.animTimer.Start(self.animTimerPeriod) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + def Play(self): if self.animTimer.IsRunning(): self.animTimer.Stop() @@ -52,7 +54,7 @@ class LoadAnimation(wx.Window): def OnPaint(self, event): rect = self.GetClientRect() - dc = wx.BufferedPaintDC(self) + dc = wx.AutoBufferedPaintDC(self) windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) dc.SetBackground(wx.Brush(windowColor)) dc.Clear() From 6c8b1439367db6943ba5235a371665743f8e634b Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 21 Nov 2017 00:32:45 -0500 Subject: [PATCH 067/212] Tentative .spec file for linux --- pyfa-linux.spec | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 pyfa-linux.spec diff --git a/pyfa-linux.spec b/pyfa-linux.spec new file mode 100644 index 000000000..bfc24a2f2 --- /dev/null +++ b/pyfa-linux.spec @@ -0,0 +1,70 @@ +# -*- mode: python -*- + +import os +from itertools import chain +import subprocess + +label = subprocess.check_output([ + "git", "describe", "--tags"]).strip() + +with open('gitversion', 'w+') as f: + f.write(label.decode()) + +block_cipher = None + + +added_files = [ + ( 'imgs/gui/*.png', 'imgs/gui' ), + ( 'imgs/gui/*.gif', 'imgs/gui' ), + ( 'imgs/icons/*.png', 'imgs/icons' ), + ( 'imgs/renders/*.png', 'imgs/renders' ), + ( 'dist_assets/win/pyfa.ico', '.' ), + ( 'dist_assets/cacert.pem', '.' ), + ( 'eve.db', '.' ), + ( 'README.md', '.' ), + ( 'LICENSE', '.' ), + ( 'gitversion', '.' ), + ] + +import_these = [] + +# Walk directories that do dynamic importing +paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') +for root, folders, files in chain.from_iterable(os.walk(path) for path in paths): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + import_these.append(mod_name) + + +a = Analysis(['pyfa.py'], + pathex=[], + binaries=[], + datas=added_files, + hiddenimports=import_these, + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name='pyfa', + debug=False, + strip=False, + upx=True, + console=True ) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + name='pyfa') From d4df9894275300a8fa17cb6c4ddd3e8d4c5d9855 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 21 Nov 2017 23:04:51 -0500 Subject: [PATCH 068/212] Remove handlers from unbind (see https://github.com/wxWidgets/Phoenix/issues/624) --- gui/builtinViews/emptyView.py | 2 +- gui/builtinViews/fittingView.py | 10 +++++----- gui/crestFittings.py | 6 +++--- gui/graphFrame.py | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gui/builtinViews/emptyView.py b/gui/builtinViews/emptyView.py index 845364fd1..ad115a841 100644 --- a/gui/builtinViews/emptyView.py +++ b/gui/builtinViews/emptyView.py @@ -18,7 +18,7 @@ class BlankPage(wx.Panel): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=None)) def Destroy(self): - self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED, handler=self.pageChanged) + self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED) wx.Panel.Destroy(self) def pageChanged(self, event): diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 22d06fa8d..b8132ace8 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -216,11 +216,11 @@ class FittingView(d.Display): wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def Destroy(self): - self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED, handler=self.pageChanged) - self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.fitChanged) - self.mainFrame.Unbind(EVT_FIT_RENAMED, handler=self.fitRenamed) - self.mainFrame.Unbind(EVT_FIT_REMOVED, handler=self.fitRemoved) - self.mainFrame.Unbind(ITEM_SELECTED, handler=self.appendItem) + self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED) + self.mainFrame.Unbind(GE.FIT_CHANGED) + self.mainFrame.Unbind(EVT_FIT_RENAMED) + self.mainFrame.Unbind(EVT_FIT_REMOVED) + self.mainFrame.Unbind(ITEM_SELECTED) d.Display.Destroy(self) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 13df6fd2f..71cc00c02 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -128,8 +128,8 @@ class CrestFittings(wx.Frame): event.Skip() # continue event def OnClose(self, event): - self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT, handler=self.ssoLogout) - self.mainFrame.Unbind(GE.EVT_SSO_LOGIN, handler=self.ssoLogin) + self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT) + self.mainFrame.Unbind(GE.EVT_SSO_LOGIN) event.Skip() def getActiveCharacter(self): @@ -255,7 +255,7 @@ class ExportToEve(wx.Frame): event.Skip() # continue event def OnClose(self, event): - self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT, handler=self.ssoLogout) + self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT) event.Skip() def getActiveCharacter(self): diff --git a/gui/graphFrame.py b/gui/graphFrame.py index a4d5fdd75..1ecf2daca 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -170,8 +170,8 @@ class GraphFrame(wx.Frame): self.AppendFitToList(fitID) def close(self, event): - self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK, handler=self.removeItem) - print(self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.draw)) + self.fitList.fitList.Unbind(wx.EVT_LEFT_DCLICK) + self.mainFrame.Unbind(GE.FIT_CHANGED) event.Skip() def getView(self): From de44e6f932b9d6c266498eee5460186f02ba4f01 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 21 Nov 2017 23:46:33 -0500 Subject: [PATCH 069/212] Get rid of some annoying messages --- eos/gamedata.py | 8 ++++---- eos/saveddata/fit.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eos/gamedata.py b/eos/gamedata.py index a6a03714e..a378c06ba 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -161,7 +161,7 @@ class Effect(EqBase): if it doesn't, set dummy values and add a dummy handler """ - pyfalog.debug("Generate effect handler for {}".format(self.name)) + #pyfalog.debug("Generate effect handler for {}".format(self.name)) try: self.__effectModule = effectModule = __import__('eos.effects.' + self.handlerName, fromlist=True) @@ -178,20 +178,20 @@ class Effect(EqBase): self.__runTime = "normal" self.__activeByDefault = True self.__type = None - pyfalog.debug("ImportError generating handler: {0}", e) + #pyfalog.debug("ImportError generating handler: {0}", e) except (AttributeError) as e: # Effect probably exists but there is an issue with it. Turn it into a dummy effect so we can continue, but flag it with an error. self.__handler = effectDummy self.__runTime = "normal" self.__activeByDefault = True self.__type = None - pyfalog.error("AttributeError generating handler: {0}", e) + #pyfalog.error("AttributeError generating handler: {0}", e) except Exception as e: self.__handler = effectDummy self.__runTime = "normal" self.__activeByDefault = True self.__type = None - pyfalog.critical("Exception generating handler:") + #pyfalog.critical("Exception generating handler:") pyfalog.critical(e) self.__generated = True diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index fdab15b63..87dbbce3b 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -756,7 +756,7 @@ class Fit(object): # Loop through our run times here. These determine which effects are run in which order. for runTime in ("early", "normal", "late"): - pyfalog.debug("Run time: {0}", runTime) + #pyfalog.debug("Run time: {0}", runTime) # Items that are unrestricted. These items are run on the local fit # first and then projected onto the target fit it one is designated u = [ @@ -795,7 +795,7 @@ class Fit(object): # targetFit.register(item, origin=self) item.calculateModifiedAttributes(targetFit, runTime, False, True) - pyfalog.debug("Command Bonuses: {}".format(self.commandBonuses)) + #pyfalog.debug("Command Bonuses: {}".format(self.commandBonuses)) # If we are calculating our local or projected fit and have command bonuses, apply them if type != CalcType.COMMAND and self.commandBonuses: From 4138088d4a9a623c60338b5b61a25dd1a52411b9 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 21 Nov 2017 23:46:50 -0500 Subject: [PATCH 070/212] Do not create a new fitting view every time we switch fits. --- gui/builtinViews/fittingView.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index b8132ace8..67d541b61 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -76,8 +76,13 @@ class FitSpawner(gui.multiSwitch.TabSpawner): if (not openFitInNew and mstate.CmdDown()) or startup or (openFitInNew and not mstate.CmdDown()): self.multiSwitch.AddPage() - view = FittingView(self.multiSwitch) - self.multiSwitch.ReplaceActivePage(view) + view = self.multiSwitch.GetSelectedPage() + + if isinstance(view, gui.builtinViews.emptyView.BlankPage): + view = FittingView(self.multiSwitch) + print("###################### Created new view:"+repr(view)) + self.multiSwitch.ReplaceActivePage(view) + view.fitSelected(event) def handleDrag(self, type, fitID): @@ -216,11 +221,12 @@ class FittingView(d.Display): wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def Destroy(self): - self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED) - self.mainFrame.Unbind(GE.FIT_CHANGED) - self.mainFrame.Unbind(EVT_FIT_RENAMED) - self.mainFrame.Unbind(EVT_FIT_REMOVED) - self.mainFrame.Unbind(ITEM_SELECTED) + print("+++++ Destroy "+repr(self)) + print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED)) + print(self.mainFrame.Unbind(GE.FIT_CHANGED)) + print(self.mainFrame.Unbind(EVT_FIT_RENAMED)) + print(self.mainFrame.Unbind(EVT_FIT_REMOVED)) + print(self.mainFrame.Unbind(ITEM_SELECTED)) d.Display.Destroy(self) @@ -305,6 +311,8 @@ class FittingView(d.Display): event.Skip() def fitSelected(self, event): + print('====== Fit Selected: '+repr(self)+str(bool(self))) + if self.parent.IsActive(self): fitID = event.fitID startup = getattr(event, "startup", False) @@ -512,6 +520,8 @@ class FittingView(d.Display): self.populate(self.mods) def fitChanged(self, event): + print('====== Fit Changed: '+repr(self)+str(bool(self))) + try: if self.activeFitID is not None and self.activeFitID == event.fitID: self.generateMods() From 405a9650461e4fb9a9d3a5cacb2c732b891002b2 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 22 Nov 2017 01:44:48 -0500 Subject: [PATCH 071/212] Fix issue with loading fighters --- eos/saveddata/fighter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py index 695024540..645d7fbe5 100644 --- a/eos/saveddata/fighter.py +++ b/eos/saveddata/fighter.py @@ -89,7 +89,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.__itemModifiedAttributes.overrides = self.__item.overrides self.__slot = self.__calculateSlot(self.__item) - chargeID = self.getModifiedItemAttr("fighterAbilityLaunchBombType") + chargeID = self.getModifiedItemAttr("fighterAbilityLaunchBombType", None) if chargeID is not None: charge = eos.db.getItem(int(chargeID)) self.__charge = charge From 5d5646df79a93f508be28d5f73084cd5fd143738 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 22 Nov 2017 01:45:06 -0500 Subject: [PATCH 072/212] Fix BlankPage having binding issue --- gui/builtinViews/emptyView.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/builtinViews/emptyView.py b/gui/builtinViews/emptyView.py index ad115a841..3e6d2b467 100644 --- a/gui/builtinViews/emptyView.py +++ b/gui/builtinViews/emptyView.py @@ -18,7 +18,8 @@ class BlankPage(wx.Panel): wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=None)) def Destroy(self): - self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED) + # todo: This unbind caused fits to not recalc when switching to their tabs; find out why + # self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED) wx.Panel.Destroy(self) def pageChanged(self, event): From 4a33365195f9787d972849dbf6e8339325bc5629 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 22 Nov 2017 01:45:31 -0500 Subject: [PATCH 073/212] Add some debug and fix indentation issue causing fit deletion to crash open fittings views --- gui/builtinShipBrowser/fitItem.py | 4 ++++ gui/builtinViews/fittingView.py | 21 +++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index 4aea370d7..760916d4b 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -2,6 +2,7 @@ import re import time +import config import wx from logbook import Logger @@ -58,6 +59,9 @@ class FitItem(SFItem.SFBrowserItem): self.shipFittingInfo = shipFittingInfo self.shipName, self.shipTrait, self.fitName, self.fitBooster, self.timestamp, self.notes = shipFittingInfo + if config.debug: + self.fitName = '({}) {}'.format(self.fitID, self.fitName) + self.shipTrait = re.sub("<.*?>", " ", self.shipTrait) # see GH issue #62 diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 67d541b61..69fa3c918 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -78,7 +78,7 @@ class FitSpawner(gui.multiSwitch.TabSpawner): view = self.multiSwitch.GetSelectedPage() - if isinstance(view, gui.builtinViews.emptyView.BlankPage): + if not isinstance(view, FittingView): view = FittingView(self.multiSwitch) print("###################### Created new view:"+repr(view)) self.multiSwitch.ReplaceActivePage(view) @@ -287,19 +287,20 @@ class FittingView(d.Display): We also refresh the fit of the new current page in case delete fit caused change in stats (projected) """ + print('_+_+_+_+_+_ Fit Removed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID)) pyfalog.debug("FittingView::fitRemoved") if event.fitID == self.getActiveFit(): pyfalog.debug(" Deleted fit is currently active") self.parent.DeletePage(self.parent.GetPageIndex(self)) - try: - # Sometimes there is no active page after deletion, hence the try block - sFit = Fit.getInstance() - sFit.refreshFit(self.getActiveFit()) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) - except RuntimeError: - pyfalog.warning("Caught dead object") - pass + try: + # Sometimes there is no active page after deletion, hence the try block + sFit = Fit.getInstance() + sFit.refreshFit(self.getActiveFit()) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) + except RuntimeError: + pyfalog.warning("Caught dead object") + pass event.Skip() @@ -520,7 +521,7 @@ class FittingView(d.Display): self.populate(self.mods) def fitChanged(self, event): - print('====== Fit Changed: '+repr(self)+str(bool(self))) + print('====== Fit Changed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID)) try: if self.activeFitID is not None and self.activeFitID == event.fitID: From 6e112b9ed55581eb1acdb8f0027373a00d1c8210 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 23 Nov 2017 11:34:54 -0500 Subject: [PATCH 074/212] Fix issues with merge --- eos/saveddata/character.py | 2 +- gui/builtinItemStatsViews/itemAffectedBy.py | 2 +- gui/builtinItemStatsViews/itemAttributes.py | 4 ++-- gui/builtinItemStatsViews/itemCompare.py | 2 +- gui/builtinItemStatsViews/itemDependants.py | 2 +- gui/builtinItemStatsViews/itemEffects.py | 2 +- gui/builtinItemStatsViews/itemProperties.py | 2 +- gui/builtinItemStatsViews/itemRequirements.py | 2 +- gui/builtinPreferenceViews/pyfaGeneralPreferences.py | 2 +- gui/characterEditor.py | 5 ++--- service/price.py | 2 +- 11 files changed, 13 insertions(+), 14 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 5aacf775d..371f02c54 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -342,7 +342,7 @@ class Skill(HandledItem): elif self.character.name == "All 0": self.activeLevel = self.__level = 0 elif self.character.alphaClone: - return min(self.activeLevel or 0, self.character.alphaClone.getSkillLevel(self)) or 0 + return min(self.activeLevel or 0, self.character.alphaClone.getSkillLevel(self) or 0) return self.activeLevel or 0 diff --git a/gui/builtinItemStatsViews/itemAffectedBy.py b/gui/builtinItemStatsViews/itemAffectedBy.py index b29d84b89..742b12737 100644 --- a/gui/builtinItemStatsViews/itemAffectedBy.py +++ b/gui/builtinItemStatsViews/itemAffectedBy.py @@ -14,7 +14,7 @@ from eos.saveddata.fit import Fit import gui.mainFrame from gui.contextMenu import ContextMenu -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class ItemAffectedBy(wx.Panel): diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index 1e1804d1d..36bb5e9c7 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -5,9 +5,9 @@ import config # noinspection PyPackageRequirements import wx -from helpers import AutoListCtrl +from .helpers import AutoListCtrl -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.market import Market from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount diff --git a/gui/builtinItemStatsViews/itemCompare.py b/gui/builtinItemStatsViews/itemCompare.py index 4e560883e..e24bab79d 100644 --- a/gui/builtinItemStatsViews/itemCompare.py +++ b/gui/builtinItemStatsViews/itemCompare.py @@ -3,7 +3,7 @@ import sys # noinspection PyPackageRequirements import wx -from helpers import AutoListCtrl +from .helpers import AutoListCtrl from service.price import Price as ServicePrice from service.market import Market from service.attribute import Attribute diff --git a/gui/builtinItemStatsViews/itemDependants.py b/gui/builtinItemStatsViews/itemDependants.py index 3a8110b55..12c5ed174 100644 --- a/gui/builtinItemStatsViews/itemDependants.py +++ b/gui/builtinItemStatsViews/itemDependants.py @@ -1,7 +1,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class ItemDependents(wx.Panel): diff --git a/gui/builtinItemStatsViews/itemEffects.py b/gui/builtinItemStatsViews/itemEffects.py index 81617fdc6..75a795e1d 100644 --- a/gui/builtinItemStatsViews/itemEffects.py +++ b/gui/builtinItemStatsViews/itemEffects.py @@ -6,7 +6,7 @@ import config # noinspection PyPackageRequirements import wx -from helpers import AutoListCtrl +from .helpers import AutoListCtrl class ItemEffects(wx.Panel): diff --git a/gui/builtinItemStatsViews/itemProperties.py b/gui/builtinItemStatsViews/itemProperties.py index b8ce86dd5..6def52ac8 100644 --- a/gui/builtinItemStatsViews/itemProperties.py +++ b/gui/builtinItemStatsViews/itemProperties.py @@ -3,7 +3,7 @@ import sys # noinspection PyPackageRequirements import wx -from helpers import AutoListCtrl +from .helpers import AutoListCtrl class ItemProperties(wx.Panel): diff --git a/gui/builtinItemStatsViews/itemRequirements.py b/gui/builtinItemStatsViews/itemRequirements.py index ff493bcab..0783ca4f2 100644 --- a/gui/builtinItemStatsViews/itemRequirements.py +++ b/gui/builtinItemStatsViews/itemRequirements.py @@ -1,7 +1,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class ItemRequirements(wx.Panel): diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index 26c630b48..59b236a40 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -91,7 +91,7 @@ class PFGeneralPref(PreferenceView): priceSizer.Add(self.stDefaultSystem, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.chPriceSource = wx.Choice(panel, choices=sorted(Price.sources.keys())) - self.chPriceSystem = wx.Choice(panel, choices=Price.systemsList.keys()) + self.chPriceSystem = wx.Choice(panel, choices=list(Price.systemsList.keys())) priceSizer.Add(self.chPriceSource, 1, wx.ALL | wx.EXPAND, 5) priceSizer.Add(self.chPriceSystem, 1, wx.ALL | wx.EXPAND, 5) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 7b79f8fc0..80e28edf9 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -22,7 +22,6 @@ import wx import wx.dataview import wx.lib.agw.hyperlink -from utils.floatspin import FloatSpin # noinspection PyPackageRequirements import wx.lib.newevent # noinspection PyPackageRequirements @@ -346,7 +345,7 @@ class SkillTreeView(wx.Panel): bSizerButtons.Add(self.btnSecStatus, 0, wx.ALL, 5) - bSizerButtons.AddSpacer((0, 0), 1, wx.EXPAND, 5) + bSizerButtons.AddSpacer(0) importExport = (("Import", wx.ART_FILE_OPEN, "from"), ("Export", wx.ART_FILE_SAVE_AS, "to")) @@ -361,7 +360,7 @@ class SkillTreeView(wx.Panel): btn.Layout() setattr(self, "{}Btn".format(name.lower()), btn) btn.Enable(True) - btn.SetToolTipString("%s skills %s clipboard" % (name, direction)) + btn.SetToolTip("%s skills %s clipboard" % (name, direction)) bSizerButtons.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT | wx.ALL, 5) btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Skills".format(name.lower()))) diff --git a/service/price.py b/service/price.py index e942e1c8d..ab6963829 100644 --- a/service/price.py +++ b/service/price.py @@ -105,7 +105,7 @@ class Price(object): return # attempt to find user's selected price source, otherwise get first one - sourceCls = cls.sources.get(sFit.serviceFittingOptions["priceSource"], cls.sources[cls.sources.keys()[0]]) + sourceCls = cls.sources.get(sFit.serviceFittingOptions["priceSource"], cls.sources[list(cls.sources.keys())[0]]) sourceCls(toRequest, cls.systemsList[sFit.serviceFittingOptions["priceSystem"]], priceMap) # if we get to this point, then we've got an error. Set to REREQUEST delay From 5e0a5da6d53dcb12c83958cf6cf8e1dcf1e19926 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 23 Nov 2017 12:51:43 -0500 Subject: [PATCH 075/212] more post-merge work --- gui/shipBrowser.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 6566e8ef2..7163a3830 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -11,7 +11,7 @@ from gui.builtinShipBrowser.shipItem import ShipItem from service.fit import Fit from service.market import Market -import gui.builtinShipBrowser.events as events +from gui.builtinShipBrowser.events import * from gui.builtinShipBrowser.pfWidgetContainer import PFWidgetsContainer from gui.builtinShipBrowser.navigationPanel import NavigationPanel from gui.builtinShipBrowser.raceSelector import RaceSelector @@ -76,11 +76,11 @@ class ShipBrowser(wx.Panel): self.Show() self.Bind(wx.EVT_SIZE, self.SizeRefreshList) - self.Bind(events.EVT_SB_STAGE2_SEL, self.stage2) - self.Bind(events.EVT_SB_STAGE1_SEL, self.stage1) - self.Bind(events.EVT_SB_STAGE3_SEL, self.stage3) - self.Bind(events.EVT_SB_SEARCH_SEL, self.searchStage) - self.Bind(events.EVT_SB_IMPORT_SEL, self.importStage) + self.Bind(EVT_SB_STAGE2_SEL, self.stage2) + self.Bind(EVT_SB_STAGE1_SEL, self.stage1) + self.Bind(EVT_SB_STAGE3_SEL, self.stage3) + self.Bind(EVT_SB_SEARCH_SEL, self.searchStage) + self.Bind(EVT_SB_IMPORT_SEL, self.importStage) self.mainFrame.Bind(GE.FIT_CHANGED, self.RefreshList) From d23398ce29482e4d29085be89590348be83104b1 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 23 Nov 2017 20:51:03 -0500 Subject: [PATCH 076/212] fix for boosters --- eos/saveddata/boosterSideEffect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/saveddata/boosterSideEffect.py b/eos/saveddata/boosterSideEffect.py index 3fd39ac4e..ad0814928 100644 --- a/eos/saveddata/boosterSideEffect.py +++ b/eos/saveddata/boosterSideEffect.py @@ -39,7 +39,7 @@ class BoosterSideEffect(object): self.__effect = None if self.effectID: - self.__effect = next((x for x in self.booster.item.effects.itervalues() if x.ID == self.effectID), None) + self.__effect = next((x for x in self.booster.item.effects.values() if x.ID == self.effectID), None) if self.__effect is None: pyfalog.error("Effect (id: {0}) does not exist", self.effectID) return From 46b0aded0306347bb74f34fe4f3b86e54bcb89b8 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 23 Nov 2017 22:43:41 -0500 Subject: [PATCH 077/212] fixes --- eos/db/migrations/upgrade25.py | 2 +- gui/builtinItemStatsViews/itemAttributes.py | 2 +- gui/builtinItemStatsViews/itemDescription.py | 4 ++-- gui/builtinItemStatsViews/itemEffects.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eos/db/migrations/upgrade25.py b/eos/db/migrations/upgrade25.py index bf334c37d..3143480af 100644 --- a/eos/db/migrations/upgrade25.py +++ b/eos/db/migrations/upgrade25.py @@ -4204,7 +4204,7 @@ conversion2 = { def upgrade(saveddata_engine): # First we want to get a list of fittings that are completely fitted out with subsystems - oldItems = [str(x) for x in conversion2.iterkeys()] + oldItems = [str(x) for x in conversion2.keys()] # I can't figure out a way to get IN operator to work when supplying a list using a parameterized query. So I'm # doing it the shitty way by formatting the SQL string. Don't do this kids! diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index 36bb5e9c7..39152e566 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -166,7 +166,7 @@ class ItemParams(wx.Panel): self.imageList = wx.ImageList(16, 16) self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) - names = list(self.attrValues.iterkeys()) + names = list(self.attrValues.keys()) names.sort() idNameMap = {} diff --git a/gui/builtinItemStatsViews/itemDescription.py b/gui/builtinItemStatsViews/itemDescription.py index c94bd2b66..ddf30dfb4 100644 --- a/gui/builtinItemStatsViews/itemDescription.py +++ b/gui/builtinItemStatsViews/itemDescription.py @@ -11,8 +11,8 @@ class ItemDescription(wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainSizer) - bgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - fgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + bgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + fgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) self.description = wx.html.HtmlWindow(self) diff --git a/gui/builtinItemStatsViews/itemEffects.py b/gui/builtinItemStatsViews/itemEffects.py index 75a795e1d..0b7db9a2e 100644 --- a/gui/builtinItemStatsViews/itemEffects.py +++ b/gui/builtinItemStatsViews/itemEffects.py @@ -47,7 +47,7 @@ class ItemEffects(wx.Panel): item = self.item effects = item.effects - names = list(effects.iterkeys()) + names = list(effects.keys()) names.sort() for name in names: From e032c9c5b1ae441fc62db1de7f37a386426b73bd Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 23 Nov 2017 22:48:37 -0500 Subject: [PATCH 078/212] fix item stats dialog --- gui/builtinItemStatsViews/itemAffectedBy.py | 42 +++++++++---------- gui/builtinItemStatsViews/itemAttributes.py | 26 ++++++------ gui/builtinItemStatsViews/itemCompare.py | 29 ++++++------- gui/builtinItemStatsViews/itemDependants.py | 6 ++- gui/builtinItemStatsViews/itemDescription.py | 3 ++ gui/builtinItemStatsViews/itemEffects.py | 13 +++--- gui/builtinItemStatsViews/itemProperties.py | 8 ++-- gui/builtinItemStatsViews/itemRequirements.py | 6 ++- gui/builtinItemStatsViews/itemTraits.py | 1 + 9 files changed, 73 insertions(+), 61 deletions(-) diff --git a/gui/builtinItemStatsViews/itemAffectedBy.py b/gui/builtinItemStatsViews/itemAffectedBy.py index 742b12737..ed4280045 100644 --- a/gui/builtinItemStatsViews/itemAffectedBy.py +++ b/gui/builtinItemStatsViews/itemAffectedBy.py @@ -43,17 +43,17 @@ class ItemAffectedBy(wx.Panel): 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) + self.toggleExpandBtn = wx.ToggleButton(self, wx.ID_ANY, "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) + self.toggleNameBtn = wx.ToggleButton(self, wx.ID_ANY, "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) + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "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) + self.refreshBtn = wx.Button(self, wx.ID_ANY, "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) @@ -74,9 +74,9 @@ class ItemAffectedBy(wx.Panel): def spawnMenu(self, item): self.affectedBy.SelectItem(item) - stuff = self.affectedBy.GetPyData(item) + stuff = self.affectedBy.GetItemData(item) # String is set as data when we are dealing with attributes, not stuff containers - if stuff is None or isinstance(stuff, basestring): + if stuff is None or isinstance(stuff, str): return contexts = [] @@ -109,10 +109,10 @@ class ItemAffectedBy(wx.Panel): self.Freeze() for item in self.treeItems: - change = self.affectedBy.GetPyData(item) + change = self.affectedBy.GetItemData(item) display = self.affectedBy.GetItemText(item) self.affectedBy.SetItemText(item, change) - self.affectedBy.SetPyData(item, display) + self.affectedBy.SetItemData(item, display) self.Thaw() @@ -141,7 +141,7 @@ class ItemAffectedBy(wx.Panel): # sheri was here del self.treeItems[:] root = self.affectedBy.AddRoot("WINPWNZ0R") - self.affectedBy.SetPyData(root, None) + self.affectedBy.SetItemData(root, None) self.imageList = wx.ImageList(16, 16) self.affectedBy.SetImageList(self.imageList) @@ -185,7 +185,7 @@ class ItemAffectedBy(wx.Panel): if attributes[attrName] == (attributes.getOriginal(attrName, 0)): continue - for fit, afflictors in attributes.getAfflictions(attrName).iteritems(): + for fit, afflictors in attributes.getAfflictions(attrName).items(): for afflictor, modifier, amount, used in afflictors: if not used or afflictor.item is None: @@ -216,7 +216,7 @@ class ItemAffectedBy(wx.Panel): (type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) # Make sure projected fits are on top - rootOrder = container.keys() + rootOrder = list(container.keys()) rootOrder.sort(key=lambda x: self.ORDER.index(type(x))) # Now, we take our created dictionary and start adding stuff to our tree @@ -230,7 +230,7 @@ class ItemAffectedBy(wx.Panel): parent = child attributes = container[thing] - attrOrder = sorted(attributes.keys(), key=self.sortAttrDisplayName) + attrOrder = sorted(list(attributes.keys()), key=self.sortAttrDisplayName) for attrName in attrOrder: attrInfo = self.stuff.item.attributes.get(attrName) @@ -257,7 +257,7 @@ class ItemAffectedBy(wx.Panel): # this is the attribute node child = self.affectedBy.AppendItem(parent, display, attrIcon) - self.affectedBy.SetPyData(child, saved) + self.affectedBy.SetItemData(child, saved) self.treeItems.append(child) items = attributes[attrName] @@ -284,12 +284,12 @@ class ItemAffectedBy(wx.Panel): penalized += "(penalized)" if 'r' in attrModifier: penalized += "(resisted)" - attrModifier = "*" + attrModifier = "*" # this is the Module node, the attribute will be attached to this display = "%s %s %.2f %s" % (displayStr, attrModifier, attrAmount, penalized) treeItem = self.affectedBy.AppendItem(child, display, itemIcon) - self.affectedBy.SetPyData(treeItem, afflictor) + self.affectedBy.SetItemData(treeItem, afflictor) def buildModuleView(self, root): """ @@ -314,7 +314,7 @@ class ItemAffectedBy(wx.Panel): if attributes[attrName] == (attributes.getOriginal(attrName, 0)): continue - for fit, afflictors in attributes.getAfflictions(attrName).iteritems(): + for fit, afflictors in attributes.getAfflictions(attrName).items(): for afflictor, modifier, amount, used in afflictors: if not used or getattr(afflictor, 'item', None) is None: continue @@ -350,7 +350,7 @@ class ItemAffectedBy(wx.Panel): info[2].append((attrName, modifier, amount)) # Make sure projected fits are on top - rootOrder = container.keys() + rootOrder = list(container.keys()) rootOrder.sort(key=lambda x: self.ORDER.index(type(x))) # Now, we take our created dictionary and start adding stuff to our tree @@ -364,7 +364,7 @@ class ItemAffectedBy(wx.Panel): parent = child items = container[thing] - order = items.keys() + order = list(items.keys()) order.sort(key=lambda x: (self.ORDER.index(items[x][0]), x)) for itemName in order: @@ -389,7 +389,7 @@ class ItemAffectedBy(wx.Panel): # this is the Module node, the attribute will be attached to this child = self.affectedBy.AppendItem(parent, displayStr, itemIcon) - self.affectedBy.SetPyData(child, afflictors.pop()) + self.affectedBy.SetItemData(child, afflictors.pop()) if counter > 0: attributes = [] @@ -416,7 +416,7 @@ class ItemAffectedBy(wx.Panel): penalized += "(penalized)" if 'r' in attrModifier: penalized += "(resisted)" - attrModifier = "*" + attrModifier = "*" attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized, attrIcon)) @@ -443,5 +443,5 @@ class ItemAffectedBy(wx.Panel): saved = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized) treeitem = self.affectedBy.AppendItem(child, display, attrIcon) - self.affectedBy.SetPyData(treeitem, saved) + self.affectedBy.SetItemData(treeitem, saved) self.treeItems.append(treeitem) diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index 39152e566..f5073796c 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -12,7 +12,6 @@ from service.market import Market from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount - class ItemParams(wx.Panel): def __init__(self, parent, stuff, item, context=None): wx.Panel.__init__(self, parent) @@ -34,19 +33,19 @@ class ItemParams(wx.Panel): 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) + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", 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, + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "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, + self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, "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) + self.refreshBtn = wx.Button(self, wx.ID_ANY, "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) @@ -203,7 +202,7 @@ class ItemParams(wx.Panel): else: attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) - index = self.paramList.InsertImageStringItem(sys.maxint, attrName, attrIcon) + index = self.paramList.InsertItem(sys.maxsize, attrName, attrIcon) idNameMap[idCount] = attrName self.paramList.SetItemData(index, idCount) idCount += 1 @@ -222,11 +221,12 @@ class ItemParams(wx.Panel): else: valueUnitDefault = formatAmount(valueDefault, 3, 0, 0) - self.paramList.SetStringItem(index, 1, valueUnit) + self.paramList.SetItem(index, 1, valueUnit) 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.SetItem(index, 2, valueUnitDefault) + # @todo: pheonix, this lamda used cmp() which no longer exists in py3. Probably a better way to do this in the + # long run, take a look + self.paramList.SortItems(lambda id1, id2: (idNameMap[id1]>idNameMap[id2])-(idNameMap[id1]idNameMap[id2])-(idNameMap[id1] Date: Fri, 24 Nov 2017 00:55:23 -0500 Subject: [PATCH 079/212] Fix an issue with with Cap Power Relay using an effect without an expecting source attribute. Used to work, but with changes to getModifiedItemAttr need to default these to None --- eos/effects/capacitorcapacitymultiply.py | 4 +++- eos/effects/poweroutputmultiply.py | 4 +++- eos/effects/shieldcapacitymultiply.py | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/eos/effects/capacitorcapacitymultiply.py b/eos/effects/capacitorcapacitymultiply.py index 4b277a8da..8b5df217d 100644 --- a/eos/effects/capacitorcapacitymultiply.py +++ b/eos/effects/capacitorcapacitymultiply.py @@ -10,4 +10,6 @@ type = "passive" def handler(fit, module, context): - fit.ship.multiplyItemAttr("capacitorCapacity", module.getModifiedItemAttr("capacitorCapacityMultiplier")) + # We default this to None as there are times when the source attribute doesn't exist (for example, Cap Power Relay). + # It will return 0 as it doesn't exist, which would nullify whatever the target attribute is + fit.ship.multiplyItemAttr("capacitorCapacity", module.getModifiedItemAttr("capacitorCapacityMultiplier", None)) diff --git a/eos/effects/poweroutputmultiply.py b/eos/effects/poweroutputmultiply.py index 3a0e5c6a1..f4d5eb600 100644 --- a/eos/effects/poweroutputmultiply.py +++ b/eos/effects/poweroutputmultiply.py @@ -9,4 +9,6 @@ type = "passive" def handler(fit, module, context): - fit.ship.multiplyItemAttr("powerOutput", module.getModifiedItemAttr("powerOutputMultiplier")) + # We default this to None as there are times when the source attribute doesn't exist (for example, Cap Power Relay). + # It will return 0 as it doesn't exist, which would nullify whatever the target attribute is + fit.ship.multiplyItemAttr("powerOutput", module.getModifiedItemAttr("powerOutputMultiplier", None)) diff --git a/eos/effects/shieldcapacitymultiply.py b/eos/effects/shieldcapacitymultiply.py index 71c91f19e..3c9aeea6d 100644 --- a/eos/effects/shieldcapacitymultiply.py +++ b/eos/effects/shieldcapacitymultiply.py @@ -9,4 +9,6 @@ type = "passive" def handler(fit, module, context): - fit.ship.multiplyItemAttr("shieldCapacity", module.getModifiedItemAttr("shieldCapacityMultiplier")) + # We default this to None as there are times when the source attribute doesn't exist (for example, Cap Power Relay). + # It will return 0 as it doesn't exist, which would nullify whatever the target attribute is + fit.ship.multiplyItemAttr("shieldCapacity", module.getModifiedItemAttr("shieldCapacityMultiplier", None)) From 5697da7ec2d22a6a9b8021abe8ebc9baf457c070 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 24 Nov 2017 01:23:04 -0500 Subject: [PATCH 080/212] fix upgrade --- eos/db/migrations/upgrade25.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/db/migrations/upgrade25.py b/eos/db/migrations/upgrade25.py index 3143480af..703048f37 100644 --- a/eos/db/migrations/upgrade25.py +++ b/eos/db/migrations/upgrade25.py @@ -4239,7 +4239,7 @@ def upgrade(saveddata_engine): # if something fails, fuck it, we tried. It'll default to the generic conversion below continue - for oldItem, newItem in conversion2.iteritems(): + for oldItem, newItem in conversion2.items(): saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (newItem, oldItem)) saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', From 0a935cf1491ecb135458705df27b669b78616611 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 24 Nov 2017 02:02:09 -0500 Subject: [PATCH 081/212] fix gtk warnings --- gui/chrome_tabs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index 8e5982221..fd1624751 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -277,7 +277,7 @@ class ChromeNotebook(wx.Panel): bx, by = self.GetBorders() ww -= bx * 4 wh -= by * 4 - self._active_page.SetSize((ww, wh)) + self._active_page.SetSize((max(ww, -1), max(wh, -1))) self._active_page.SetPosition((0, 0)) if not resize_only: From f3dc3bc6545b2e27704607d5f44f7fa63792c4b7 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 24 Nov 2017 12:22:21 -0500 Subject: [PATCH 082/212] Fix tabs not disabling. It was primarily an issue with event propagation not being handled correctly, as well as some missing helper code in chrome_tabs --- gui/builtinContextMenus/commandFits.py | 1 + gui/chrome_tabs.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/gui/builtinContextMenus/commandFits.py b/gui/builtinContextMenus/commandFits.py index 48758c343..936dd0cc0 100644 --- a/gui/builtinContextMenus/commandFits.py +++ b/gui/builtinContextMenus/commandFits.py @@ -30,6 +30,7 @@ class CommandFits(ContextMenu): if evt is None or not ids.isdisjoint(cls.commandTypeIDs): # we are adding or removing an item that defines a command fit. Need to refresh fit list cls.populateFits(evt) + evt.Skip() @classmethod def populateFits(cls, evt): diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index fd1624751..e2212499e 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -542,7 +542,7 @@ class _TabRenderer: # draw the tab icon if self.tab_img: - bmp = wx.Bitmap(self.tab_img) + bmp = wx.Bitmap(self.tab_img.ConvertToGreyscale() if self.disabled else self.tab_img) # @todo: is this conditional relevant anymore? if self.content_width > 16: # Draw tab icon @@ -595,6 +595,12 @@ class _TabRenderer: self.tab_bitmap = bmp + def __repr__(self): + return "_TabRenderer(text={}, disabled={}) at {}".format( + self.text, self.disabled, hex(id(self)) + ) + + class _AddRenderer: def __init__(self): """Renders the add tab button""" @@ -901,6 +907,8 @@ class _TabsContainer(wx.Panel): return True if self.TabHitTest(tab, x, y): + if tab.disabled: + return tab.SetSelected(True) old_sel_tab.SetSelected(False) From 30a6e29b39b445d646bfd18a91cdab77c858f282 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 24 Nov 2017 12:30:46 -0500 Subject: [PATCH 083/212] Fix slight sizing issue with toggle panel header arrow --- gui/toggle_panel.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/gui/toggle_panel.py b/gui/toggle_panel.py index 7340af112..0813ae26d 100644 --- a/gui/toggle_panel.py +++ b/gui/toggle_panel.py @@ -37,7 +37,7 @@ class TogglePanel (wx.Panel): # Add arrow self.header_arrow = wx.StaticText(self.header_panel, wx.ID_ANY, - "\u25bc") + "\u25bc", size=wx.Size((10, -1))) header_sizer.Add(self.header_arrow, 0, wx.RIGHT, 5) # Add header text @@ -143,7 +143,10 @@ class TogglePanel (wx.Panel): self.OnStateChange(self.GetBestSize()) if __name__ == "__main__": - class TestPanel(wx.Panel): + + from wx.lib.inspection import InspectionTool + + class MainPanel(wx.Panel): def __init__(self, parent): super().__init__(parent, size=(-1, -1)) @@ -176,11 +179,21 @@ if __name__ == "__main__": super().__init__(None, title=title, size=(500, 500)) main_sizer = wx.BoxSizer(wx.VERTICAL) - self.statsPane = TestPanel(self) + self.statsPane = MainPanel(self) main_sizer.Add(self.statsPane, 0, wx.EXPAND) self.SetSizerAndFit(main_sizer) + if not InspectionTool().initialized: + InspectionTool().Init() + + # Find a widget to be selected in the tree. Use either the + # one under the cursor, if any, or this frame. + wnd, _ = wx.FindWindowAtPointer() + if not wnd: + wnd = self + InspectionTool().Show(wnd, True) + app = wx.App(redirect=False) # Error messages go to popup window top = Frame("Test Toggle Panel") top.Show() From 53936a3e66d1240db91e9cdd896a9c8cd6bef523 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 24 Nov 2017 12:44:00 -0500 Subject: [PATCH 084/212] Fix additional panel note toggling correctly --- gui/additionsPane.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/gui/additionsPane.py b/gui/additionsPane.py index dd1ebf25e..8bc72f8ec 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -92,19 +92,16 @@ class AdditionsPane(TogglePanel): def getName(self, idx): return self.PANES[idx] - def toggleContent(self, event): - TogglePanel.toggleContent(self, event) - h = self.headerPanel.GetSize()[1] + 4 + def ToggleContent(self, event): + TogglePanel.ToggleContent(self, event) + h = self.header_panel.GetSize()[1] + 4 if self.IsCollapsed(): self.old_pos = self.parent.GetSashPosition() self.parent.SetMinimumPaneSize(h) self.parent.SetSashPosition(h * -1, True) - # only available in >= wx2.9 - if getattr(self.parent, "SetSashInvisible", None): - self.parent.SetSashInvisible(True) + self.parent.SetSashInvisible(True) else: - if getattr(self.parent, "SetSashInvisible", None): - self.parent.SetSashInvisible(False) + self.parent.SetSashInvisible(False) self.parent.SetMinimumPaneSize(200) self.parent.SetSashPosition(self.old_pos, True) From 337f0a9c8a4707bf8a68179773cfcbc6a5e25a4e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 24 Nov 2017 17:23:04 -0500 Subject: [PATCH 085/212] Fix stretchspacers --- gui/builtinAdditionPanes/fighterView.py | 5 ++--- gui/builtinAdditionPanes/implantView.py | 4 ++-- gui/builtinPreferenceViews/dummyView.py | 2 +- gui/builtinPreferenceViews/pyfaDatabasePreferences.py | 2 +- gui/builtinPreferenceViews/pyfaNetworkPreferences.py | 2 +- gui/builtinPreferenceViews/pyfaUpdatePreferences.py | 4 ++-- gui/builtinStatsViews/resourcesViewFull.py | 4 ++-- gui/builtinViews/implantEditor.py | 4 ++-- gui/characterEditor.py | 2 +- gui/preferenceDialog.py | 2 +- gui/updateDialog.py | 5 ++--- 11 files changed, 17 insertions(+), 19 deletions(-) diff --git a/gui/builtinAdditionPanes/fighterView.py b/gui/builtinAdditionPanes/fighterView.py index e68b56820..9eec32dab 100644 --- a/gui/builtinAdditionPanes/fighterView.py +++ b/gui/builtinAdditionPanes/fighterView.py @@ -60,8 +60,7 @@ class FighterView(wx.Panel): mainSizer.Add(self.fighterDisplay, 1, wx.EXPAND, 0) textSizer = wx.BoxSizer(wx.HORIZONTAL) - # @todo pheonix: Add spacer doesn't take a tuple anymore, using int of 0, and other parameters are killed off - textSizer.AddSpacer(0) + textSizer.AddStretchSpacer() for x in self.labels: lbl = wx.StaticText(self, wx.ID_ANY, x.capitalize()) @@ -76,7 +75,7 @@ class FighterView(wx.Panel): lbl = wx.StaticText(self, wx.ID_ANY, "0") setattr(self, "label%sTotal" % (x.capitalize()), lbl) textSizer.Add(lbl, 0, wx.ALIGN_CENTER) - textSizer.AddSpacer(0) + textSizer.AddStretchSpacer() mainSizer.Add(textSizer, 0, wx.EXPAND, 5) diff --git a/gui/builtinAdditionPanes/implantView.py b/gui/builtinAdditionPanes/implantView.py index 8aae19435..8e043702f 100644 --- a/gui/builtinAdditionPanes/implantView.py +++ b/gui/builtinAdditionPanes/implantView.py @@ -41,12 +41,12 @@ class ImplantView(wx.Panel): mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0) radioSizer = wx.BoxSizer(wx.HORIZONTAL) - radioSizer.AddSpacer(0) + radioSizer.AddStretchSpacer() 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) radioSizer.Add(self.rbChar, 0, wx.ALL, 5) - radioSizer.AddSpacer(0) + radioSizer.AddStretchSpacer() mainSizer.Add(radioSizer, 0, wx.EXPAND, 5) diff --git a/gui/builtinPreferenceViews/dummyView.py b/gui/builtinPreferenceViews/dummyView.py index 6d964ad78..959057f11 100644 --- a/gui/builtinPreferenceViews/dummyView.py +++ b/gui/builtinPreferenceViews/dummyView.py @@ -80,7 +80,7 @@ class DummyView(PreferenceView): def initFooter(self, panel): footerSizer = wx.BoxSizer(wx.HORIZONTAL) - footerSizer.AddSpacer(0) + footerSizer.AddStretchSpacer() self.btnRestore = wx.Button(panel, wx.ID_ANY, "Restore", wx.DefaultPosition, wx.DefaultSize, 0) self.btnRestore.Enable(False) diff --git a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py index d734d577a..5ecdf9570 100644 --- a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py +++ b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py @@ -76,7 +76,7 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.m_staticline3, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) btnSizer = wx.BoxSizer(wx.VERTICAL) - btnSizer.AddSpacer(0) + btnSizer.AddStretchSpacer() self.btnDeleteDamagePatterns = wx.Button(panel, wx.ID_ANY, "Delete All Damage Pattern Profiles", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnDeleteDamagePatterns, 0, wx.ALL, 5) diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index 4b8b620a8..6b7669e15 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -139,7 +139,7 @@ class PFNetworkPref(PreferenceView): mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) btnSizer = wx.BoxSizer(wx.HORIZONTAL) - btnSizer.AddSpacer(0) + btnSizer.AddStretchSpacer() self.btnApply = wx.Button(panel, wx.ID_ANY, "Apply Proxy Settings", wx.DefaultPosition, wx.DefaultSize, 0) diff --git a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py index b99b9fac2..f07d0380f 100644 --- a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py +++ b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py @@ -52,11 +52,11 @@ class PFUpdatePref(PreferenceView): "You can choose to reset notification suppression for this release, " "or download the new release from GitHub.") - self.versionSizer.AddSpacer(0) + self.versionSizer.AddStretchSpacer() self.versionSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 5) - self.versionSizer.AddSpacer(0) + self.versionSizer.AddStretchSpacer() self.versionSizer.Add(self.versionTitle, 0, wx.EXPAND, 5) self.versionDesc = wx.StaticText(panel, wx.ID_ANY, self.versionInfo, wx.DefaultPosition, wx.DefaultSize, 0) diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index 2ab61cf2b..77a1eaae5 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -102,7 +102,7 @@ class ResourcesViewFull(StatsView): panel = "full" base = sizerResources - sizer.AddSpacer(0) + sizer.AddStretchSpacer() # Turrets & launcher hardslots display tooltipText = {"turret": "Turret hardpoints", "launcher": "Launcher hardpoints", "drones": "Drones active", "fighter": "Fighter squadrons active", "calibration": "Calibration"} @@ -134,7 +134,7 @@ class ResourcesViewFull(StatsView): # 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": - sizer.AddSpacer(0) + sizer.AddStretchSpacer() gauge_font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index d950dd6c7..b391c1812 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -54,7 +54,7 @@ class BaseImplantEditorView(wx.Panel): pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) buttonSizer = wx.BoxSizer(wx.VERTICAL) - buttonSizer.AddSpacer(0) + buttonSizer.AddStretchSpacer() self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style=wx.BORDER_NONE) @@ -64,7 +64,7 @@ class BaseImplantEditorView(wx.Panel): style=wx.BORDER_NONE) buttonSizer.Add(self.btnRemove, 0) - buttonSizer.AddSpacer(0) + buttonSizer.AddStretchSpacer() pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 0) characterImplantSizer = wx.BoxSizer(wx.VERTICAL) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 80e28edf9..95fda70b8 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -345,7 +345,7 @@ class SkillTreeView(wx.Panel): bSizerButtons.Add(self.btnSecStatus, 0, wx.ALL, 5) - bSizerButtons.AddSpacer(0) + bSizerButtons.AddStretchSpacer() importExport = (("Import", wx.ART_FILE_OPEN, "from"), ("Export", wx.ART_FILE_SAVE_AS, "to")) diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index 4a1c79a21..05701987d 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -46,7 +46,7 @@ class PreferenceDialog(wx.Dialog): mainSizer.Add(self.m_staticline2, 0, wx.EXPAND, 5) btnSizer = wx.BoxSizer(wx.HORIZONTAL) - btnSizer.AddSpacer(0) + btnSizer.AddStretchSpacer() self.btnOK = wx.Button(self, wx.ID_ANY, "OK", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnOK, 0, wx.ALL, 5) mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 7cb793dc5..9adaa3594 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -70,10 +70,9 @@ class UpdateDialog(wx.Dialog): self.versionText.SetFont(wx.Font(12, 74, 90, 90, False)) versionSizer.Add(self.versionText, 1, wx.ALL, 5) - versionSizer.AddSpacer(15) - mainSizer.Add(versionSizer, 0, wx.EXPAND, 5) - mainSizer.AddSpacer(0) + + mainSizer.Add(versionSizer, 0, wx.EXPAND, 0) releaseDate = dateutil.parser.parse(self.releaseInfo['published_at']) notesSizer = wx.BoxSizer(wx.HORIZONTAL) From 3c405f51d84bcc9b86a4aeb20bff52129fcb5340 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 24 Nov 2017 17:27:44 -0500 Subject: [PATCH 086/212] Remove platform-specific CREST checks (add for mac-deprecated client) --- gui/builtinPreferenceViews/__init__.py | 6 +-- .../pyfaCrestPreferences.py | 3 +- gui/builtinViews/fittingView.py | 12 ++--- gui/crestFittings.py | 3 +- gui/mainFrame.py | 13 +++--- gui/mainMenuBar.py | 45 +++++++++---------- pyfa.py | 13 +++--- service/port.py | 3 +- 8 files changed, 44 insertions(+), 54 deletions(-) diff --git a/gui/builtinPreferenceViews/__init__.py b/gui/builtinPreferenceViews/__init__.py index 8b61beb13..e6b55fb3c 100644 --- a/gui/builtinPreferenceViews/__init__.py +++ b/gui/builtinPreferenceViews/__init__.py @@ -1,6 +1,3 @@ -# noinspection PyPackageRequirements -import wx - __all__ = [ "pyfaGeneralPreferences", "pyfaHTMLExportPreferences", @@ -10,7 +7,6 @@ __all__ = [ "pyfaLoggingPreferences", "pyfaEnginePreferences", "pyfaStatViewPreferences", + "pyfaCrestPreferences" ] -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - __all__.append("pyfaCrestPreferences") diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py index 2e82cb7bc..f2dbe694d 100644 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ b/gui/builtinPreferenceViews/pyfaCrestPreferences.py @@ -11,8 +11,7 @@ from service.settings import CRESTSettings # noinspection PyPackageRequirements from wx.lib.intctrl import IntCtrl -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - from service.crest import Crest +from service.crest import Crest class PFCrestPref(PreferenceView): diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 69fa3c918..271278283 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -684,12 +684,12 @@ class FittingView(d.Display): self.Thaw() self.itemCount = self.GetItemCount() - if 'wxMac' in wx.PlatformInfo: - try: - self.MakeSnapshot() - except Exception as e: - pyfalog.critical("Failed to make snapshot") - pyfalog.critical(e) + # if 'wxMac' in wx.PlatformInfo: + # try: + # self.MakeSnapshot() + # except Exception as e: + # pyfalog.critical("Failed to make snapshot") + # pyfalog.critical(e) def OnShow(self, event): pass diff --git a/gui/crestFittings.py b/gui/crestFittings.py index c23abb93c..bff2c5b9b 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -18,8 +18,7 @@ from logbook import Logger import calendar pyfalog = Logger(__name__) -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - from service.crest import Crest, CrestModes +from service.crest import Crest, CrestModes class CrestFittings(wx.Frame): diff --git a/gui/mainFrame.py b/gui/mainFrame.py index be3a1cd27..849c295fe 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -81,10 +81,10 @@ from time import gmtime, strftime import threading import webbrowser import wx.adv -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - from service.crest import Crest - from service.crest import CrestModes - from gui.crestFittings import CrestFittings, ExportToEve, CrestMgmt + +from service.crest import Crest +from service.crest import CrestModes +from gui.crestFittings import CrestFittings, ExportToEve, CrestMgmt disableOverrideEditor = False @@ -236,9 +236,8 @@ class MainFrame(wx.Frame): self.sUpdate = Update.getInstance() self.sUpdate.CheckUpdate(self.ShowUpdateBox) - 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) + self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin) + self.Bind(GE.EVT_SSO_LOGOUT, self.onSSOLogout) self.titleTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.updateTitle, self.titleTimer) diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index aa85e0883..7fba900e2 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -30,9 +30,8 @@ from gui.bitmap_loader import BitmapLoader from logbook import Logger pyfalog = Logger(__name__) -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - from service.crest import Crest - from service.crest import CrestModes +from service.crest import Crest +from service.crest import CrestModes class MainMenuBar(wx.MenuBar): @@ -61,6 +60,7 @@ class MainMenuBar(wx.MenuBar): self.toggleIgnoreRestrictionID = wx.NewId() self.devToolsId = wx.NewId() + # pheonix: evaluate if this is needed if 'wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0): wx.ID_COPY = wx.NewId() wx.ID_PASTE = wx.NewId() @@ -134,29 +134,28 @@ class MainMenuBar(wx.MenuBar): preferencesItem.SetBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) windowMenu.Append(preferencesItem) - if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - self.sCrest = Crest.getInstance() + self.sCrest = Crest.getInstance() - # CREST Menu - crestMenu = wx.Menu() - self.Append(crestMenu, "&CREST") - if self.sCrest.settings.get('mode') != CrestModes.IMPLICIT: - crestMenu.Append(self.ssoLoginId, "Manage Characters") - else: - crestMenu.Append(self.ssoLoginId, "Login to EVE") - crestMenu.Append(self.eveFittingsId, "Browse EVE Fittings") - crestMenu.Append(self.exportToEveId, "Export To EVE") + # CREST Menu + crestMenu = wx.Menu() + self.Append(crestMenu, "&CREST") + if self.sCrest.settings.get('mode') != CrestModes.IMPLICIT: + crestMenu.Append(self.ssoLoginId, "Manage Characters") + else: + crestMenu.Append(self.ssoLoginId, "Login to EVE") + crestMenu.Append(self.eveFittingsId, "Browse EVE Fittings") + crestMenu.Append(self.exportToEveId, "Export To EVE") - if self.sCrest.settings.get('mode') == CrestModes.IMPLICIT or len(self.sCrest.getCrestCharacters()) == 0: - self.Enable(self.eveFittingsId, False) - self.Enable(self.exportToEveId, False) + if self.sCrest.settings.get('mode') == CrestModes.IMPLICIT or len(self.sCrest.getCrestCharacters()) == 0: + self.Enable(self.eveFittingsId, False) + self.Enable(self.exportToEveId, False) - if not self.mainFrame.disableOverrideEditor: - windowMenu.AppendSeparator() - attrItem = wx.MenuItem(windowMenu, self.attrEditorId, "Attribute Overrides\tCTRL+B") - attrItem.SetBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) - windowMenu.Append(attrItem) - windowMenu.Append(self.toggleOverridesId, "Turn Overrides On") + if not self.mainFrame.disableOverrideEditor: + windowMenu.AppendSeparator() + attrItem = wx.MenuItem(windowMenu, self.attrEditorId, "Attribute Overrides\tCTRL+B") + attrItem.SetBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) + windowMenu.Append(attrItem) + windowMenu.Append(self.toggleOverridesId, "Turn Overrides On") # Help menu helpMenu = wx.Menu() diff --git a/pyfa.py b/pyfa.py index 379f67de9..3a573a6e2 100755 --- a/pyfa.py +++ b/pyfa.py @@ -371,13 +371,12 @@ if __name__ == "__main__": # if int(logVersion[0]) == 0 and int(logVersion[1]) < 10: # raise PreCheckException("Logbook version >= 0.10.0 is required.") - if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - try: - import requests - config.requestsVersion = requests.__version__ - except ImportError: - pass - # raise PreCheckException("Cannot import requests. You can download requests from https://pypi.python.org/pypi/requests.") + try: + import requests + config.requestsVersion = requests.__version__ + except ImportError: + pass + # raise PreCheckException("Cannot import requests. You can download requests from https://pypi.python.org/pypi/requests.") import eos.db diff --git a/service/port.py b/service/port.py index 6c743570c..2a396f854 100644 --- a/service/port.py +++ b/service/port.py @@ -49,8 +49,7 @@ from service.market import Market from utils.strfunctions import sequential_rep, replace_ltgt from abc import ABCMeta, abstractmethod -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - from service.crest import Crest +from service.crest import Crest pyfalog = Logger(__name__) From 65e17119af4cad18004bf0eb44ca4e4aa1c31bb1 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 25 Nov 2017 13:12:26 -0500 Subject: [PATCH 087/212] Deprecation fix --- gui/builtinContextMenus/priceOptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/builtinContextMenus/priceOptions.py b/gui/builtinContextMenus/priceOptions.py index 905ca9d4b..c9cb7f74a 100644 --- a/gui/builtinContextMenus/priceOptions.py +++ b/gui/builtinContextMenus/priceOptions.py @@ -35,7 +35,7 @@ class PriceOptions(ContextMenu): for option in self.optionList: menuItem = self.addOption(rootMenu if msw else sub, option) - sub.AppendItem(menuItem) + sub.Append(menuItem) menuItem.Check(self.settings.get(option.lower())) return sub From b3157303cd1c7f9dd57c10a122a910512743f5a9 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 25 Nov 2017 13:13:09 -0500 Subject: [PATCH 088/212] Reenable error dialog and remove versioning info - want to look into a more uniform way of doing this if possible, and the requirements are all changing. --- gui/errorDialog.py | 87 ++++++++++----------- pyfa.py | 183 +++++++++++++++++++++++---------------------- 2 files changed, 136 insertions(+), 134 deletions(-) diff --git a/gui/errorDialog.py b/gui/errorDialog.py index f20da128e..627e719f9 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -40,6 +40,9 @@ try: except: logbook_version = "Unknown" +import wx.lib.agw.hyperlink + + class ErrorFrame(wx.Frame): def __init__(self, exception=None, tb=None, error_title='Error!'): @@ -74,13 +77,11 @@ class ErrorFrame(wx.Frame): descText = wx.StaticText(self, wx.ID_ANY, desc) box.Add(descText, 1, wx.ALL, 5) - github = wx.HyperlinkCtrl(self, wx.ID_ANY, "Github", "https://github.com/pyfa-org/Pyfa/issues", - wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE) - box.Add(github, 0, wx.ALL, 5) - - eveForums = wx.HyperlinkCtrl(self, wx.ID_ANY, "EVE Forums", "https://forums.eveonline.com/t/27156", - wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE) - box.Add(eveForums, 0, wx.ALL, 5) + # github = wx.lib.agw.hyperlink.HyperLinkCtrl(self, wx.ID_ANY, label="Github", URL="https://github.com/pyfa-org/Pyfa/issues") + # box.Add(github, 0, wx.ALL, 5) + # + # eveForums = wx.lib.agw.hyperlink.HyperLinkCtrl(self, wx.ID_ANY, label="EVE Forums", URL="https://forums.eveonline.com/t/27156") + # box.Add(eveForums, 0, wx.ALL, 5) # mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) @@ -88,42 +89,42 @@ class ErrorFrame(wx.Frame): errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) mainSizer.Add(errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5) - try: - errorTextCtrl.AppendText("OS version: \t" + str(platform.platform())) - except: - errorTextCtrl.AppendText("OS version: Unknown") - errorTextCtrl.AppendText("\n") - - try: - errorTextCtrl.AppendText("Python: \t" + '{}.{}.{}'.format(v.major, v.minor, v.micro)) - except: - errorTextCtrl.AppendText("Python: Unknown") - errorTextCtrl.AppendText("\n") - - try: - errorTextCtrl.AppendText("wxPython: \t" + wx.VERSION_STRING) - except: - errorTextCtrl.AppendText("wxPython: Unknown") - errorTextCtrl.AppendText("\n") - - errorTextCtrl.AppendText("SQLAlchemy: \t" + str(sqlalchemy_version)) - errorTextCtrl.AppendText("\n") - - errorTextCtrl.AppendText("Logbook: \t" + str(logbook_version)) - errorTextCtrl.AppendText("\n") - - try: - errorTextCtrl.AppendText("pyfa version: {0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion)) - except: - errorTextCtrl.AppendText("pyfa version: Unknown") - errorTextCtrl.AppendText('\n') - - errorTextCtrl.AppendText("pyfa root: " + str(config.pyfaPath or "Unknown")) - errorTextCtrl.AppendText('\n') - errorTextCtrl.AppendText("save path: " + str(config.savePath or "Unknown")) - errorTextCtrl.AppendText('\n') - errorTextCtrl.AppendText("fs encoding: " + str(sys.getfilesystemencoding() or "Unknown")) - errorTextCtrl.AppendText('\n\n') + # try: + # errorTextCtrl.AppendText("OS version: \t" + str(platform.platform())) + # except: + # errorTextCtrl.AppendText("OS version: Unknown") + # errorTextCtrl.AppendText("\n") + # + # try: + # errorTextCtrl.AppendText("Python: \t" + '{}.{}.{}'.format(v.major, v.minor, v.micro)) + # except: + # errorTextCtrl.AppendText("Python: Unknown") + # errorTextCtrl.AppendText("\n") + # + # try: + # errorTextCtrl.AppendText("wxPython: \t" + wx.VERSION_STRING) + # except: + # errorTextCtrl.AppendText("wxPython: Unknown") + # errorTextCtrl.AppendText("\n") + # + # errorTextCtrl.AppendText("SQLAlchemy: \t" + str(sqlalchemy_version)) + # errorTextCtrl.AppendText("\n") + # + # errorTextCtrl.AppendText("Logbook: \t" + str(logbook_version)) + # errorTextCtrl.AppendText("\n") + # + # try: + # errorTextCtrl.AppendText("pyfa version: {0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion)) + # except: + # errorTextCtrl.AppendText("pyfa version: Unknown") + # errorTextCtrl.AppendText('\n') + # + # errorTextCtrl.AppendText("pyfa root: " + str(config.pyfaPath or "Unknown")) + # errorTextCtrl.AppendText('\n') + # errorTextCtrl.AppendText("save path: " + str(config.savePath or "Unknown")) + # errorTextCtrl.AppendText('\n') + # errorTextCtrl.AppendText("fs encoding: " + str(sys.getfilesystemencoding() or "Unknown")) + # errorTextCtrl.AppendText('\n\n') errorTextCtrl.AppendText("EXCEPTION: " + str(exception or "Unknown")) errorTextCtrl.AppendText('\n\n') diff --git a/pyfa.py b/pyfa.py index 3a573a6e2..59e416b8b 100755 --- a/pyfa.py +++ b/pyfa.py @@ -51,8 +51,6 @@ ascii_text = ''' |_| |___/ You are running a alpha/beta version of pyfa. -If you run into problems, please let me know at: -https://github.com/pyfa-org/Pyfa/issues ++++++++++++++++++++++++++++++++++++++++++++++++++ ''' @@ -95,87 +93,88 @@ class PassThroughOptionParser(OptionParser): # self.level(sys.stderr) # # -# class PreCheckException(Exception): -# def __init__(self, msg): -# try: -# ln = sys.exc_info()[-1].tb_lineno -# except AttributeError: -# ln = inspect.currentframe().f_back.f_lineno -# self.message = "{0.__name__} (line {1}): {2}".format(type(self), ln, msg) -# self.args = self.message, -# -# -# def handleGUIException(exc_type, exc_value, exc_traceback): -# try: -# # Try and import wx in case it's missing. -# # noinspection PyPackageRequirements -# import wx -# from gui.errorDialog import ErrorFrame -# except: -# # noinspection PyShadowingNames -# wx = None -# # noinspection PyShadowingNames -# ErrorFrame = None -# -# tb = traceback.format_tb(exc_traceback) -# -# try: -# -# # Try and output to our log handler -# with logging_setup.threadbound(): -# module_list = list(set(sys.modules) & set(globals())) -# if module_list: -# pyfalog.info("Imported Python Modules:") -# for imported_module in module_list: -# module_details = sys.modules[imported_module] -# pyfalog.info("{0}: {1}", imported_module, getattr(module_details, '__version__', '')) -# -# pyfalog.critical("Exception in main thread: {0}", exc_value.message) -# # Print the base level traceback -# traceback.print_tb(exc_traceback) -# -# if wx and ErrorFrame: -# pyfa_gui = wx.App(False) -# if exc_type == PreCheckException: -# msgbox = wx.MessageBox(exc_value.message, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) -# msgbox.ShowModal() -# else: -# ErrorFrame(exc_value, tb) -# -# pyfa_gui.MainLoop() -# -# pyfalog.info("Exiting.") -# except: -# # Most likely logging isn't available. Try and output to the console -# module_list = list(set(sys.modules) & set(globals())) -# if module_list: -# pyfalog.info("Imported Python Modules:") -# for imported_module in module_list: -# module_details = sys.modules[imported_module] -# print((str(imported_module) + ": " + str(getattr(module_details, '__version__', '')))) -# -# print(("Exception in main thread: " + str(exc_value.message))) -# traceback.print_tb(exc_traceback) -# -# if wx and ErrorFrame: -# pyfa_gui = wx.App(False) -# if exc_type == PreCheckException: -# msgbox = wx.MessageBox(exc_value.message, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) -# msgbox.ShowModal() -# else: -# ErrorFrame(exc_value, tb) -# -# pyfa_gui.MainLoop() -# -# print("Exiting.") -# -# finally: -# # TODO: Add cleanup when exiting here. -# sys.exit() -# -# -# # Replace the uncaught exception handler with our own handler. -# sys.excepthook = handleGUIException +class PreCheckException(Exception): + def __init__(self, msg): + try: + ln = sys.exc_info()[-1].tb_lineno + except AttributeError: + ln = inspect.currentframe().f_back.f_lineno + self.message = "{0.__name__} (line {1}): {2}".format(type(self), ln, msg) + self.args = self.message, + + +def handleGUIException(exc_type, exc_value, exc_traceback): + try: + # Try and import wx in case it's missing. + # noinspection PyPackageRequirements + import wx + from gui.errorDialog import ErrorFrame + except: + # noinspection PyShadowingNames + wx = None + # noinspection PyShadowingNames + ErrorFrame = None + + tb = traceback.format_tb(exc_traceback) + + try: + + # Try and output to our log handler + with logging_setup.threadbound(): + module_list = list(set(sys.modules) & set(globals())) + if module_list: + pyfalog.info("Imported Python Modules:") + for imported_module in module_list: + module_details = sys.modules[imported_module] + pyfalog.info("{0}: {1}", imported_module, getattr(module_details, '__version__', '')) + + pyfalog.critical("Exception in main thread: {0}", str(exc_value)) + # Print the base level traceback + traceback.print_tb(exc_traceback) + + if wx and ErrorFrame: + pyfa_gui = wx.App(False) + if exc_type == PreCheckException: + msgbox = wx.MessageBox(str(exc_value), 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) + msgbox.ShowModal() + else: + ErrorFrame(exc_value, tb) + + pyfa_gui.MainLoop() + + pyfalog.info("Exiting.") + except Exception as ex: + # Most likely logging isn't available. Try and output to the console + module_list = list(set(sys.modules) & set(globals())) + if module_list: + pyfalog.info("Imported Python Modules:") + for imported_module in module_list: + module_details = sys.modules[imported_module] + print((str(imported_module) + ": " + str(getattr(module_details, '__version__', '')))) + + print(("Exception in main thread: " + str(exc_value))) + traceback.print_tb(exc_traceback) + + if wx and ErrorFrame: + pyfa_gui = wx.App(False) + if exc_type == PreCheckException: + msgbox = wx.MessageBox(str(exc_value), 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) + msgbox.ShowModal() + else: + ErrorFrame(exc_value, tb) + + pyfa_gui.MainLoop() + + print("Exiting.") + + finally: + # TODO: Add cleanup when exiting here. + pass + # sys.exit() + + +# Replace the uncaught exception handler with our own handler. +sys.excepthook = handleGUIException # Parse command line options usage = "usage: %prog [--root]" @@ -294,10 +293,11 @@ if __name__ == "__main__": with logging_setup.threadbound(): pyfalog.info("Starting Pyfa") - pyfalog.info("Logbook version: {0}", logbook_version) + # pyfalog.info("Logbook version: {0}", logbook_version) - pyfalog.info("Running in logging mode: {0}", logging_mode) - pyfalog.info("Writing log file to: {0}", config.logPath) + # pyfalog.info("Running in logging mode: {0}", logging_mode) + # move this to the log set up - if it fails, can't say that we're writing it + # pyfalog.info("Writing log file to: {0}", config.logPath) # Output all stdout (print) messages as warnings # try: @@ -311,9 +311,9 @@ if __name__ == "__main__": # except: # pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.") - pyfalog.info("OS version: {0}", platform.platform()) - - pyfalog.info("Python version: {0}", sys.version) + # pyfalog.info("OS version: {0}", platform.platform()) + # + # pyfalog.info("Python version: {0}", sys.version) # if sys.version_info < (2, 7) or sys.version_info > (3, 0): # exit_message = "Pyfa requires python 2.x branch ( >= 2.7 )." # raise PreCheckException(exit_message) @@ -344,7 +344,7 @@ if __name__ == "__main__": exit_message = "Cannot import wxPython. You can download wxPython (2.8+) from http://www.wxpython.org/" # raise PreCheckException(exit_message) - pyfalog.info("wxPython version: {0}.", str(wx.VERSION_STRING)) + #pyfalog.info("wxPython version: {0}.", str(wx.VERSION_STRING)) if sqlalchemy is None: exit_message = "\nCannot find sqlalchemy.\nYou can download sqlalchemy (0.6+) from http://www.sqlalchemy.org/" @@ -363,7 +363,8 @@ if __name__ == "__main__": pyfalog.critical("You can download sqlAlchemy (0.5.8+) from http://www.sqlalchemy.org/") pyfalog.critical("Attempting to run with unsupported version of sqlAlchemy.") else: - pyfalog.info("Current version of sqlAlchemy is: {0}", sqlalchemy.__version__) + pass + # pyfalog.info("Current version of sqlAlchemy is: {0}", sqlalchemy.__version__) else: pyfalog.warning("Unknown sqlalchemy version string format, skipping check. Version: {0}", sqlalchemy.__version__) From 7d46d7e22db6a5eeaa5acfb70b1c4a4a1199400b Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 25 Nov 2017 17:31:19 -0500 Subject: [PATCH 089/212] fix crash dealing with CREST fitting window's cache timer --- gui/crestFittings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index bff2c5b9b..ed1b62ce4 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -129,6 +129,7 @@ class CrestFittings(wx.Frame): def OnClose(self, event): self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT) self.mainFrame.Unbind(GE.EVT_SSO_LOGIN) + self.cacheTimer.Stop() # must be manually stopped, otherwise crash. See https://github.com/wxWidgets/Phoenix/issues/632 event.Skip() def getActiveCharacter(self): From 8850da6cdb3af23bf9d18e7fd5bbe06e9cf0822e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 25 Nov 2017 20:11:05 -0500 Subject: [PATCH 090/212] OS X bundling puts it all into one file, which is extracted. We need to make sure the script knows of the actual path --- config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py b/config.py index 082caae55..ed7420dad 100644 --- a/config.py +++ b/config.py @@ -37,14 +37,14 @@ def isFrozen(): return True else: return False - - def __createDirs(path): if not os.path.exists(path): os.makedirs(path) def getPyfaRoot(): + if hasattr(sys, '_MEIPASS'): + return sys._MEIPASS base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else __file__ root = os.path.dirname(os.path.realpath(os.path.abspath(base))) root = root From da67cdba9b0e36d3b2bbdd91a30b68662c587a6e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 25 Nov 2017 20:34:03 -0500 Subject: [PATCH 091/212] Get a mailmap working for properish tracking of authors --- .mailmap | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000..26dd19431 --- /dev/null +++ b/.mailmap @@ -0,0 +1,14 @@ +cncfanatics cncfanatics +blitzmann +blitzmann blitzmann +blitzmann +blitzmann blitzman +blitzmann Ryan Holmes +blitzmann +Corollax Corollax +Corollax Corollax +Mr. Nukealizer Mr. Nukealizer +DarkPhoenix +Sakari Orisi +Will Wykeham Will Wykeham +OISumeko OISumeko \ No newline at end of file From dd3bc668962c4115a10f08258fd69d8906ba7f2b Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 25 Nov 2017 22:40:23 -0500 Subject: [PATCH 092/212] Remove old py<2.6 OrderedDict compat module --- eos/gamedata.py | 5 +- gui/builtinContextMenus/damagePattern.py | 5 +- gui/builtinContextMenus/targetResists.py | 6 +- .../targetingMiscViewFull.py | 6 +- .../targetingMiscViewMinimal.py | 5 +- service/market.py | 7 +- service/port.py | 6 +- utils/compat.py | 270 ------------------ 8 files changed, 7 insertions(+), 303 deletions(-) delete mode 100644 utils/compat.py diff --git a/eos/gamedata.py b/eos/gamedata.py index 3277c4336..af7d413a7 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -24,11 +24,8 @@ from sqlalchemy.orm import reconstructor import eos.db from .eqBase import EqBase from eos.saveddata.price import Price as types_Price +from collections import OrderedDict -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict from logbook import Logger diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index bf725e233..ca65cf9c9 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -8,10 +8,7 @@ from service.fit import Fit from service.damagePattern import DamagePattern as import_DamagePattern from service.settings import ContextMenuSettings -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +from collections import OrderedDict class DamagePattern(ContextMenu): diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index 391608fed..03478b8d2 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -7,11 +7,7 @@ from gui.bitmap_loader import BitmapLoader from service.targetResists import TargetResists as svc_TargetResists from service.fit import Fit from service.settings import ContextMenuSettings - -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +from collections import OrderedDict class TargetResists(ContextMenu): diff --git a/gui/builtinStatsViews/targetingMiscViewFull.py b/gui/builtinStatsViews/targetingMiscViewFull.py index 23c5efd0e..e85336977 100644 --- a/gui/builtinStatsViews/targetingMiscViewFull.py +++ b/gui/builtinStatsViews/targetingMiscViewFull.py @@ -21,11 +21,7 @@ import wx from gui.statsView import StatsView from gui.utils.numberFormatter import formatAmount - -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +from collections import OrderedDict class TargetingMiscViewFull(StatsView): diff --git a/gui/builtinStatsViews/targetingMiscViewMinimal.py b/gui/builtinStatsViews/targetingMiscViewMinimal.py index fbc819d68..e37c4b3a4 100644 --- a/gui/builtinStatsViews/targetingMiscViewMinimal.py +++ b/gui/builtinStatsViews/targetingMiscViewMinimal.py @@ -21,11 +21,8 @@ import wx from gui.statsView import StatsView from gui.utils.numberFormatter import formatAmount +from collections import OrderedDict -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict class TargetingMiscViewMinimal(StatsView): diff --git a/service/market.py b/service/market.py index 73f93c921..7f0efda35 100644 --- a/service/market.py +++ b/service/market.py @@ -33,12 +33,7 @@ from service.settings import SettingsProvider from eos.gamedata import Category as types_Category, Group as types_Group, Item as types_Item, MarketGroup as types_MarketGroup, \ MetaGroup as types_MetaGroup, MetaType as types_MetaType - - -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +from collections import OrderedDict pyfalog = Logger(__name__) diff --git a/service/port.py b/service/port.py index 2a396f854..066ff12e2 100644 --- a/service/port.py +++ b/service/port.py @@ -52,11 +52,7 @@ from abc import ABCMeta, abstractmethod from service.crest import Crest pyfalog = Logger(__name__) - -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +from collections import OrderedDict EFT_SLOT_ORDER = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM] INV_FLAGS = { diff --git a/utils/compat.py b/utils/compat.py deleted file mode 100644 index 7507c7c96..000000000 --- a/utils/compat.py +++ /dev/null @@ -1,270 +0,0 @@ -# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. -# Passes Python2.7's test suite and incorporates all the latest updates. - -try: - from _thread import get_ident as _get_ident -except ImportError: - from _dummy_thread import get_ident as _get_ident - -try: - from _abcoll import KeysView, ValuesView, ItemsView -except ImportError: - KeysView = ValuesView = ItemsView = None - - -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. - # Big-O running times for all methods are the same as for regular dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - """Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - """ - super(OrderedDict, self).__init__(**kwds) - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - """od.__setitem__(i, y) <==> od[i]=y""" - # Setting a new item creates a new link which goes at the end of the linked - # list, and the inherited dictionary is updated with the new key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - """od.__delitem__(y) <==> del od[y]""" - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link_prev, link_next, key = self.__map.pop(key) - link_prev[1] = link_next - link_next[0] = link_prev - - def __iter__(self): - """od.__iter__() <==> iter(od)""" - root = self.__root - curr = root[1] - while curr is not root: - yield curr[2] - curr = curr[1] - - def __reversed__(self): - """od.__reversed__() <==> reversed(od)""" - root = self.__root - curr = root[0] - while curr is not root: - yield curr[2] - curr = curr[0] - - def clear(self): - """od.clear() -> None. Remove all items from od.""" - try: - for node in self.__map.values(): - del node[:] - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - def popitem(self, last=True): - """od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - """ - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root[0] - link_prev = link[0] - link_prev[1] = root - root[0] = link_prev - else: - link = root[1] - link_next = link[1] - root[1] = link_next - link_next[0] = root - key = link[2] - del self.__map[key] - value = dict.pop(self, key) - return key, value - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - """od.keys() -> list of keys in od""" - return list(self) - - def values(self): - """od.values() -> list of values in od""" - return [self[key] for key in self] - - def items(self): - """od.items() -> list of (key, value) pairs in od""" - return [(key, self[key]) for key in self] - - def iterkeys(self): - """od.iterkeys() -> an iterator over the keys in od""" - return iter(self) - - def itervalues(self): - """od.itervalues -> an iterator over the values in od""" - for k in self: - yield self[k] - - def iteritems(self): - """od.iteritems -> an iterator over the (key, value) items in od""" - for k in self: - yield (k, self[k]) - - def update(*args, **kwds): - """od.update(E, **F) -> None. Update od from dict/iterable E and F. - - If E is a dict instance, does: for k in E: od[k] = E[k] - If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] - Or if E is an iterable of items, does: for k, v in E: od[k] = v - In either case, this is followed by: for k, v in F.items(): od[k] = v - - """ - if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) - elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') - self = args[0] - # Make progressively weaker assumptions about "other" - other = () - if len(args) == 2: - other = args[1] - if isinstance(other, dict): - for key in other: - self[key] = other[key] - elif hasattr(other, 'keys'): - for key in list(other.keys()): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in list(kwds.items()): - self[key] = value - - __update = update # let subclasses override update without breaking __init__ - - __marker = object() - - def pop(self, key, default=__marker): - """od.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - - """ - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - """od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od""" - if key in self: - return self[key] - self[key] = default - return default - - def __repr__(self, _repr_running=None): - """od.__repr__() <==> repr(od)""" - if _repr_running is None: - _repr_running = {} - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self.items())) - finally: - del _repr_running[call_key] - - def __reduce__(self): - """Return state information for pickling""" - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return self.__class__, (items,), inst_dict - return self.__class__, (items,) - - def copy(self): - """od.copy() -> a shallow copy of od""" - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - """OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - """ - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - """od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - """ - if isinstance(other, OrderedDict): - return len(self) == len(other) and list(self.items()) == list(other.items()) - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other - - # -- the following methods are only used in Python 2.7 -- - - def viewkeys(self): - """od.viewkeys() -> a set-like object providing a view on od's keys""" - if KeysView: - return KeysView(self) - else: - return None - - def viewvalues(self): - """od.viewvalues() -> an object providing a view on od's values""" - if ValuesView: - return ValuesView(self) - else: - return None - - def viewitems(self): - """od.viewitems() -> a set-like object providing a view on od's items""" - if ItemsView: - return ItemsView(self) - else: - return None From fcdf55632f2369cfdfe9e3bc83090bbbfd154e5c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 26 Nov 2017 01:08:02 -0500 Subject: [PATCH 093/212] working commit to try to clean up pyfa.py and re-implement version checking --- pyfa.py | 102 +++++----------------------------------- service/prereqsCheck.py | 71 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 90 deletions(-) create mode 100644 service/prereqsCheck.py diff --git a/pyfa.py b/pyfa.py index 59e416b8b..9b1c91e22 100755 --- a/pyfa.py +++ b/pyfa.py @@ -18,14 +18,13 @@ # along with pyfa. If not, see . # ============================================================================== -import inspect + import os import platform -import re import sys import traceback from optparse import AmbiguousOptionError, BadOptionError, OptionParser - +from service.prereqsCheck import PreCheckException, version_precheck, version_block from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, StreamHandler, TimedRotatingFileHandler, WARNING, \ __version__ as logbook_version @@ -93,15 +92,6 @@ class PassThroughOptionParser(OptionParser): # self.level(sys.stderr) # # -class PreCheckException(Exception): - def __init__(self, msg): - try: - ln = sys.exc_info()[-1].tb_lineno - except AttributeError: - ln = inspect.currentframe().f_back.f_lineno - self.message = "{0.__name__} (line {1}): {2}".format(type(self), ln, msg) - self.args = self.message, - def handleGUIException(exc_type, exc_value, exc_traceback): try: @@ -158,8 +148,7 @@ def handleGUIException(exc_type, exc_value, exc_traceback): if wx and ErrorFrame: pyfa_gui = wx.App(False) if exc_type == PreCheckException: - msgbox = wx.MessageBox(str(exc_value), 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) - msgbox.ShowModal() + wx.MessageBox(str(exc_value), 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) else: ErrorFrame(exc_value, tb) @@ -180,7 +169,6 @@ sys.excepthook = handleGUIException usage = "usage: %prog [--root]" parser = PassThroughOptionParser(usage=usage) parser.add_option("-r", "--root", action="store_true", dest="rootsavedata", help="if you want pyfa to store its data in root folder, use this option", default=False) -parser.add_option("-w", "--wx28", action="store_true", dest="force28", help="Force usage of wxPython 2.8", default=False) parser.add_option("-d", "--debug", action="store_true", dest="debug", help="Set logger to debug level.", default=False) parser.add_option("-t", "--title", action="store", dest="title", help="Set Window Title", default=None) parser.add_option("-s", "--savepath", action="store", dest="savepath", help="Set the folder for savedata", default=None) @@ -204,6 +192,11 @@ parser.add_option("-l", "--logginglevel", action="store", dest="logginglevel", h if __name__ == "__main__": # Configure paths print ('starting') + + version_precheck() + + import wx + if options.rootsavedata is True: config.saveInRoot = True @@ -212,10 +205,6 @@ if __name__ == "__main__": 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 - if options.savepath is not None: - options.savepath = str(options.savepath) config.defPaths(options.savepath) # Basic logging initialization @@ -293,11 +282,11 @@ if __name__ == "__main__": with logging_setup.threadbound(): pyfalog.info("Starting Pyfa") - # pyfalog.info("Logbook version: {0}", logbook_version) + pyfalog.info("Logbook version: {0}", logbook_version) - # pyfalog.info("Running in logging mode: {0}", logging_mode) + pyfalog.info("Running in logging mode: {0}", logging_mode) # move this to the log set up - if it fails, can't say that we're writing it - # pyfalog.info("Writing log file to: {0}", config.logPath) + pyfalog.info("Writing log file to: {0}", config.logPath) # Output all stdout (print) messages as warnings # try: @@ -311,80 +300,13 @@ if __name__ == "__main__": # except: # pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.") - # pyfalog.info("OS version: {0}", platform.platform()) - # - # pyfalog.info("Python version: {0}", sys.version) - # if sys.version_info < (2, 7) or sys.version_info > (3, 0): - # exit_message = "Pyfa requires python 2.x branch ( >= 2.7 )." - # raise PreCheckException(exit_message) - if hasattr(sys, 'frozen'): pyfalog.info("Running in a frozen state.") else: pyfalog.info("Running in a thawed state.") - # if not hasattr(sys, 'frozen') and wxversion: - # try: - # if options.force28 is True: - # pyfalog.info("Selecting wx version: 2.8. (Forced)") - # wxversion.select('2.8') - # else: - # pyfalog.info("Selecting wx versions: 3.0, 2.8") - # wxversion.select(['3.0', '2.8']) - # except: - # pyfalog.warning("Unable to select wx version. Attempting to import wx without specifying the version.") - # else: - # if not wxversion: - # pyfalog.warning("wxVersion not found. Attempting to import wx without specifying the version.") - - try: - # noinspection PyPackageRequirements - import wx - except: - exit_message = "Cannot import wxPython. You can download wxPython (2.8+) from http://www.wxpython.org/" - # raise PreCheckException(exit_message) - - #pyfalog.info("wxPython version: {0}.", str(wx.VERSION_STRING)) - - if sqlalchemy is None: - exit_message = "\nCannot find sqlalchemy.\nYou can download sqlalchemy (0.6+) from http://www.sqlalchemy.org/" - # raise PreCheckException(exit_message) - else: - saVersion = sqlalchemy.__version__ - saMatch = re.match("([0-9]+).([0-9]+)([b\.])([0-9]+)", saVersion) - config.saVersion = (int(saMatch.group(1)), int(saMatch.group(2)), int(saMatch.group(4))) - if saMatch: - saMajor = int(saMatch.group(1)) - saMinor = int(saMatch.group(2)) - betaFlag = True if saMatch.group(3) == "b" else False - saBuild = int(saMatch.group(4)) if not betaFlag else 0 - if saMajor == 0 and (saMinor < 5 or (saMinor == 5 and saBuild < 8)): - pyfalog.critical("Pyfa requires sqlalchemy 0.5.8 at least but current sqlAlchemy version is {0}", format(sqlalchemy.__version__)) - pyfalog.critical("You can download sqlAlchemy (0.5.8+) from http://www.sqlalchemy.org/") - pyfalog.critical("Attempting to run with unsupported version of sqlAlchemy.") - else: - pass - # pyfalog.info("Current version of sqlAlchemy is: {0}", sqlalchemy.__version__) - else: - pyfalog.warning("Unknown sqlalchemy version string format, skipping check. Version: {0}", sqlalchemy.__version__) - - logVersion = logbook_version.split('.') - # if int(logVersion[0]) == 0 and int(logVersion[1]) < 10: - # raise PreCheckException("Logbook version >= 0.10.0 is required.") - - try: - import requests - config.requestsVersion = requests.__version__ - except ImportError: - pass - # raise PreCheckException("Cannot import requests. You can download requests from https://pypi.python.org/pypi/requests.") - import eos.db - - if config.saVersion[0] > 0 or config.saVersion[1] >= 7: - # <0.7 doesn't have support for events ;_; (mac-deprecated) - config.sa_events = True - import eos.events + import eos.events # todo: move this to eos initialization # noinspection PyUnresolvedReferences import service.prefetch # noqa: F401 diff --git a/service/prereqsCheck.py b/service/prereqsCheck.py new file mode 100644 index 000000000..a5b0896c1 --- /dev/null +++ b/service/prereqsCheck.py @@ -0,0 +1,71 @@ +import sys +import inspect +import re +import platform + +version_block = '' + +class PreCheckException(Exception): + pass + + +def version_precheck(): + global version_block + + version_block += "\nOS version: {}".format(platform.platform()) + version_block += "\nPython version: {}".format(sys.version) + + if sys.version_info < (3, 6): + msg = "pyfa requires python 3.6" + raise PreCheckException(msg) + + try: + # the way that the version string is imported in wx is odd, causing us to have to split out the imports like this. :( + from wx.__version__ import VERSION, VERSION_STRING + + if VERSION[0] < 4: + raise Exception() + if VERSION[3] != '': + if VERSION[3][0] == 'b' and int(VERSION[3][-1]) < 2: + raise Exception() + + import wx + version_block += "\nwxPython version: {} (wxWidgets {})".format(VERSION_STRING, wx.wxWidgets_version) + except: + msg = "pyfa requires wxPython v4.0.0b2+. You can download wxPython from https://wxpython.org/pages/downloads/" + raise PreCheckException(msg) + + try: + import sqlalchemy + saMatch = re.match("([0-9]+).([0-9]+).([0-9]+)(([b\.])([0-9]+))?", sqlalchemy.__version__) + version_block += "\nSQLAlchemy version: {}".format(sqlalchemy.__version__) + + if (int(saMatch.group(1)), int(saMatch.group(2)), int(saMatch.group(3))) < (3, 0, 5): + raise Exception() + except: + msg = "pyfa requires SQLAlchemy v1.0.5+. You can download SQLAlchemy from https://www.sqlalchemy.org/download.html" + raise PreCheckException(msg) + + try: + import logbook + logVersion = logbook.__version__.split('.') + version_block += "\nLogbook version: {}".format(logbook.__version__) + + if int(logVersion[0]) < 1: + raise Exception() + except: + raise PreCheckException("pyfa requires Logbook version 1.0.0+. You can download Logbook from https://pypi.python.org/pypi/Logbook") + + try: + import requests + version_block += "\nRequests version: {}".format(requests.__version__) + except: + msg = "pyfa requires the requests module. You can download requests from https://pypi.python.org/pypi/requests" + raise PreCheckException(msg) + + try: + import dateutil + version_block += "\nDateutil version: {}".format(dateutil.__version__) + except: + msg = "pyfa requires the python-dateutil module. You can download python-dateutil form https://pypi.python.org/pypi/python-dateutil" + raise PreCheckException(msg) \ No newline at end of file From 1da127c89812f94e88cb44c8f37df1ddc026a5b5 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 26 Nov 2017 01:54:36 -0500 Subject: [PATCH 094/212] Working commit to better handle errors --- gui/errorDialog.py | 53 +++++++++++++++++++++++++---------------- pyfa.py | 11 +++++---- service/prereqsCheck.py | 15 +++++++++++- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/gui/errorDialog.py b/gui/errorDialog.py index 627e719f9..b1d972b2b 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -17,31 +17,42 @@ # along with pyfa. If not, see . # =============================================================================== -import platform -import sys +# import platform +# import sys +# +# # noinspection PyPackageRequirements +# import wx +# +# try: +# import config +# except: +# config = None +# +# try: +# import sqlalchemy +# +# sqlalchemy_version = sqlalchemy.__version__ +# except: +# sqlalchemy_version = "Unknown" +# +# try: +# from logbook import __version__ as logbook_version +# except: +# logbook_version = "Unknown" +# +# import wx.lib.agw.hyperlink -# noinspection PyPackageRequirements -import wx -try: - import config -except: - config = None +class ErrorFrameHandler(object): + __app = None -try: - import sqlalchemy - - sqlalchemy_version = sqlalchemy.__version__ -except: - sqlalchemy_version = "Unknown" - -try: - from logbook import __version__ as logbook_version -except: - logbook_version = "Unknown" - -import wx.lib.agw.hyperlink + @classmethod + def HandleException(cls, exc_type, exc_value, exc_traceback): + print("Handle excpetion! {}".format(cls.__app)) + @classmethod + def SetApp(cls, wxApp): + cls.__app = wxApp class ErrorFrame(wx.Frame): diff --git a/pyfa.py b/pyfa.py index 9b1c91e22..9d6f60afa 100755 --- a/pyfa.py +++ b/pyfa.py @@ -20,11 +20,10 @@ import os -import platform import sys import traceback from optparse import AmbiguousOptionError, BadOptionError, OptionParser -from service.prereqsCheck import PreCheckException, version_precheck, version_block +from service.prereqsCheck import PreCheckException, PreCheckMessage, version_precheck, version_block from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, StreamHandler, TimedRotatingFileHandler, WARNING, \ __version__ as logbook_version @@ -161,9 +160,10 @@ def handleGUIException(exc_type, exc_value, exc_traceback): pass # sys.exit() +from gui.errorDialog import ErrorFrameHandler # Replace the uncaught exception handler with our own handler. -sys.excepthook = handleGUIException +sys.excepthook = ErrorFrameHandler.HandleException # Parse command line options usage = "usage: %prog [--root]" @@ -193,7 +193,10 @@ if __name__ == "__main__": # Configure paths print ('starting') - version_precheck() + try: + version_precheck() + except PreCheckException as ex: + PreCheckMessage(str(ex)) import wx diff --git a/service/prereqsCheck.py b/service/prereqsCheck.py index a5b0896c1..4b781e5f9 100644 --- a/service/prereqsCheck.py +++ b/service/prereqsCheck.py @@ -8,6 +8,19 @@ version_block = '' class PreCheckException(Exception): pass +class PreCheckMessage(): + def __init__(self, msg): + # wx may not be installed, in which case print to console. For all other prechecks, should pop up a MessageDialog + try: + import wx + app = wx.App(False) + wx.MessageBox(msg, 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) + app.MainLoop() + except: + pass + finally: + print(msg) + sys.exit() def version_precheck(): global version_block @@ -40,7 +53,7 @@ def version_precheck(): saMatch = re.match("([0-9]+).([0-9]+).([0-9]+)(([b\.])([0-9]+))?", sqlalchemy.__version__) version_block += "\nSQLAlchemy version: {}".format(sqlalchemy.__version__) - if (int(saMatch.group(1)), int(saMatch.group(2)), int(saMatch.group(3))) < (3, 0, 5): + if (int(saMatch.group(1)), int(saMatch.group(2)), int(saMatch.group(3))) < (1, 0, 5): raise Exception() except: msg = "pyfa requires SQLAlchemy v1.0.5+. You can download SQLAlchemy from https://www.sqlalchemy.org/download.html" From c000b19986663b70df1892595ec3b93e2dd1138f Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 26 Nov 2017 03:53:38 -0500 Subject: [PATCH 095/212] Finish pulling all the cruft out of pyfa.py relating to logging and error handling. All that is now done in separate areas. Also finally did some major reworking on the error dialog. Now it doesn't spawn a new wxApp and wxFrame for each and every error - attaches to MainFrame and sticks around, having exceptions append to it rather than spawn a new one. In the case that an error happens before MainFrame is available, it spins up a new wxApp. Yay cleanup! --- config.py | 114 +++++++++++++++++++- gui/errorDialog.py | 121 ++++++++------------- pyfa.py | 234 ++++------------------------------------ service/prereqsCheck.py | 3 +- 4 files changed, 179 insertions(+), 293 deletions(-) diff --git a/config.py b/config.py index ed7420dad..d0fd19084 100644 --- a/config.py +++ b/config.py @@ -1,7 +1,8 @@ import os import sys -from logbook import Logger +from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, \ + StreamHandler, TimedRotatingFileHandler, WARNING pyfalog = Logger(__name__) @@ -30,6 +31,16 @@ savePath = None saveDB = None gameDB = None logPath = None +loggingLevel = None +logging_setup = None + +LOGLEVEL_MAP = { + "critical": CRITICAL, + "error": ERROR, + "warning": WARNING, + "info": INFO, + "debug": DEBUG, +} def isFrozen(): @@ -68,6 +79,7 @@ def defPaths(customSavePath=None): global saveDB global gameDB global saveInRoot + global logPath pyfalog.debug("Configuring Pyfa") @@ -106,6 +118,13 @@ def defPaths(customSavePath=None): if not gameDB: gameDB = os.path.join(pyfaPath, "eve.db") + if debug: + logFile = "pyfa_debug.log" + else: + logFile = "pyfa.log" + + logPath = os.path.join(savePath, logFile) + # DON'T MODIFY ANYTHING BELOW import eos.config @@ -121,3 +140,96 @@ def defPaths(customSavePath=None): # initialize the settings from service.settings import EOSSettings eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever + +def defLogging(): + global debug + global logPath + global loggingLevel + global logging_setup + + try: + if debug: + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + StreamHandler( + sys.stdout, + bubble=False, + level=loggingLevel + ), + TimedRotatingFileHandler( + logPath, + level=0, + backup_count=3, + bubble=True, + date_format='%Y-%m-%d', + ), + ]) + else: + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + FingersCrossedHandler( + TimedRotatingFileHandler( + logPath, + level=0, + backup_count=3, + bubble=False, + date_format='%Y-%m-%d', + ), + action_level=ERROR, + buffer_size=1000, + # pull_information=True, + # reset=False, + ) + ]) + except: + print("Critical error attempting to setup logging. Falling back to console only.") + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + StreamHandler( + sys.stdout, + bubble=False + ) + ]) + + with logging_setup.threadbound(): + + # Output all stdout (print) messages as warnings + try: + sys.stdout = LoggerWriter(pyfalog.warning) + except: + pyfalog.critical("Cannot redirect. Continuing without writing stdout to log.") + + # Output all stderr (stacktrace) messages as critical + try: + sys.stderr = LoggerWriter(pyfalog.critical) + except: + pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.") + + +class LoggerWriter(object): + def __init__(self, level): + # self.level is really like using log.debug(message) + # at least in my case + self.level = level + + def write(self, message): + # if statement reduces the amount of newlines that are + # printed to the logger + if message.strip() != '': + self.level(message.replace("\n", "")) + + def flush(self): + # create a flush method so things can be flushed when + # the system wants to. Not sure if simply 'printing' + # sys.stderr is the correct way to do it, but it seemed + # to work properly for me. + self.level(sys.stderr) + + + diff --git a/gui/errorDialog.py b/gui/errorDialog.py index b1d972b2b..7188efb59 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -18,48 +18,50 @@ # =============================================================================== # import platform -# import sys +import sys # -# # noinspection PyPackageRequirements -# import wx -# -# try: -# import config -# except: -# config = None -# -# try: -# import sqlalchemy -# -# sqlalchemy_version = sqlalchemy.__version__ -# except: -# sqlalchemy_version = "Unknown" -# -# try: -# from logbook import __version__ as logbook_version -# except: -# logbook_version = "Unknown" -# -# import wx.lib.agw.hyperlink +# noinspection PyPackageRequirements +import wx +import traceback +import config +from logbook import Logger +from service.prereqsCheck import version_block + +pyfalog = Logger(__name__) -class ErrorFrameHandler(object): - __app = None +class ErrorHandler(object): + __parent = None + __frame = None @classmethod def HandleException(cls, exc_type, exc_value, exc_traceback): - print("Handle excpetion! {}".format(cls.__app)) + with config.logging_setup.threadbound(): + # Print the base level traceback + t = traceback.format_exception(exc_type, exc_value, exc_traceback) + pyfalog.critical("\n\n"+"".join(t)) + + if cls.__parent is None: + app = wx.App(False) + ErrorFrame(None) + app.MainLoop() + sys.exit() + else: + if not cls.__frame: + cls.__frame = ErrorFrame(cls.__parent) + cls.__frame.Show() + cls.__frame.addException("".join(t)) @classmethod - def SetApp(cls, wxApp): - cls.__app = wxApp + def SetParent(cls, parent): + cls.__parent = parent class ErrorFrame(wx.Frame): - def __init__(self, exception=None, tb=None, error_title='Error!'): + def __init__(self, parent=None, error_title='Error!'): v = sys.version_info - wx.Frame.__init__(self, None, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600), + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.STAY_ON_TOP) desc = "pyfa has experienced an unexpected issue. Below is a message that contains crucial\n" \ @@ -96,59 +98,24 @@ class ErrorFrame(wx.Frame): # mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) - errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP) - errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) - mainSizer.Add(errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5) - - # try: - # errorTextCtrl.AppendText("OS version: \t" + str(platform.platform())) - # except: - # errorTextCtrl.AppendText("OS version: Unknown") - # errorTextCtrl.AppendText("\n") - # - # try: - # errorTextCtrl.AppendText("Python: \t" + '{}.{}.{}'.format(v.major, v.minor, v.micro)) - # except: - # errorTextCtrl.AppendText("Python: Unknown") - # errorTextCtrl.AppendText("\n") - # - # try: - # errorTextCtrl.AppendText("wxPython: \t" + wx.VERSION_STRING) - # except: - # errorTextCtrl.AppendText("wxPython: Unknown") - # errorTextCtrl.AppendText("\n") - # - # errorTextCtrl.AppendText("SQLAlchemy: \t" + str(sqlalchemy_version)) - # errorTextCtrl.AppendText("\n") - # - # errorTextCtrl.AppendText("Logbook: \t" + str(logbook_version)) - # errorTextCtrl.AppendText("\n") - # - # try: - # errorTextCtrl.AppendText("pyfa version: {0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion)) - # except: - # errorTextCtrl.AppendText("pyfa version: Unknown") - # errorTextCtrl.AppendText('\n') - # - # errorTextCtrl.AppendText("pyfa root: " + str(config.pyfaPath or "Unknown")) - # errorTextCtrl.AppendText('\n') - # errorTextCtrl.AppendText("save path: " + str(config.savePath or "Unknown")) - # errorTextCtrl.AppendText('\n') - # errorTextCtrl.AppendText("fs encoding: " + str(sys.getfilesystemencoding() or "Unknown")) - # errorTextCtrl.AppendText('\n\n') - - errorTextCtrl.AppendText("EXCEPTION: " + str(exception or "Unknown")) - errorTextCtrl.AppendText('\n\n') - - if tb: - for line in tb: - errorTextCtrl.AppendText(line) - errorTextCtrl.Layout() + self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version_block.strip(), wx.DefaultPosition, (-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP) + self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) + mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5) + self.errorTextCtrl.AppendText("\n") + self.errorTextCtrl.Layout() self.SetSizer(mainSizer) mainSizer.Layout() self.Layout() self.Centre(wx.BOTH) + self.Bind(wx.EVT_CLOSE, self.OnClose) self.Show() + + + def OnClose(self, evt): + self.Hide() + + def addException(self, text): + self.errorTextCtrl.AppendText("\n{}\n\n{}".format("#" * 20, text)) \ No newline at end of file diff --git a/pyfa.py b/pyfa.py index 9d6f60afa..4c3509683 100755 --- a/pyfa.py +++ b/pyfa.py @@ -24,19 +24,8 @@ import sys import traceback from optparse import AmbiguousOptionError, BadOptionError, OptionParser from service.prereqsCheck import PreCheckException, PreCheckMessage, version_precheck, version_block -from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, StreamHandler, TimedRotatingFileHandler, WARNING, \ - __version__ as logbook_version - import config -try: - import sqlalchemy -except ImportError: - sqlalchemy = None - -pyfalog = Logger(__name__) - - ascii_text = ''' ++++++++++++++++++++++++++++++++++++++++++++++++++ __ @@ -67,104 +56,9 @@ class PassThroughOptionParser(OptionParser): try: OptionParser._process_args(self, largs, rargs, values) except (BadOptionError, AmbiguousOptionError) as e: - pyfalog.error("Bad startup option passed.") + # pyfalog.error("Bad startup option passed.") largs.append(e.opt_str) -# -# class LoggerWriter(object): -# def __init__(self, level): -# # self.level is really like using log.debug(message) -# # at least in my case -# self.level = level -# -# def write(self, message): -# # if statement reduces the amount of newlines that are -# # printed to the logger -# if message not in {'\n', ' '}: -# self.level(message.replace("\n", "")) -# -# def flush(self): -# # create a flush method so things can be flushed when -# # the system wants to. Not sure if simply 'printing' -# # sys.stderr is the correct way to do it, but it seemed -# # to work properly for me. -# self.level(sys.stderr) -# -# - -def handleGUIException(exc_type, exc_value, exc_traceback): - try: - # Try and import wx in case it's missing. - # noinspection PyPackageRequirements - import wx - from gui.errorDialog import ErrorFrame - except: - # noinspection PyShadowingNames - wx = None - # noinspection PyShadowingNames - ErrorFrame = None - - tb = traceback.format_tb(exc_traceback) - - try: - - # Try and output to our log handler - with logging_setup.threadbound(): - module_list = list(set(sys.modules) & set(globals())) - if module_list: - pyfalog.info("Imported Python Modules:") - for imported_module in module_list: - module_details = sys.modules[imported_module] - pyfalog.info("{0}: {1}", imported_module, getattr(module_details, '__version__', '')) - - pyfalog.critical("Exception in main thread: {0}", str(exc_value)) - # Print the base level traceback - traceback.print_tb(exc_traceback) - - if wx and ErrorFrame: - pyfa_gui = wx.App(False) - if exc_type == PreCheckException: - msgbox = wx.MessageBox(str(exc_value), 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) - msgbox.ShowModal() - else: - ErrorFrame(exc_value, tb) - - pyfa_gui.MainLoop() - - pyfalog.info("Exiting.") - except Exception as ex: - # Most likely logging isn't available. Try and output to the console - module_list = list(set(sys.modules) & set(globals())) - if module_list: - pyfalog.info("Imported Python Modules:") - for imported_module in module_list: - module_details = sys.modules[imported_module] - print((str(imported_module) + ": " + str(getattr(module_details, '__version__', '')))) - - print(("Exception in main thread: " + str(exc_value))) - traceback.print_tb(exc_traceback) - - if wx and ErrorFrame: - pyfa_gui = wx.App(False) - if exc_type == PreCheckException: - wx.MessageBox(str(exc_value), 'Error', wx.ICON_ERROR | wx.STAY_ON_TOP) - else: - ErrorFrame(exc_value, tb) - - pyfa_gui.MainLoop() - - print("Exiting.") - - finally: - # TODO: Add cleanup when exiting here. - pass - # sys.exit() - -from gui.errorDialog import ErrorFrameHandler - -# Replace the uncaught exception handler with our own handler. -sys.excepthook = ErrorFrameHandler.HandleException - # Parse command line options usage = "usage: %prog [--root]" parser = PassThroughOptionParser(usage=usage) @@ -175,31 +69,27 @@ parser.add_option("-s", "--savepath", action="store", dest="savepath", help="Set parser.add_option("-l", "--logginglevel", action="store", dest="logginglevel", help="Set desired logging level [Critical|Error|Warning|Info|Debug]", default="Error") (options, args) = parser.parse_args() -# -# if options.logginglevel == "Critical": -# options.logginglevel = CRITICAL -# elif options.logginglevel == "Error": -# options.logginglevel = ERROR -# elif options.logginglevel == "Warning": -# options.logginglevel = WARNING -# elif options.logginglevel == "Info": -# options.logginglevel = INFO -# elif options.logginglevel == "Debug": -# options.logginglevel = DEBUG -# else: -# options.logginglevel = ERROR if __name__ == "__main__": - # Configure paths - print ('starting') - try: + # first and foremost - check required libraries version_precheck() except PreCheckException as ex: + # do not pass GO, go directly to jail (and then die =/) PreCheckMessage(str(ex)) + sys.exit() + # from here, we can assume we have the libraries that we need, including wx import wx + from logbook import Logger + pyfalog = Logger(__name__) + + from gui.errorDialog import ErrorHandler + + # Replace the uncaught exception handler with our own handler. + sys.excepthook = ErrorHandler.HandleException + if options.rootsavedata is True: config.saveInRoot = True @@ -208,108 +98,25 @@ if __name__ == "__main__": options.title = "pyfa %s%s - Python Fitting Assistant" % (config.version, "" if config.tag.lower() != 'git' else " (git)") config.debug = options.debug + config.loggingLevel = config.LOGLEVEL_MAP.get(options.logginglevel.lower(), config.LOGLEVEL_MAP['error']) config.defPaths(options.savepath) + config.defLogging() - # Basic logging initialization + with config.logging_setup.threadbound(): - # Logging levels: - ''' - logbook.CRITICAL - logbook.ERROR - logbook.WARNING - logbook.INFO - logbook.DEBUG - logbook.NOTSET - ''' - - if options.debug: - savePath_filename = "Pyfa_debug.log" - else: - savePath_filename = "Pyfa.log" - - config.logPath = os.path.join(config.savePath, savePath_filename) - - try: - if options.debug: - logging_mode = "Debug" - logging_setup = NestedSetup([ - # make sure we never bubble up to the stderr handler - # if we run out of setup handling - NullHandler(), - StreamHandler( - sys.stdout, - bubble=False, - level=options.logginglevel - ), - TimedRotatingFileHandler( - config.logPath, - level=0, - backup_count=3, - bubble=True, - date_format='%Y-%m-%d', - ), - ]) - else: - logging_mode = "User" - logging_setup = NestedSetup([ - # make sure we never bubble up to the stderr handler - # if we run out of setup handling - NullHandler(), - FingersCrossedHandler( - TimedRotatingFileHandler( - config.logPath, - level=0, - backup_count=3, - bubble=False, - date_format='%Y-%m-%d', - ), - action_level=ERROR, - buffer_size=1000, - # pull_information=True, - # reset=False, - ) - ]) - except: - print("Critical error attempting to setup logging. Falling back to console only.") - logging_mode = "Console Only" - logging_setup = NestedSetup([ - # make sure we never bubble up to the stderr handler - # if we run out of setup handling - NullHandler(), - StreamHandler( - sys.stdout, - bubble=False - ) - ]) - - with logging_setup.threadbound(): pyfalog.info("Starting Pyfa") + pyfalog.info(version_block) - pyfalog.info("Logbook version: {0}", logbook_version) - - pyfalog.info("Running in logging mode: {0}", logging_mode) - # move this to the log set up - if it fails, can't say that we're writing it pyfalog.info("Writing log file to: {0}", config.logPath) - # Output all stdout (print) messages as warnings - # try: - # sys.stdout = LoggerWriter(pyfalog.warning) - # except: - # pyfalog.critical("Cannot redirect. Continuing without writing stdout to log.") - - # Output all stderr (stacktrace) messages as critical - # try: - # sys.stderr = LoggerWriter(pyfalog.critical) - # except: - # pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.") - if hasattr(sys, 'frozen'): pyfalog.info("Running in a frozen state.") else: pyfalog.info("Running in a thawed state.") + # Lets get to the good stuff, shall we? import eos.db - import eos.events # todo: move this to eos initialization + import eos.events # todo: move this to eos initialization? # noinspection PyUnresolvedReferences import service.prefetch # noqa: F401 @@ -323,7 +130,8 @@ if __name__ == "__main__": from gui.mainFrame import MainFrame pyfa = wx.App(False) - MainFrame(options.title) + mf = MainFrame(options.title) + ErrorHandler.SetParent(mf) pyfa.MainLoop() # TODO: Add some thread cleanup code here. Right now we bail, and that can lead to orphaned threads or threads not properly exiting. diff --git a/service/prereqsCheck.py b/service/prereqsCheck.py index 4b781e5f9..ce8b95083 100644 --- a/service/prereqsCheck.py +++ b/service/prereqsCheck.py @@ -20,7 +20,6 @@ class PreCheckMessage(): pass finally: print(msg) - sys.exit() def version_precheck(): global version_block @@ -43,7 +42,7 @@ def version_precheck(): raise Exception() import wx - version_block += "\nwxPython version: {} (wxWidgets {})".format(VERSION_STRING, wx.wxWidgets_version) + version_block += "\nwxPython version: {} ({})".format(VERSION_STRING, wx.wxWidgets_version) except: msg = "pyfa requires wxPython v4.0.0b2+. You can download wxPython from https://wxpython.org/pages/downloads/" raise PreCheckException(msg) From ac7908c62ccdbc09b754b4b74dea2b24207c85d8 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 26 Nov 2017 18:15:33 -0500 Subject: [PATCH 096/212] rearrange spec files --- .../linux/pyfa-linux.spec | 0 pyfa-mac.spec => dist_assets/mac/pyfa.spec | 2 +- .../hook-matplotlib.backends.py | 78 +++++++++++++++++++ dist_assets/win/pyfa.spec | 72 +++++++++-------- pyfa-win.spec | 78 ------------------- 5 files changed, 114 insertions(+), 116 deletions(-) rename pyfa-linux.spec => dist_assets/linux/pyfa-linux.spec (100%) rename pyfa-mac.spec => dist_assets/mac/pyfa.spec (97%) create mode 100644 dist_assets/pyinstaller_hooks/hook-matplotlib.backends.py delete mode 100644 pyfa-win.spec diff --git a/pyfa-linux.spec b/dist_assets/linux/pyfa-linux.spec similarity index 100% rename from pyfa-linux.spec rename to dist_assets/linux/pyfa-linux.spec diff --git a/pyfa-mac.spec b/dist_assets/mac/pyfa.spec similarity index 97% rename from pyfa-mac.spec rename to dist_assets/mac/pyfa.spec index d10728185..f0accf264 100644 --- a/pyfa-mac.spec +++ b/dist_assets/mac/pyfa.spec @@ -43,7 +43,7 @@ a = Analysis([r'pyfa.py'], binaries=[], datas=added_files, hiddenimports=import_these, - hookspath=[], + hookspath=['dist_assets/pyinstaller_hooks'], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, diff --git a/dist_assets/pyinstaller_hooks/hook-matplotlib.backends.py b/dist_assets/pyinstaller_hooks/hook-matplotlib.backends.py new file mode 100644 index 000000000..d73ba1ec3 --- /dev/null +++ b/dist_assets/pyinstaller_hooks/hook-matplotlib.backends.py @@ -0,0 +1,78 @@ +# This apes hook-matplotlib.backends.py, but REMOVES backends, all but +# the ones in the list below. +# Courtesy of https://github.com/bpteague/cytoflow/blob/70f9291/packaging/hook-matplotlib.backends.py + +KEEP = ["WXAgg", "WX", "agg"] + +from PyInstaller.compat import is_darwin +from PyInstaller.utils.hooks import ( + eval_statement, exec_statement, logger) + + +def get_matplotlib_backend_module_names(): + """ + List the names of all matplotlib backend modules importable under the + current Python installation. + Returns + ---------- + list + List of the fully-qualified names of all such modules. + """ + # Statement safely importing a single backend module. + import_statement = """ +import os, sys +# Preserve stdout. +sys_stdout = sys.stdout +try: + # Redirect output printed by this importation to "/dev/null", preventing + # such output from being erroneously interpreted as an error. + with open(os.devnull, 'w') as dev_null: + sys.stdout = dev_null + __import__('%s') +# If this is an ImportError, print this exception's message without a traceback. +# ImportError messages are human-readable and require no additional context. +except ImportError as exc: + sys.stdout = sys_stdout + print(exc) +# Else, print this exception preceded by a traceback. traceback.print_exc() +# prints to stderr rather than stdout and must not be called here! +except Exception: + sys.stdout = sys_stdout + import traceback + print(traceback.format_exc()) +""" + + # List of the human-readable names of all available backends. + backend_names = eval_statement( + 'import matplotlib; print(matplotlib.rcsetup.all_backends)') + + # List of the fully-qualified names of all importable backend modules. + module_names = [] + + # If the current system is not OS X and the "CocoaAgg" backend is available, + # remove this backend from consideration. Attempting to import this backend + # on non-OS X systems halts the current subprocess without printing output + # or raising exceptions, preventing its reliable detection. + if not is_darwin and 'CocoaAgg' in backend_names: + backend_names.remove('CocoaAgg') + + # For safety, attempt to import each backend in a unique subprocess. + for backend_name in backend_names: + if backend_name in KEEP: + continue + + module_name = 'matplotlib.backends.backend_%s' % backend_name.lower() + stdout = exec_statement(import_statement % module_name) + + # If no output was printed, this backend is importable. + if not stdout: + module_names.append(module_name) + logger.info(' Matplotlib backend "%s": removed' % backend_name) + + return module_names + +# Freeze all importable backends, as PyInstaller is unable to determine exactly +# which backends are required by the current program. +e=get_matplotlib_backend_module_names() +print(e) +excludedimports = e \ No newline at end of file diff --git a/dist_assets/win/pyfa.spec b/dist_assets/win/pyfa.spec index 0f61a860d..a5e71adca 100644 --- a/dist_assets/win/pyfa.spec +++ b/dist_assets/win/pyfa.spec @@ -1,35 +1,36 @@ # -*- mode: python -*- -# Note: This script is provided AS-IS for those that may be interested. -# pyfa does not currently support pyInstaller (or any other build process) 100% at the moment - -# Command line to build: -# (Run from directory where pyfa.py and pyfa.spec lives.) -# c:\Python27\scripts\pyinstaller.exe --clean --noconfirm --windowed --upx-dir=.\scripts\upx.exe pyfa.spec - -# Don't forget to change the path to where your pyfa.py and pyfa.spec lives -# pathex=['C:\\Users\\Ebag333\\Documents\\GitHub\\Ebag333\\Pyfa'], - import os +from itertools import chain +import subprocess +import requests.certs + +label = subprocess.check_output([ + "git", "describe", "--tags"]).strip() + +with open('gitversion', 'w+') as f: + f.write(label.decode()) block_cipher = None added_files = [ - ( 'imgs/gui/*.png', 'imgs/gui' ), - ( 'imgs/gui/*.gif', 'imgs/gui' ), - ( 'imgs/icons/*.png', 'imgs/icons' ), - ( 'imgs/renders/*.png', 'imgs/renders' ), - ( 'dist_assets/win/pyfa.ico', '.' ), - ( 'dist_assets/cacert.pem', '.' ), - ( 'eve.db', '.' ), - ( 'README.md', '.' ), - ( 'LICENSE', '.' ), + ('../../imgs/gui/*.png', 'imgs/gui'), + ('../../imgs/gui/*.gif', 'imgs/gui'), + ('../../imgs/icons/*.png', 'imgs/icons'), + ('../../imgs/renders/*.png', 'imgs/renders'), + ('../../dist_assets/win/pyfa.ico', '.'), + (requests.certs.where(), '.'), # is this needed anymore? + ('../../eve.db', '.'), + ('../../README.md', '.'), + ('../../LICENSE', '.'), + ('../../gitversion', '.'), ] import_these = [] -# Walk eos.effects and add all effects so we can import them properly -for root, folders, files in os.walk("eos/effects"): +# Walk directories that do dynamic importing +paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') +for root, folders, files in chain.from_iterable(os.walk(path) for path in paths): for file_ in files: if file_.endswith(".py") and not file_.startswith("_"): mod_name = "{}.{}".format( @@ -38,36 +39,34 @@ for root, folders, files in os.walk("eos/effects"): ) import_these.append(mod_name) -a = Analysis( - ['pyfa.py'], - pathex=['C:\\projects\\pyfa\\'], +a = Analysis(['../../pyfa.py'], + pathex=[ + # Need this, see https://github.com/pyinstaller/pyinstaller/issues/1566 + # To get this, download and install windows 10 SDK + # If not building on Windows 10, this might be optional + r'C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86'], binaries=[], datas=added_files, hiddenimports=import_these, - hookspath=[], + hookspath=['dist_assets/pyinstaller_hooks'], runtime_hooks=[], - excludes=[], + excludes=['Tkinter'], win_no_prefer_redirects=False, win_private_assemblies=False, - cipher=block_cipher, - ) + cipher=block_cipher) -pyz = PYZ( - a.pure, - a.zipped_data, - cipher=block_cipher, - ) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) exe = EXE(pyz, a.scripts, exclude_binaries=True, debug=False, - console=False, + console=True, strip=False, upx=True, name='pyfa', icon='dist_assets/win/pyfa.ico', - onefile=False, ) coll = COLLECT( @@ -77,7 +76,6 @@ coll = COLLECT( a.datas, strip=False, upx=True, - onefile=False, name='pyfa', icon='dist_assets/win/pyfa.ico', - ) + ) \ No newline at end of file diff --git a/pyfa-win.spec b/pyfa-win.spec deleted file mode 100644 index 7671e380d..000000000 --- a/pyfa-win.spec +++ /dev/null @@ -1,78 +0,0 @@ -# -*- mode: python -*- - -import os -from itertools import chain -import subprocess - -label = subprocess.check_output([ - "git", "describe", "--tags"]).strip() - -with open('gitversion', 'w+') as f: - f.write(label.decode()) - -block_cipher = None - -added_files = [ - ( 'imgs/gui/*.png', 'imgs/gui' ), - ( 'imgs/gui/*.gif', 'imgs/gui' ), - ( 'imgs/icons/*.png', 'imgs/icons' ), - ( 'imgs/renders/*.png', 'imgs/renders' ), - ( 'dist_assets/win/pyfa.ico', '.' ), - ( 'dist_assets/cacert.pem', '.' ), - ( 'eve.db', '.' ), - ( 'README.md', '.' ), - ( 'LICENSE', '.' ), - ( 'gitversion', '.' ), - ] - -import_these = [] - -# Walk directories that do dynamic importing -paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') -for root, folders, files in chain.from_iterable(os.walk(path) for path in paths): - for file_ in files: - if file_.endswith(".py") and not file_.startswith("_"): - mod_name = "{}.{}".format( - root.replace("/", "."), - file_.split(".py")[0], - ) - import_these.append(mod_name) - -a = Analysis([r'C:\Users\Ryan\Sync\Git\blitzmann\Pyfa\pyfa.py'], - pathex=[ - # Need this, see https://github.com/pyinstaller/pyinstaller/issues/1566 - # To get this, download and install windows 10 SDK - # If not building on Windows 10, this might be optional - r'C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86'], - binaries=[], - datas=added_files, - hiddenimports=import_these, - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher) -pyz = PYZ(a.pure, a.zipped_data, - cipher=block_cipher) -exe = EXE(pyz, - a.scripts, - exclude_binaries=True, - debug=False, - console=True, - strip=False, - upx=True, - name='pyfa', - icon='dist_assets/win/pyfa.ico', - ) - -coll = COLLECT( - exe, - a.binaries, - a.zipfiles, - a.datas, - strip=False, - upx=True, - name='pyfa', - icon='dist_assets/win/pyfa.ico', - ) \ No newline at end of file From 6f19d45c6dd647594e63fc95d79ef039948c7db3 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 26 Nov 2017 18:29:03 -0500 Subject: [PATCH 097/212] fix mac spec --- dist_assets/mac/pyfa.spec | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/dist_assets/mac/pyfa.spec b/dist_assets/mac/pyfa.spec index f0accf264..3f674346d 100644 --- a/dist_assets/mac/pyfa.spec +++ b/dist_assets/mac/pyfa.spec @@ -3,6 +3,7 @@ import os from itertools import chain import subprocess +import requests.certs label = subprocess.check_output([ "git", "describe", "--tags"]).strip() @@ -13,18 +14,19 @@ with open('gitversion', 'w+') as f: block_cipher = None added_files = [ - ( 'imgs/gui/*.png', 'imgs/gui' ), - ( 'imgs/gui/*.gif', 'imgs/gui' ), - ( 'imgs/icons/*.png', 'imgs/icons' ), - ( 'imgs/renders/*.png', 'imgs/renders' ), - ( 'dist_assets/win/pyfa.ico', '.' ), - ( 'dist_assets/cacert.pem', '.' ), - ( 'eve.db', '.' ), - ( 'README.md', '.' ), - ( 'LICENSE', '.' ), - ( 'gitversion', '.' ), + ('../../imgs/gui/*.png', 'imgs/gui'), + ('../../imgs/gui/*.gif', 'imgs/gui'), + ('../../imgs/icons/*.png', 'imgs/icons'), + ('../../imgs/renders/*.png', 'imgs/renders'), + ('../../dist_assets/win/pyfa.ico', '.'), + (requests.certs.where(), '.'), # is this needed anymore? + ('../../eve.db', '.'), + ('../../README.md', '.'), + ('../../LICENSE', '.'), + ('../../gitversion', '.'), ] + import_these = [] # Walk directories that do dynamic importing @@ -38,7 +40,7 @@ for root, folders, files in chain.from_iterable(os.walk(path) for path in paths) ) import_these.append(mod_name) -a = Analysis([r'pyfa.py'], +a = Analysis([r'../../pyfa.py'], pathex=[], binaries=[], datas=added_files, From 1daef5354d092005ea62f434c81686f2878a4a82 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 26 Nov 2017 18:31:48 -0500 Subject: [PATCH 098/212] remove old wx test file --- wxthing.py | 82 ------------------------------------------------------ 1 file changed, 82 deletions(-) delete mode 100644 wxthing.py diff --git a/wxthing.py b/wxthing.py deleted file mode 100644 index 59eaaf75f..000000000 --- a/wxthing.py +++ /dev/null @@ -1,82 +0,0 @@ -import wx - - -# Custom class that forces the TextEntryDialog to accept a Validator on the TextCtrl -class TextEntryValidatedDialog(wx.TextEntryDialog): - def __init__(self, parent, validator=None, *args, **kargs): - wx.TextEntryDialog.__init__(self, parent, *args, **kargs) - self.parent = parent - - self.txtctrl = self.FindWindowById(3000) - if validator: - self.txtctrl.SetValidator(validator()) - -# Define a base Validator class that all other Validators will extend -class BaseValidator(wx.Validator): - def __init__(self): - wx.Validator.__init__(self) - - def Validate(self, win): - raise NotImplementedError() - - def TransferToWindow(self): - return True - - def TransferFromWindow(self): - return True - -# Define my Custom Validator -class MyTextValidator(BaseValidator): - def __init__(self): - BaseValidator.__init__(self) - - def Clone(self): - return MyTextValidator() - - def Validate(self, win): - print("Validating!") - textCtrl = self.GetWindow() - text = textCtrl.GetValue().strip() - try: - if len(text) == 0: - raise ValueError("You must supply a value!") - elif text == "error": - raise ValueError("Simulate another error!") - return True - except ValueError as e: - wx.MessageBox("{}".format(e), "Error") - textCtrl.SetFocus() - return False - - -class MyForm(wx.Frame): - def __init__(self): - wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial") - - # Add a panel so it looks the correct on all platforms - panel = wx.Panel(self, wx.ID_ANY) - self.index = 0 - - btn = wx.Button(panel, label="Pop Dialog") - btn.Bind(wx.EVT_BUTTON, self.pop_dialog) - - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(btn, 0, wx.ALL | wx.CENTER, 5) - panel.SetSizer(sizer) - - def pop_dialog(self, event): - dlg = TextEntryValidatedDialog(self, MyTextValidator, - "Enter some text here (or \"error\" if you want to simulate a validation failure", - "Thing") - dlg.txtctrl.SetInsertionPointEnd() - dlg.CenterOnParent() - - if dlg.ShowModal() == wx.ID_OK: - print("Entered Value: "+dlg.txtctrl.GetValue().strip()) - - -if __name__ == "__main__": - app = wx.App(False) - frame = MyForm() - frame.Show() - app.MainLoop() \ No newline at end of file From 2365112292f35c3d73f747da5d22472ba22f4418 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 26 Nov 2017 18:36:06 -0500 Subject: [PATCH 099/212] Some file cleanup --- .version | 1 + dist_assets/mac/pyfa.spec | 2 +- dist_assets/win/pyfa.spec | 2 +- requirements.txt | 9 +-- requirements_build_linux.txt | 8 --- requirements_build_osx.txt | 9 --- requirements_build_windows.txt | 11 ---- requirements_test.txt | 10 ---- setup-osx.py | 22 ------- setup.py | 103 --------------------------------- testFits.py | 79 ------------------------- 11 files changed, 8 insertions(+), 248 deletions(-) create mode 100644 .version delete mode 100644 requirements_build_linux.txt delete mode 100644 requirements_build_osx.txt delete mode 100644 requirements_build_windows.txt delete mode 100644 requirements_test.txt delete mode 100644 setup-osx.py delete mode 100644 setup.py delete mode 100644 testFits.py diff --git a/.version b/.version new file mode 100644 index 000000000..71eef1c4b --- /dev/null +++ b/.version @@ -0,0 +1 @@ +v1.33.2-100-gc000b19 \ No newline at end of file diff --git a/dist_assets/mac/pyfa.spec b/dist_assets/mac/pyfa.spec index 3f674346d..ece015d1c 100644 --- a/dist_assets/mac/pyfa.spec +++ b/dist_assets/mac/pyfa.spec @@ -8,7 +8,7 @@ import requests.certs label = subprocess.check_output([ "git", "describe", "--tags"]).strip() -with open('gitversion', 'w+') as f: +with open('.version', 'w+') as f: f.write(label.decode()) block_cipher = None diff --git a/dist_assets/win/pyfa.spec b/dist_assets/win/pyfa.spec index a5e71adca..21e8bef83 100644 --- a/dist_assets/win/pyfa.spec +++ b/dist_assets/win/pyfa.spec @@ -8,7 +8,7 @@ import requests.certs label = subprocess.check_output([ "git", "describe", "--tags"]).strip() -with open('gitversion', 'w+') as f: +with open('.version', 'w+') as f: f.write(label.decode()) block_cipher = None diff --git a/requirements.txt b/requirements.txt index 42a072a9b..694a2d348 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ -logbook >= 0.10.0 -matplotlib +wxPython >= 4.0.0b2 +logbook >= 1.0.0 +matplotlib >= 2.0.0 python-dateutil urllib3 -requests==2.11.1 -sqlalchemy==1.0.5 +requests == 2.0.0 +sqlalchemy == 1.0.5 diff --git a/requirements_build_linux.txt b/requirements_build_linux.txt deleted file mode 100644 index 28f42aa34..000000000 --- a/requirements_build_linux.txt +++ /dev/null @@ -1,8 +0,0 @@ -PyInstaller >= 3.2.1 -cycler >= 0.10.0 -functools32 >= 3.2.3 -future >= 0.16.0 -numpy >= 1.12 -pyparsing >= 2.1.10 -pytz >= 2016.10 -six diff --git a/requirements_build_osx.txt b/requirements_build_osx.txt deleted file mode 100644 index 67b64e480..000000000 --- a/requirements_build_osx.txt +++ /dev/null @@ -1,9 +0,0 @@ -PyInstaller >= 3.2.1 -cycler >= 0.10.0 -functools32 >= 3.2.3 -future >= 0.16.0 -numpy >= 1.12 -pyparsing >= 2.1.10 -pypiwin32 >= 219 -pytz >= 2016.10 -six >= 1.10.0 diff --git a/requirements_build_windows.txt b/requirements_build_windows.txt deleted file mode 100644 index e18a0801d..000000000 --- a/requirements_build_windows.txt +++ /dev/null @@ -1,11 +0,0 @@ -PyInstaller >= 3.2.1 -cx_freeze == 4.3.4 -cycler >= 0.10.0 -functools32 >= 3.2.3 -future >= 0.16.0 -numpy >= 1.12 -pyparsing >= 2.1.10 -pypiwin32 >= 219 -pytz >= 2016.10 -six >= 1.10.0 -tornado diff --git a/requirements_test.txt b/requirements_test.txt deleted file mode 100644 index 58e4ba7e9..000000000 --- a/requirements_test.txt +++ /dev/null @@ -1,10 +0,0 @@ -pytest == 3.0.3 -pytest-mock == 1.2 -pytest-cov == 2.3.1 -pytest-capturelog == 0.7 -coverage == 4.2 -coveralls == 1.1 -codecov -virtualenv>=15.0.0 -tox -wheel diff --git a/setup-osx.py b/setup-osx.py deleted file mode 100644 index 8e2d55de7..000000000 --- a/setup-osx.py +++ /dev/null @@ -1,22 +0,0 @@ -""" -This is a setup.py script generated by py2applet -Usage: - python setup.py py2app -""" - -from setuptools import setup -import requests.certs -APP = ['pyfa.py'] -DATA_FILES = ['eve.db', 'README.md', 'LICENSE', 'imgs', requests.certs.where()] -OPTIONS = { - 'argv_emulation': False, - 'iconfile': 'dist_assets/mac/pyfa.icns', - 'packages': ['eos', 'gui', 'service', 'utils', 'sqlalchemy'] -} - -setup( - app=APP, - data_files=DATA_FILES, - options={'py2app': OPTIONS}, - setup_requires=['py2app'], -) diff --git a/setup.py b/setup.py deleted file mode 100644 index 0d281bd89..000000000 --- a/setup.py +++ /dev/null @@ -1,103 +0,0 @@ -""" -Distribution builder for pyfa. - -Windows executable: python setup.py build -Windows executable + installer: python setup.py bdist_msi -""" -import requests.certs - -# The modules that contain the bulk of teh source -packages = ['eos', 'gui', 'service', 'utils'] -# Extra files that will be copied into the root directory -include_files = ['eve.db', 'LICENSE', 'README.md', (requests.certs.where(), 'cacert.pem')] -# this is read by dist.py to package the icons -icon_dirs = ['gui', 'icons', 'renders'] - -includes = [] -# collection.abc due to bug: -# https://bitbucket.org/anthony_tuininga/cx_freeze/issues/127/collectionssys-error -# All the other stuff is crap that I have. VENV isn't working right for me for a few dependancies, so bleh -excludes = ['Tkinter', 'collections.abc', 'IPython', 'PyQt4', 'PIL', 'nose', 'tornado', 'zmq', 'mysql', 'scipy'] - - -if __name__ == "__main__": - import sys - # noinspection PyPackageRequirements,PyUnresolvedReferences - from cx_Freeze import setup, Executable - import config - import tempfile - import os - import zipfile - import shutil - tmpdir = tempfile.mkdtemp() - - imagesFile = os.path.join(tmpdir, "imgs.zip") - - def zipdir(path, zip): - for root, dirs, files in os.walk(path): - for file in files: - zip.write(os.path.join(root, file)) - - os.chdir('imgs') - with zipfile.ZipFile(imagesFile, 'w') as images: - for dir in icon_dirs: - zipdir(dir, images) - os.chdir('..') - - app_name = 'pyfa' - app_version = '{}'.format(config.version) - app_description = 'Python fitting assistant' - - # Windows-specific options - build_options_winexe = { - 'packages': packages, - 'include_files': include_files+[imagesFile], - 'includes': includes, - 'excludes': excludes, - 'compressed': True, - 'optimize': 2, - 'include_msvcr': True, - } - - build_options_winmsi = { - 'upgrade_code': '{E80885AC-31BA-4D9A-A04F-9E5915608A6C}', - 'add_to_path': False, - 'initial_target_dir': r'[ProgramFilesFolder]\{}'.format(app_name), - } - - # Mac-specific options (untested) - build_options_macapp = { - 'iconfile': 'dist_assets/mac/pyfa.icns', - 'bundle_name': app_name, - } - - build_options_macdmg = { - 'volume_label': app_name, - 'applications-shortcut': True, - } - - # Generic executable options - executable_options = { - 'script': 'pyfa.py', - # Following are windows-specific options, they are stored - # on a per-executable basis - 'base': 'Win32GUI' if sys.platform == 'win32' else None, - 'icon': 'dist_assets/win/pyfa.ico', - 'shortcutDir': 'DesktopFolder', - 'shortcutName': app_name, - } - - setup( - name=app_name, - version=app_version, - description=app_description, - options={ - 'build_exe': build_options_winexe, - 'bdist_msi': build_options_winmsi, - 'bdist_mac': build_options_macapp, - 'bdist_dmg': build_options_macdmg, - }, - executables=[Executable(**executable_options)] - ) - - shutil.rmtree(tmpdir) diff --git a/testFits.py b/testFits.py deleted file mode 100644 index b2c31df5c..000000000 --- a/testFits.py +++ /dev/null @@ -1,79 +0,0 @@ -fits = ["306", "317", "318", "320", "321", "322", "323", "330", "334", "337", "338", "342", "348", "349", "350", - "374", "377", "379", "382", - "387", "389", "390", "393", "394", "395", "397", "399", "420", "423", "424", "427", "431", "432", "436", - "437", "438", "439", "440", "444", - "446", "450", "453", "457", "458", "459", "461", "464", "471", "472", "473", "474", "478", "483", "484", - "485", "486", "487", "494", "495", - "496", "501", "504", "505", "508", "509", "513", "515", "517", "518", "519", "522", "523", "526", "527", - "528", "529", "530", "531", "532", - "534", "535", "536", "537", "539", "540", "541", "542", "543", "546", "547", "548", "550", "551", "554", - "555", "556", "558", "559", "560", - "563", "565", "566", "568", "569", "570", "571", "572", "574", "576", "578", "581", "582", "583", "585", - "586", "587", "589", "593", "594", - "598", "602", "603", "606", "610", "611", "612", "613", "614", "618", "620", "621", "624", "626", "627", - "628", "629", "630", "631", "632", - "633", "634", "635", "637", "638", "640", "644", "645", "648", "649", "654", "655", "656", "657", "665", - "666", "667", "668", "669", "671", - "674", "677", "678", "685", "686", "690", "691", "692", "693", "694", "695", "696", "698", "699", "702", - "704", "712", "713", "714", "716", - "718", "722", "723", "726", "727", "728", "730", "731", "733", "735", "736", "737", "738", "741", "745", - "746", "749", "750", "751", "756", - "757", "758", "759", "766", "769", "778", "779", "780", "782", "783", "786", "788", "789", "791", "794", - "796", "798", "801", "808", "817", - "818", "819", "822", "823", "827", "831", "837", "838", "839", "846", "847", "856", "857", "866", "867", - "868", "869", "871", "877", "880", - "881", "885", "886", "889", "892", "893", "894", "895", "897", "905", "906", "908", "910", "911", "912", - "915", "916", "917", "922", "924", - "926", "931", "932", "933", "934", "936", "939", "942", "947", "949", "951", "952", "955", "957", "958", - "959", "961", "962", "968", "969", - "971", "974", "975", "977", "978", "979", "980", "982", "984", "985", "987", "988", "989", "990", "991", - "992", "997", "999", "1004", "1005", - "1008", "1009", "1015", "1016", "1025", "1026", "1027", "1036", "1037", "1043", "1044", "1045", "1046", - "1047", "1048", "1049", "1051", - "1060", "1061", "1062", "1063", "1065", "1066", "1067", "1069", "1073", "1074", "1075", "1076", "1078", - "1083", "1084", "1085", "1086", - "1088", "1089", "1090", "1091", "1092", "1094", "1096", "1099", "1100", "1101", "1102", "1104", "1105", - "1112", "1114", "1115", "1117", - "1119", "1121", "1122", "1124", "1128", "1132", "1134", "1139", "1140", "1142", "1143", "1144", "1145", - "1146", "1150", "1151", "1152", - "1155", "1156", "1157", "1161", "1163", "1164", "1166", "1167", "1170", "1172", "1173", "1174", "1176", - "1180", "1181", "1184", "1185", - "1187", "1194", "1196", "1199", "1200", "1201", "1202", "1203", "1206", "1207", "1214", "1216", "1218", - "1220", "1225", "1228", "1230", - "1235", "1236", "1237", "1239", "1243", "1244", "1245", "1249", "1250", "1251", "1252", "1255", "1261", - "1262", "1264", "1265", "1266", - "1267", "1269", "1270", "1272", "1273", "1275", "1276", "1281", "1284", "1285", "1286", "1291", "1292", - "1293", "1297", "1299", "1300", - "1301", "1302", "1304", "1305", "1306", "1310", "1311", "1312", "1315", "1322", "1331", "1332", "1338", - "1341", "1345", "1346", "1358", - "1359", "1360", "1362", "1364", "1370", "1373", "1374", "1387", "1391", "1393", "1394", "1395", "1400", - "1403", "1404", "1406", "1408", - "1411", "1412", "1413", "1414", "1415", "1419", "1420", "1421", "1422", "1423", "1424", "1426", "1427", - "1428", "1429", "1430", "1440", - "1441", "1443", "1445", "1447", "1450", "1451", "1454", "1455", "1456", "1457", "1458", "1459", "1462", - "1463", "1465", "1466", "1468", - "1469", "1471", "1476", "1479", "1481", "1482", "1483", "1484", "1485", "1486", "1487", "1489", "1490", - "1491", "1493", "1495", "1499", - "1503", "1505", "1506", "1508", "1511", "1517", "1528", "1529", "1532", "1534", "1535", "1537", "1542", - "1543", "1544", "1546", "1547", - "1548", "1549", "1550", ] -import time - -for fitID in fits[:100]: - time.sleep(.200) - print(fitID) - wx.PostEvent(self, FitSelected(fitID=int(fitID), startup=2)) - -from service.fit import Fit -sFit = Fit.getInstance() - -for x in fits: - try: - print(x) - fit = sFit.getFit(int(x)) - fit.clear() - fit.calculateModifiedAttributes() - except Exception as e: - print(("failed on ",x)) - print(e) - raise e From 2936b88c3d20fea6f008b83429537684710cab5c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 27 Nov 2017 21:05:06 -0500 Subject: [PATCH 100/212] code cleanup --- .gitignore | 1 + .version | 2 +- config.py | 8 +- eos/saveddata/fit.py | 4 +- gui/builtinItemStatsViews/itemAttributes.py | 5 +- gui/builtinItemStatsViews/itemCompare.py | 3 +- gui/builtinItemStatsViews/itemDependants.py | 2 - gui/builtinItemStatsViews/itemDescription.py | 3 - gui/builtinItemStatsViews/itemEffects.py | 3 +- gui/builtinItemStatsViews/itemProperties.py | 2 +- gui/builtinItemStatsViews/itemRequirements.py | 2 - gui/builtinItemStatsViews/itemTraits.py | 1 - gui/builtinMarketBrowser/itemView.py | 2 +- gui/builtinMarketBrowser/marketTree.py | 2 +- .../pyfaGaugePreferences.py | 80 +++++++++---------- .../pyfaHTMLExportPreferences.py | 1 + gui/builtinShipBrowser/categoryItem.py | 2 +- gui/builtinShipBrowser/fitItem.py | 2 +- gui/builtinShipBrowser/navigationPanel.py | 3 +- gui/builtinShipBrowser/raceSelector.py | 3 +- gui/builtinShipBrowser/shipItem.py | 2 +- .../targetingMiscViewMinimal.py | 1 - gui/builtinViews/fittingView.py | 10 +-- gui/characterEditor.py | 5 +- gui/characterSelection.py | 1 + gui/chrome_tabs.py | 43 +++++----- gui/crestFittings.py | 4 +- gui/devTools.py | 39 ++++++++- gui/errorDialog.py | 10 +-- gui/mainFrame.py | 4 +- gui/mainMenuBar.py | 4 +- gui/pyfa_gauge.py | 36 ++++----- gui/shipBrowser.py | 2 +- gui/toggle_panel.py | 13 +-- gui/updateDialog.py | 1 - gui/utils/color.py | 14 ++-- gui/utils/draw.py | 11 +-- pyfa.py | 14 ++-- service/eveapi.py | 4 +- service/network.py | 5 +- service/port.py | 2 +- service/prereqsCheck.py | 5 +- service/settings.py | 4 +- 43 files changed, 193 insertions(+), 172 deletions(-) diff --git a/.gitignore b/.gitignore index c3530eab7..6cf50c952 100644 --- a/.gitignore +++ b/.gitignore @@ -118,3 +118,4 @@ ENV/ .idea eos.iml gitversion +.version diff --git a/.version b/.version index 71eef1c4b..524d0fb50 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -v1.33.2-100-gc000b19 \ No newline at end of file +v1.33.2-104-g2365112 \ No newline at end of file diff --git a/config.py b/config.py index d0fd19084..716f5ef24 100644 --- a/config.py +++ b/config.py @@ -48,6 +48,8 @@ def isFrozen(): return True else: return False + + def __createDirs(path): if not os.path.exists(path): os.makedirs(path) @@ -104,7 +106,7 @@ def defPaths(customSavePath=None): __createDirs(savePath) - #if isFrozen(): + # if isFrozen(): # os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem") # os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") @@ -141,6 +143,7 @@ def defPaths(customSavePath=None): from service.settings import EOSSettings eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever + def defLogging(): global debug global logPath @@ -230,6 +233,3 @@ class LoggerWriter(object): # sys.stderr is the correct way to do it, but it seemed # to work properly for me. self.level(sys.stderr) - - - diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 979b007dc..c4b855973 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -756,7 +756,7 @@ class Fit(object): # Loop through our run times here. These determine which effects are run in which order. for runTime in ("early", "normal", "late"): - #pyfalog.debug("Run time: {0}", runTime) + # pyfalog.debug("Run time: {0}", runTime) # Items that are unrestricted. These items are run on the local fit # first and then projected onto the target fit it one is designated u = [ @@ -795,7 +795,7 @@ class Fit(object): # targetFit.register(item, origin=self) item.calculateModifiedAttributes(targetFit, runTime, False, True) - #pyfalog.debug("Command Bonuses: {}".format(self.commandBonuses)) + # pyfalog.debug("Command Bonuses: {}".format(self.commandBonuses)) # If we are calculating our local or projected fit and have command bonuses, apply them if type != CalcType.COMMAND and self.commandBonuses: diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index f5073796c..c9850a6e5 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -12,6 +12,7 @@ from service.market import Market from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount + class ItemParams(wx.Panel): def __init__(self, parent, stuff, item, context=None): wx.Panel.__init__(self, parent) @@ -226,7 +227,7 @@ class ItemParams(wx.Panel): self.paramList.SetItem(index, 2, valueUnitDefault) # @todo: pheonix, this lamda used cmp() which no longer exists in py3. Probably a better way to do this in the # long run, take a look - self.paramList.SortItems(lambda id1, id2: (idNameMap[id1]>idNameMap[id2])-(idNameMap[id1] idNameMap[id2]) - (idNameMap[id1] < idNameMap[id2])) self.paramList.RefreshRows() self.totalAttrsLabel.SetLabel("%d attributes. " % idCount) self.Layout() @@ -271,5 +272,3 @@ class ItemParams(wx.Panel): return "%s %s" % (fvalue, override[1]) else: return "%s %s" % (formatAmount(value, 3, 0), unitName) - - diff --git a/gui/builtinItemStatsViews/itemCompare.py b/gui/builtinItemStatsViews/itemCompare.py index 8e38954bd..f91a81e2a 100644 --- a/gui/builtinItemStatsViews/itemCompare.py +++ b/gui/builtinItemStatsViews/itemCompare.py @@ -9,6 +9,7 @@ from service.market import Market from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount + class ItemCompare(wx.Panel): def __init__(self, parent, stuff, item, items, context=None): # Start dealing with Price stuff to get that thread going @@ -205,5 +206,3 @@ class ItemCompare(wx.Panel): return "%s %s" % (fvalue, override[1]) else: return "%s %s" % (formatAmount(value, 3, 0), unitName) - - diff --git a/gui/builtinItemStatsViews/itemDependants.py b/gui/builtinItemStatsViews/itemDependants.py index ae9984b7b..9f7e459c9 100644 --- a/gui/builtinItemStatsViews/itemDependants.py +++ b/gui/builtinItemStatsViews/itemDependants.py @@ -51,5 +51,3 @@ class ItemDependents(wx.Panel): itemIcon = -1 self.reqTree.AppendItem(child, "{}".format(item.name), itemIcon) - - diff --git a/gui/builtinItemStatsViews/itemDescription.py b/gui/builtinItemStatsViews/itemDescription.py index 8dce7138f..ddf30dfb4 100644 --- a/gui/builtinItemStatsViews/itemDescription.py +++ b/gui/builtinItemStatsViews/itemDescription.py @@ -5,7 +5,6 @@ import wx.html import re - class ItemDescription(wx.Panel): def __init__(self, parent, stuff, item): wx.Panel.__init__(self, parent) @@ -32,5 +31,3 @@ class ItemDescription(wx.Panel): mainSizer.Add(self.description, 1, wx.ALL | wx.EXPAND, 0) self.Layout() - - diff --git a/gui/builtinItemStatsViews/itemEffects.py b/gui/builtinItemStatsViews/itemEffects.py index 0675ac3c4..8c3537ad0 100644 --- a/gui/builtinItemStatsViews/itemEffects.py +++ b/gui/builtinItemStatsViews/itemEffects.py @@ -8,6 +8,7 @@ import wx from .helpers import AutoListCtrl + class ItemEffects(wx.Panel): def __init__(self, parent, stuff, item): wx.Panel.__init__(self, parent) @@ -127,5 +128,3 @@ class ItemEffects(wx.Panel): self.Layout() self.Thaw() event.Skip() - - diff --git a/gui/builtinItemStatsViews/itemProperties.py b/gui/builtinItemStatsViews/itemProperties.py index 0eb13f4c8..8a7fdacbd 100644 --- a/gui/builtinItemStatsViews/itemProperties.py +++ b/gui/builtinItemStatsViews/itemProperties.py @@ -93,7 +93,7 @@ class ItemProperties(wx.Panel): # We couldn't get a property for some reason. Skip it for now. continue - self.paramList.SortItems(lambda id1, id2: (idNameMap[id1]>idNameMap[id2])-(idNameMap[id1] idNameMap[id2]) - (idNameMap[id1] < idNameMap[id2])) self.paramList.RefreshRows() self.totalAttrsLabel.SetLabel("%d attributes. " % idCount) self.Layout() diff --git a/gui/builtinItemStatsViews/itemRequirements.py b/gui/builtinItemStatsViews/itemRequirements.py index 453679a10..dc3c947cd 100644 --- a/gui/builtinItemStatsViews/itemRequirements.py +++ b/gui/builtinItemStatsViews/itemRequirements.py @@ -37,5 +37,3 @@ class ItemRequirements(wx.Panel): if skill.ID not in self.skillIdHistory: self.getFullSkillTree(skill, child, sbIconId) self.skillIdHistory.append(skill.ID) - - diff --git a/gui/builtinItemStatsViews/itemTraits.py b/gui/builtinItemStatsViews/itemTraits.py index 0b01a4dd5..12abd078d 100644 --- a/gui/builtinItemStatsViews/itemTraits.py +++ b/gui/builtinItemStatsViews/itemTraits.py @@ -15,4 +15,3 @@ class ItemTraits(wx.Panel): mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0) self.Layout() - diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py index 52abea877..f2b5a7627 100644 --- a/gui/builtinMarketBrowser/itemView.py +++ b/gui/builtinMarketBrowser/itemView.py @@ -9,7 +9,7 @@ from gui.utils.staticHelpers import DragDropHelper from logbook import Logger -from gui.builtinMarketBrowser.events import * +from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES, MAX_RECENTLY_USED_MODULES, ItemSelected pyfalog = Logger(__name__) diff --git a/gui/builtinMarketBrowser/marketTree.py b/gui/builtinMarketBrowser/marketTree.py index a64f59953..8b1286e50 100644 --- a/gui/builtinMarketBrowser/marketTree.py +++ b/gui/builtinMarketBrowser/marketTree.py @@ -1,7 +1,7 @@ import wx from gui.cachingImageList import CachingImageList -from gui.builtinMarketBrowser.events import * +from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES from logbook import Logger diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index 6e8d4ced2..c0ca68a98 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -1,14 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - # noinspection PyPackageRequirements import wx import copy from gui.preferenceView import PreferenceView from gui.bitmap_loader import BitmapLoader -from gui.utils import color +from gui.utils.color import CalculateTransition import gui.utils.draw as drawUtils @@ -110,44 +109,45 @@ class PFGaugePreview(wx.Window): self.Refresh() def OnPaint(self, event): - rect = self.GetClientRect() - dc = wx.AutoBufferedPaintDC(self) - dc.SetBackground(wx.Brush(self.bkColor)) - dc.Clear() - - value = float(self.value) - if self.percS >= 100: - w = rect.width - else: - w = rect.width * (float(value) / 100) - r = copy.copy(rect) - r.width = w - - color = color.CalculateTransitionColor(self.colorS, self.colorE, float(value) / 100) - if self.gradientStart > 0: - gcolor = color.BrightenColor(color, float(self.gradientStart) / 100) - gMid = color.BrightenColor(color, float(self.gradientStart / 2) / 100) - else: - gcolor = color.DarkenColor(color, float(-self.gradientStart) / 100) - gMid = color.DarkenColor(color, float(-self.gradientStart / 2) / 100) - - gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) - dc.DrawBitmap(gBmp, 0, 0) - dc.SetFont(self.font) - - r = copy.copy(rect) - 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.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) - - dc.SetTextForeground(wx.Colour(255, 255, 255)) - dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) + pass + # rect = self.GetClientRect() + # dc = wx.AutoBufferedPaintDC(self) + # dc.SetBackground(wx.Brush(self.bkColor)) + # dc.Clear() + # + # value = float(self.value) + # if self.percS >= 100: + # w = rect.width + # else: + # w = rect.width * (float(value) / 100) + # r = copy.copy(rect) + # r.width = w + # + # color = CalculateTransitionColor(self.colorS, self.colorE, float(value) / 100) + # if self.gradientStart > 0: + # gcolor = color.BrightenColor(color, float(self.gradientStart) / 100) + # gMid = color.BrightenColor(color, float(self.gradientStart / 2) / 100) + # else: + # gcolor = color.DarkenColor(color, float(-self.gradientStart) / 100) + # gMid = color.DarkenColor(color, float(-self.gradientStart / 2) / 100) + # + # gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) + # dc.DrawBitmap(gBmp, 0, 0) + # dc.SetFont(self.font) + # + # r = copy.copy(rect) + # 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.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) + # + # dc.SetTextForeground(wx.Colour(255, 255, 255)) + # dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) class PFGaugePref(PreferenceView): diff --git a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py index 4df2ca7ca..d73cc8e58 100644 --- a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py +++ b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py @@ -10,6 +10,7 @@ import gui.mainFrame from service.settings import HTMLExportSettings import wx.lib.agw.hyperlink + class PFHTMLExportPref(PreferenceView): title = "HTML Export" desc = ("HTML Export (File > Export HTML) allows you to export your entire fitting " diff --git a/gui/builtinShipBrowser/categoryItem.py b/gui/builtinShipBrowser/categoryItem.py index 28ffec3bd..b88557d22 100644 --- a/gui/builtinShipBrowser/categoryItem.py +++ b/gui/builtinShipBrowser/categoryItem.py @@ -8,7 +8,7 @@ import gui.utils.color as colorUtils import gui.utils.draw as drawUtils import gui.utils.fonts as fonts from gui.bitmap_loader import BitmapLoader -from .events import * +from .events import Stage2Selected pyfalog = Logger(__name__) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index 760916d4b..f76e3d6f9 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -13,7 +13,7 @@ import gui.mainFrame import gui.utils.color as colorUtils import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -from .events import * +from .events import ImportSelected, SearchSelected, FitSelected, BoosterListUpdated, Stage3Selected, FitRenamed, FitRemoved from gui.bitmap_loader import BitmapLoader from gui.builtinShipBrowser.pfBitmapFrame import PFBitmapFrame from service.fit import Fit diff --git a/gui/builtinShipBrowser/navigationPanel.py b/gui/builtinShipBrowser/navigationPanel.py index 414fe0b21..204802c45 100644 --- a/gui/builtinShipBrowser/navigationPanel.py +++ b/gui/builtinShipBrowser/navigationPanel.py @@ -8,7 +8,7 @@ import gui.mainFrame import gui.utils.color as colorUtils import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -from .events import * +from .events import FitSelected, SearchSelected, ImportSelected, Stage1Selected, Stage2Selected, Stage3Selected from gui.bitmap_loader import BitmapLoader from service.fit import Fit @@ -271,4 +271,3 @@ class NavigationPanel(SFItem.SFBrowserItem): wx.PostEvent(self.Parent, ImportSelected(fits=data)) else: wx.PostEvent(self.Parent, Stage1Selected()) - diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index 5931e3aad..809474249 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -6,7 +6,7 @@ from logbook import Logger import gui.utils.anim_effects as animEffects import gui.utils.color as colorUtils import gui.utils.draw as drawUtils -from .events import * +from .events import Stage2Selected from gui.bitmap_loader import BitmapLoader pyfalog = Logger(__name__) @@ -270,4 +270,3 @@ class RaceSelector(wx.Window): self.checkMaximize = False event.Skip() - diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py index 3c48cbddf..4c679fb8a 100644 --- a/gui/builtinShipBrowser/shipItem.py +++ b/gui/builtinShipBrowser/shipItem.py @@ -8,7 +8,7 @@ import gui.mainFrame import gui.utils.color as colorUtils import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -from .events import * +from .events import Stage3Selected, Stage2Selected, Stage1Selected, FitSelected from gui.bitmap_loader import BitmapLoader from gui.contextMenu import ContextMenu from service.fit import Fit diff --git a/gui/builtinStatsViews/targetingMiscViewMinimal.py b/gui/builtinStatsViews/targetingMiscViewMinimal.py index e37c4b3a4..5e17f8969 100644 --- a/gui/builtinStatsViews/targetingMiscViewMinimal.py +++ b/gui/builtinStatsViews/targetingMiscViewMinimal.py @@ -24,7 +24,6 @@ from gui.utils.numberFormatter import formatAmount from collections import OrderedDict - class TargetingMiscViewMinimal(StatsView): name = "targetingMiscViewMinimal" diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 271278283..f1bb3f89d 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -25,7 +25,7 @@ import gui.mainFrame from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED import gui.display as d from gui.contextMenu import ContextMenu -from gui.builtinShipBrowser.events import * +from gui.builtinShipBrowser.events import EVT_FIT_RENAMED, EVT_FIT_REMOVED, FitSelected, EVT_FIT_SELECTED import gui.multiSwitch from eos.saveddata.mode import Mode from eos.saveddata.module import Module, Slot, Rack @@ -77,10 +77,10 @@ class FitSpawner(gui.multiSwitch.TabSpawner): self.multiSwitch.AddPage() view = self.multiSwitch.GetSelectedPage() - + if not isinstance(view, FittingView): view = FittingView(self.multiSwitch) - print("###################### Created new view:"+repr(view)) + print("###################### Created new view:" + repr(view)) self.multiSwitch.ReplaceActivePage(view) view.fitSelected(event) @@ -221,7 +221,7 @@ class FittingView(d.Display): wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def Destroy(self): - print("+++++ Destroy "+repr(self)) + print("+++++ Destroy " + repr(self)) print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED)) print(self.mainFrame.Unbind(GE.FIT_CHANGED)) print(self.mainFrame.Unbind(EVT_FIT_RENAMED)) @@ -312,7 +312,7 @@ class FittingView(d.Display): event.Skip() def fitSelected(self, event): - print('====== Fit Selected: '+repr(self)+str(bool(self))) + print('====== Fit Selected: ' + repr(self) + str(bool(self))) if self.parent.IsActive(self): fitID = event.fitID diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 95fda70b8..080940c7d 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -324,14 +324,14 @@ class SkillTreeView(wx.Panel): tree.AppendColumn("Skill") tree.AppendColumn("Level") - #tree.SetMainColumn(0) + # tree.SetMainColumn(0) self.root = tree.GetRootItem() # self.root = tree.AppendItem(root, "Skills") # # tree.SetItemText(self.root, 1, "Levels") - #tree.SetColumnWidth(0, 300) + # tree.SetColumnWidth(0, 300) self.btnSecStatus = wx.Button(self, wx.ID_ANY, "Sec Status: {0:.2f}".format(char.secStatus or 0.0)) self.btnSecStatus.Bind(wx.EVT_BUTTON, self.onSecStatus) @@ -622,7 +622,6 @@ class SkillTreeView(wx.Panel): if parent[1] in dirtyGroups: self.skillTreeListCtrl.SetItemImage(parentID, self.skillBookImageId) - event.Skip() diff --git a/gui/characterSelection.py b/gui/characterSelection.py index d6685e0fe..aba1c68fb 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -27,6 +27,7 @@ import gui.globalEvents as GE import gui.mainFrame from service.character import Character from service.fit import Fit +from gui.utils.clipboard import toClipboard pyfalog = Logger(__name__) diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py index e2212499e..8aa85929b 100644 --- a/gui/chrome_tabs.py +++ b/gui/chrome_tabs.py @@ -11,7 +11,7 @@ # tab index?). This will also help with finding close buttons. # ToDo: Fix page preview code (PFNotebookPagePreview) # -#=============================================================================== +#= ============================================================================== import wx import wx.lib.newevent @@ -28,6 +28,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 @@ -412,7 +413,7 @@ class _TabRenderer: mdc.SelectObject(ebmp) mdc.SetFont(self.font) textSizeX, textSizeY = mdc.GetTextExtent(self.text) - totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width/2 + 16 + self.padding*2 + totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding* 2 mdc.SelectObject(wx.NullBitmap) return totalSize, self.tab_height @@ -512,7 +513,7 @@ class _TabRenderer: x_offset = self.content_width \ + self.left_width \ - self.ctab_close_bmp.GetWidth() / 2 - y_offset = (self.tab_height - self.ctab_close_bmp.GetHeight())/2 + y_offset = (self.tab_height - self.ctab_close_bmp.GetHeight()) / 2 self.close_region.Offset(x_offset, y_offset) def InitColors(self): @@ -580,8 +581,8 @@ class _TabRenderer: mdc.DrawBitmap( cbmp, - self.content_width + self.left_width - cbmp.GetWidth()/2, - (height - cbmp.GetHeight())/2) + self.content_width + self.left_width - cbmp.GetWidth() / 2, + (height - cbmp.GetHeight()) / 2) mdc.SelectObject(wx.NullBitmap) @@ -594,7 +595,6 @@ class _TabRenderer: bmp = wx.Bitmap(img) self.tab_bitmap = bmp - def __repr__(self): return "_TabRenderer(text={}, disabled={}) at {}".format( self.text, self.disabled, hex(id(self)) @@ -620,7 +620,7 @@ class _AddRenderer: def GetPosition(self): return self.position - def SetPosition(self,pos): + def SetPosition(self, pos): self.position = pos def GetSize(self): @@ -869,7 +869,7 @@ class _TabsContainer(wx.Panel): for tab in self.tabs: if self.CheckTabClose(tab, mposx, mposy): return - + def DisableTab(self, tab, disabled=True): tb_renderer = self.tabs[tab] tb_renderer.disabled = disabled @@ -1249,7 +1249,7 @@ class _TabsContainer(wx.Panel): selected = None for i in range(len(self.tabs) - 1, -1, -1): tab = self.tabs[i] - width = tab.tab_width - self.inclination*2 + width = tab.tab_width - self.inclination * 2 pos -= width if not tab.IsSelected(): tab.SetPosition((pos, self.container_height - self.height)) @@ -1260,12 +1260,8 @@ class _TabsContainer(wx.Panel): if selected is not skip_tab: selected.SetPosition((selpos, self.container_height - self.height)) - self.add_button.SetPosition(( - round(tabsWidth) - + self.inclination*2, - self.container_height - - self.height / 2 - - self.add_button.GetHeight()/3)) + self.add_button.SetPosition((round(tabsWidth) + self.inclination * 2, + self.container_height - self.height / 2 - self.add_button.GetHeight() / 3)) def OnLeaveWindow(self, event): if self.start_drag and not self.dragging: @@ -1292,7 +1288,7 @@ class _TabsContainer(wx.Panel): if page.Snapshot(): self.preview_wnd = PFNotebookPagePreview( self, - (mposx+3, mposy+3), + (mposx + 3, mposy + 3), page.Snapshot(), self.preview_tab.text) self.preview_wnd.Show() @@ -1332,13 +1328,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 @@ -1347,7 +1343,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) @@ -1371,8 +1367,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): @@ -1384,18 +1379,16 @@ class PFNotebookPagePreview(wx.Frame): mdc.SetBackground(wx.Brush(color)) mdc.Clear() - font = wx.Font(10, wx.SWISS, wx.NORMAL,wx.NORMAL, False) + font = wx.Font(10, 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.DrawBitmap(self.bitmap, 0, 16) mdc.SetPen(wx.Pen("#000000", width=1)) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index ed1b62ce4..718b6654b 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -16,10 +16,10 @@ import gui.globalEvents as GE from logbook import Logger import calendar -pyfalog = Logger(__name__) - from service.crest import Crest, CrestModes +pyfalog = Logger(__name__) + class CrestFittings(wx.Frame): def __init__(self, parent): diff --git a/gui/devTools.py b/gui/devTools.py index 89d9a6f97..98a14662c 100644 --- a/gui/devTools.py +++ b/gui/devTools.py @@ -21,6 +21,11 @@ import wx from logbook import Logger import gc +import eos +import time +import threading +from gui.builtinShipBrowser.events import FitSelected + pyfalog = Logger(__name__) @@ -48,14 +53,17 @@ class DevTools(wx.Dialog): self.gcCollect.Bind(wx.EVT_BUTTON, self.gc_collect) + self.fitTest = wx.Button(self, wx.ID_ANY, "Test fits", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.fitTest, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + self.fitTest .Bind(wx.EVT_BUTTON, self.fit_test) + self.SetSizer(mainSizer) self.Layout() self.CenterOnParent() self.Show() - - def objects_by_id(self, evt): input = self.id_get.GetValue() if input.startswith("0x"): @@ -77,3 +85,30 @@ class DevTools(wx.Dialog): print(gc.collect()) print(gc.get_debug()) print(gc.get_stats()) + + def fit_test(self, evt): + fits = eos.db.getFitList() + self.thread = FitTestThread([x.ID for x in fits], self.Parent) + self.thread.start() + + +class FitTestThread(threading.Thread): + def __init__(self, fitIDs, mainFrame): + threading.Thread.__init__(self) + self.name = "FitTestThread" + self.mainFrame = mainFrame + self.stopRunning = False + self.fits = fitIDs + + def stop(self): + self.stopRunning = True + + def run(self): + # wait 1 second just in case a lot of modifications get made + if self.stopRunning: + return + + for fit in self.fits: + time.sleep(1) + e = FitSelected(fitID=fit) + wx.PostEvent(self.mainFrame, e) diff --git a/gui/errorDialog.py b/gui/errorDialog.py index 7188efb59..27bf842fb 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -39,7 +39,7 @@ class ErrorHandler(object): with config.logging_setup.threadbound(): # Print the base level traceback t = traceback.format_exception(exc_type, exc_value, exc_traceback) - pyfalog.critical("\n\n"+"".join(t)) + pyfalog.critical("\n\n" + "".join(t)) if cls.__parent is None: app = wx.App(False) @@ -59,8 +59,6 @@ class ErrorHandler(object): class ErrorFrame(wx.Frame): def __init__(self, parent=None, error_title='Error!'): - v = sys.version_info - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.STAY_ON_TOP) @@ -98,7 +96,8 @@ class ErrorFrame(wx.Frame): # mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) - self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version_block.strip(), wx.DefaultPosition, (-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP) + self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version_block.strip(), wx.DefaultPosition, + (-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP) self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5) self.errorTextCtrl.AppendText("\n") @@ -113,9 +112,8 @@ class ErrorFrame(wx.Frame): self.Show() - def OnClose(self, evt): self.Hide() def addException(self, text): - self.errorTextCtrl.AppendText("\n{}\n\n{}".format("#" * 20, text)) \ No newline at end of file + self.errorTextCtrl.AppendText("\n{}\n\n{}".format("#" * 20, text)) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 849c295fe..2aa6bb750 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -137,6 +137,7 @@ class OpenFitsThread(threading.Thread): wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fits[-1], startup=2)) wx.CallAfter(self.callback) + # todo: include IPortUser again class MainFrame(wx.Frame): __instance = None @@ -356,10 +357,9 @@ class MainFrame(wx.Frame): event.Skip() def ShowAboutBox(self, evt): - v = sys.version_info info = wx.adv.AboutDialogInfo() info.Name = "pyfa" - info.Version = config.getGitVersion() # gui.aboutData.versionString + info.Version = config.getGitVersion() # gui.aboutData.versionString # # try: # import matplotlib diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 7fba900e2..be9322010 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -28,11 +28,11 @@ import gui.globalEvents as GE from gui.bitmap_loader import BitmapLoader from logbook import Logger -pyfalog = Logger(__name__) - from service.crest import Crest from service.crest import CrestModes +pyfalog = Logger(__name__) + class MainMenuBar(wx.MenuBar): def __init__(self, mainFrame): diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py index 6e464066b..de0ce1ba8 100644 --- a/gui/pyfa_gauge.py +++ b/gui/pyfa_gauge.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # PyfaGauge is a generic Gauge implementation tailored for pyfa (the Python # Fitting Assistant). It uses the easeOutQuad equation from # caurina.transitions.Tweener to do animations @@ -11,7 +11,7 @@ # ToDo: possibly devise a way to determine transition percents on init # (currently hardcoded) # -#=============================================================================== +# =============================================================================== import copy import wx @@ -52,10 +52,10 @@ class PyGauge(wx.Window): # transition colors used based on how full (or overfilled) the gauge is. self.transition_colors = [ - (wx.Colour(191, 191, 191), wx.Colour(96, 191, 0)), # < 0-100% - (wx.Colour(191, 167, 96), wx.Colour(255, 191, 0)), # < 100-101% - (wx.Colour(255, 191, 0), wx.Colour(255, 128, 0)), # < 101-103% - (wx.Colour(255, 128, 0), wx.Colour(255, 0, 0)) # < 103-105% + (wx.Colour(191, 191, 191), wx.Colour(96, 191, 0)), # < 0-100% + (wx.Colour(191, 167, 96), wx.Colour(255, 191, 0)), # < 100-101% + (wx.Colour(255, 191, 0), wx.Colour(255, 128, 0)), # < 101-103% + (wx.Colour(255, 128, 0), wx.Colour(255, 0, 0)) # < 103-105% ] self.gradient_effect = -35 @@ -91,19 +91,18 @@ class PyGauge(wx.Window): def GetBorderColour(self): return self._border_colour - def SetBorderColour(self, colour: wx.Colour): + def SetBorderColour(self, colour): self._border_colour = colour def GetBarColour(self): return self._bar_colour - def SetBarColour(self, colour: wx.Colour=None): + def SetBarColour(self, colour): self._bar_colour = colour def SetFractionDigits(self, digits): self._fraction_digits = digits - def GetBarGradient(self): if self._bar_gradient is None: return None @@ -119,7 +118,7 @@ class PyGauge(wx.Window): else: self._bar_gradient = list(gradient) - def GetBorderPadding(self) -> int: + def GetBorderPadding(self): return self._border_padding def SetBorderPadding(self, padding): @@ -153,7 +152,7 @@ class PyGauge(wx.Window): if reinit is False: self._old_percentage = self._percentage - self._percentage = (self._value/self._max_range) * 100 + self._percentage = (self._value / self._max_range) * 100 else: self._old_percentage = self._percentage self._percentage = 0 @@ -162,7 +161,7 @@ class PyGauge(wx.Window): if animate: self.Animate() - self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range if self._max_range >0.01 else 0)) + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range if self._max_range > 0.01 else 0)) def GetValue(self): return self._value @@ -178,7 +177,7 @@ class PyGauge(wx.Window): if value < 0: self._value = 0 - self._percentage = (self._value/self._max_range) * 100 + self._percentage = (self._value / self._max_range) * 100 if animate: self.Animate() @@ -270,16 +269,16 @@ class PyGauge(wx.Window): # progress between the two transition ranges) pv = value if pv <= 100: - xv = pv/100 + xv = pv / 100 transition = 0 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 @@ -301,7 +300,7 @@ class PyGauge(wx.Window): gradient_mid = color_utils.Brighten(color, mid_factor) else: gradient_color = color_utils.Darken(color, color_factor * -1) - gradient_mid = color_utils.Darken(color, mid_factor * -1) + gradient_mid = color_utils.Darken(color, mid_factor * -1) # draw bar gradient_bitmap = draw.DrawGradientBar( @@ -389,6 +388,7 @@ class PyGauge(wx.Window): self.Refresh() + if __name__ == "__main__": def frange(x, y, jump): while x < y: @@ -437,9 +437,7 @@ if __name__ == "__main__": main_sizer.Add(panel) self.SetSizer(main_sizer) - app = wx.App(redirect=False) # Error messages go to popup window top = Frame("Test Chrome Tabs") top.Show() app.MainLoop() - diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 7163a3830..2b12edd8c 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -11,7 +11,7 @@ from gui.builtinShipBrowser.shipItem import ShipItem from service.fit import Fit from service.market import Market -from gui.builtinShipBrowser.events import * +from gui.builtinShipBrowser.events import EVT_SB_IMPORT_SEL, EVT_SB_STAGE1_SEL, EVT_SB_STAGE2_SEL, EVT_SB_STAGE3_SEL, EVT_SB_SEARCH_SEL from gui.builtinShipBrowser.pfWidgetContainer import PFWidgetsContainer from gui.builtinShipBrowser.navigationPanel import NavigationPanel from gui.builtinShipBrowser.raceSelector import RaceSelector diff --git a/gui/toggle_panel.py b/gui/toggle_panel.py index 0813ae26d..2f7421d38 100644 --- a/gui/toggle_panel.py +++ b/gui/toggle_panel.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # TogglePanel is based on PyCollapsiblePane - includes a few improvements # such as adding items to header, lack of button implementation ("GTK # expander" style is implemented with plain text with unicode arrows rather @@ -10,7 +10,7 @@ # # ToDo: Create animations for collapsing / expanding # -#=============================================================================== +# =============================================================================== import wx @@ -38,7 +38,7 @@ class TogglePanel (wx.Panel): # Add arrow self.header_arrow = wx.StaticText(self.header_panel, wx.ID_ANY, "\u25bc", size=wx.Size((10, -1))) - header_sizer.Add(self.header_arrow, 0, wx.RIGHT, 5) + header_sizer.Add(self.header_arrow, 0, wx.RIGHT, 5) # Add header text self.header_label = wx.StaticText(self.header_panel, wx.ID_ANY, "") @@ -120,7 +120,7 @@ class TogglePanel (wx.Panel): self.parent.Fit() def ToggleContent(self, event): - #self.Freeze() + # self.Freeze() if self._toggled: # If we are expanded, save previous size and collapse by setting @@ -135,13 +135,14 @@ class TogglePanel (wx.Panel): self._toggled = not self._toggled - #self.Thaw() + # self.Thaw() if self.force_layout: self.parent.Layout() else: self.OnStateChange(self.GetBestSize()) + if __name__ == "__main__": from wx.lib.inspection import InspectionTool @@ -166,7 +167,7 @@ if __name__ == "__main__": content_sizer = wx.BoxSizer(wx.HORIZONTAL) header = wx.StaticText(content_panel, -1, "TogglePanel Test") - header.SetFont(wx.Font(10+(x*3), wx.SWISS, wx.NORMAL, wx.BOLD)) + header.SetFont(wx.Font(10 + (x * 3), wx.SWISS, wx.NORMAL, wx.BOLD)) content_sizer.Add(header, 0, wx.ALL, 10) content_panel.SetSizer(content_sizer) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 9adaa3594..83b8426b3 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -71,7 +71,6 @@ class UpdateDialog(wx.Dialog): versionSizer.Add(self.versionText, 1, wx.ALL, 5) - mainSizer.Add(versionSizer, 0, wx.EXPAND, 0) releaseDate = dateutil.parser.parse(self.releaseInfo['published_at']) diff --git a/gui/utils/color.py b/gui/utils/color.py index 4b44e77a4..f89b56d7a 100644 --- a/gui/utils/color.py +++ b/gui/utils/color.py @@ -2,7 +2,7 @@ import wx -def Brighten(color: wx.Colour, factor: [0, 1]): +def Brighten(color, factor): """ Brightens a Color using a factor between 0 and 1""" r, g, b, a = color @@ -15,7 +15,7 @@ def Brighten(color: wx.Colour, factor: [0, 1]): return wx.Colour(r, g, b, a) -def Darken(color: wx.Colour, factor: [0, 1]): +def Darken(color, factor): """ Darkens a Color using a factor between 0 and 1""" r, g, b, a = color @@ -33,16 +33,16 @@ def Darken(color: wx.Colour, factor: [0, 1]): return wx.Colour(r, g, b, a) -def _getBrightness(color: wx.Colour): +def _getBrightness(color): """ Calculates brightness of color http://stackoverflow.com/a/596243/788054 """ r, g, b, a = color - return 0.299*r + 0.587*g + 0.114*b + return 0.299 * r + 0.587 * g + 0.114 * b -def GetSuitable(color: wx.Colour, factor: [0, 1]): +def GetSuitable(color, factor: [0, 1]): """ Calculates a suitable color based on original color (wx.Colour), its brightness, and a factor (darken/brighten by factor depending on @@ -57,7 +57,7 @@ def GetSuitable(color: wx.Colour, factor: [0, 1]): return Brighten(color, factor) -def CalculateTransition(s_color: wx.Colour, e_color: wx.Colour, delta: [0, 1]): +def CalculateTransition(s_color, e_color, delta): """ Calculates the color between a given start and end color using a delta value between 0 and 1 @@ -70,4 +70,4 @@ def CalculateTransition(s_color: wx.Colour, e_color: wx.Colour, delta: [0, 1]): tG = sG + (eG - sG) * delta tB = sB + (eB - sB) * delta - return wx.Colour(tR, tG, tB, (sA + eA)/2) + return wx.Colour(tR, tG, tB, (sA + eA) / 2) diff --git a/gui/utils/draw.py b/gui/utils/draw.py index 7079e4058..992dbb9fb 100644 --- a/gui/utils/draw.py +++ b/gui/utils/draw.py @@ -6,14 +6,14 @@ from . import color 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) + return DrawFilledBitmap(width, height, windowColor) gStart = color.GetSuitable(windowColor, sFactor) if mFactor: gMid = color.GetSuitable(windowColor, mFactor) else: - gMid = color.GetSuitable(windowColor, sFactor + (eFactor - sFactor) / 2) + gMid = color.GetSuitable(windowColor, sFactor + (eFactor - sFactor) / 2) gEnd = color.GetSuitable(windowColor, eFactor) @@ -21,7 +21,7 @@ def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor=None def DrawFilledBitmap(width, height, color): - canvas = wx.Bitmap(width,height) + canvas = wx.Bitmap(width, height) mdc = wx.MemoryDC() mdc.SelectObject(canvas) @@ -33,8 +33,9 @@ def DrawFilledBitmap(width, height, color): return canvas + def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4): - canvas = wx.Bitmap(width,height) + canvas = wx.Bitmap(width, height) mdc = wx.MemoryDC() mdc.SelectObject(canvas) @@ -47,7 +48,7 @@ def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4): mdc.GradientFillLinear(r, gStart, gMid, wx.SOUTH) r.SetTop(r.GetHeight()) - r.SetHeight(height * (fillRatio - 1)/fillRatio + (1 if height % fillRatio != 0 else 0)) + r.SetHeight(height * (fillRatio - 1) / fillRatio + (1 if height % fillRatio != 0 else 0)) mdc.GradientFillLinear(r, gMid, gEnd, wx.SOUTH) diff --git a/pyfa.py b/pyfa.py index 4c3509683..889178b6e 100755 --- a/pyfa.py +++ b/pyfa.py @@ -28,22 +28,23 @@ import config ascii_text = ''' ++++++++++++++++++++++++++++++++++++++++++++++++++ - __ - / _| - _ __ _ _ | |_ __ _ + + / _| + _ __ _ _ | | | '_ \ | | | || _|/ _` | | |_) || |_| || | | (_| | | .__/ \__, ||_| \__,_| - | | __/ | - |_| |___/ + | | __/ | + |_| |___/ -You are running a alpha/beta version of pyfa. +You are running a alpha/beta version of pyfa. ++++++++++++++++++++++++++++++++++++++++++++++++++ ''' print(ascii_text) + class PassThroughOptionParser(OptionParser): """ An unknown option pass-through implementation of OptionParser. @@ -59,6 +60,7 @@ class PassThroughOptionParser(OptionParser): # pyfalog.error("Bad startup option passed.") largs.append(e.opt_str) + # Parse command line options usage = "usage: %prog [--root]" parser = PassThroughOptionParser(usage=usage) diff --git a/service/eveapi.py b/service/eveapi.py index 260b8afc1..a06cf2efe 100644 --- a/service/eveapi.py +++ b/service/eveapi.py @@ -723,7 +723,8 @@ _fmt = "%s:%s".__mod__ def _cmp(self, a, b): - return (a>b)-(a b) - (a < b) + class Row(object): # A Row is a single database record associated with a Rowset. @@ -746,7 +747,6 @@ class Row(object): def __eq__(self, other): return self.__cmp__(other) == 0 - def __cmp__(self, other): if type(other) != type(self): raise TypeError("Incompatible comparison type") diff --git a/service/network.py b/service/network.py index 924e2940b..9befd3fff 100644 --- a/service/network.py +++ b/service/network.py @@ -18,8 +18,9 @@ # ============================================================================= -import urllib.request, urllib.error, urllib.parse -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.error +import urllib.parse import socket from logbook import Logger diff --git a/service/port.py b/service/port.py index 066ff12e2..c61237b5c 100644 --- a/service/port.py +++ b/service/port.py @@ -50,9 +50,9 @@ from utils.strfunctions import sequential_rep, replace_ltgt from abc import ABCMeta, abstractmethod from service.crest import Crest +from collections import OrderedDict pyfalog = Logger(__name__) -from collections import OrderedDict EFT_SLOT_ORDER = [Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM] INV_FLAGS = { diff --git a/service/prereqsCheck.py b/service/prereqsCheck.py index ce8b95083..f0e064796 100644 --- a/service/prereqsCheck.py +++ b/service/prereqsCheck.py @@ -5,9 +5,11 @@ import platform version_block = '' + class PreCheckException(Exception): pass + class PreCheckMessage(): def __init__(self, msg): # wx may not be installed, in which case print to console. For all other prechecks, should pop up a MessageDialog @@ -21,6 +23,7 @@ class PreCheckMessage(): finally: print(msg) + def version_precheck(): global version_block @@ -80,4 +83,4 @@ def version_precheck(): version_block += "\nDateutil version: {}".format(dateutil.__version__) except: msg = "pyfa requires the python-dateutil module. You can download python-dateutil form https://pypi.python.org/pypi/python-dateutil" - raise PreCheckException(msg) \ No newline at end of file + raise PreCheckException(msg) diff --git a/service/settings.py b/service/settings.py index 8550bc267..7517255c5 100644 --- a/service/settings.py +++ b/service/settings.py @@ -19,7 +19,9 @@ import pickle import os.path -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error +import urllib.parse import config import eos.config From 29d175c7b3f1c800878ce9978c917fa3fbf77057 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 10 Jan 2018 00:51:21 -0500 Subject: [PATCH 101/212] Fix for error that happens when switching to/from firepower/mining --- gui/builtinStatsViews/firepowerViewFull.py | 4 ++-- gui/builtinStatsViews/miningyieldViewFull.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 1ab60cce3..98d54d714 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -128,8 +128,8 @@ class FirepowerViewFull(StatsView): # Remove effective label hsizer = self.headerPanel.GetSizer() - hsizer.Remove(self.stEff) - self.stEff.Destroy() + hsizer.Hide(self.stEff) + #self.stEff.Destroy() # Get the new view view = StatsView.getView("miningyieldViewFull")(self.parent) diff --git a/gui/builtinStatsViews/miningyieldViewFull.py b/gui/builtinStatsViews/miningyieldViewFull.py index b25c7f460..7793a2850 100644 --- a/gui/builtinStatsViews/miningyieldViewFull.py +++ b/gui/builtinStatsViews/miningyieldViewFull.py @@ -123,7 +123,7 @@ class MiningYieldViewFull(StatsView): # Get the TogglePanel tp = self.panel.GetParent() # Bind the new panel's children to allow context menu access - self.parent.applyBinding(self.parent, tp.GetContentPane()) + self.parent.applyBinding(self.parent, tp.content_panel) tp.SetLabel(view.getHeaderText(fit)) view.refreshPanel(fit) From 9f7e4e0dc0940cb047343729fffe0155646b43a8 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 10 Jan 2018 21:39:50 -0500 Subject: [PATCH 102/212] Fix issue with market search (None / int comparison) --- gui/builtinMarketBrowser/itemView.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py index f2b5a7627..eb0d6bb51 100644 --- a/gui/builtinMarketBrowser/itemView.py +++ b/gui/builtinMarketBrowser/itemView.py @@ -204,13 +204,14 @@ class ItemView(Display): try: mktgrpid = sMkt.getMarketGroupByItem(item).ID except AttributeError: - mktgrpid = None + mktgrpid = -1 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): From 9e8166c13d94e0eeb35c2f1a6ccff08db30c88d4 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 11 Jan 2018 20:54:33 -0500 Subject: [PATCH 103/212] Add ESI service skeleton, and my customer ESI Proxy class --- requirements.txt | 1 + service/esi.py | 46 +++++++ service/esi_security_proxy.py | 238 ++++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 service/esi.py create mode 100644 service/esi_security_proxy.py diff --git a/requirements.txt b/requirements.txt index 694a2d348..8216804cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ python-dateutil urllib3 requests == 2.0.0 sqlalchemy == 1.0.5 +esipy == 0.3.0 \ No newline at end of file diff --git a/service/esi.py b/service/esi.py new file mode 100644 index 000000000..0125c42c4 --- /dev/null +++ b/service/esi.py @@ -0,0 +1,46 @@ +# noinspection PyPackageRequirements +import wx +from logbook import Logger +import threading +import copy +import uuid +import time + +import eos.db +from eos.enum import Enum +from eos.saveddata.crestchar import CrestChar +import gui.globalEvents as GE +from service.settings import CRESTSettings +from service.server import StoppableHTTPServer, AuthHandler +from service.pycrest.eve import EVE + +pyfalog = Logger(__name__) + + +class Servers(Enum): + TQ = 0 + SISI = 1 + + +class CrestModes(Enum): + IMPLICIT = 0 + USER = 1 + + +class ESI(object): + # @todo: move this to settings + clientCallback = 'http://localhost:6461' + clientTest = True + + _instance = None + + @classmethod + def getInstance(cls): + if cls._instance is None: + cls._instance = ESI() + + return cls._instance + + + def __init__(self): + pass diff --git a/service/esi_security_proxy.py b/service/esi_security_proxy.py new file mode 100644 index 000000000..f6263d5e8 --- /dev/null +++ b/service/esi_security_proxy.py @@ -0,0 +1,238 @@ +# -*- encoding: utf-8 -*- +""" EsiPy Security Proxy - An ESI Security class that directs authentication towards a third-party service. +Client key/secret not needed. +""" + +from __future__ import absolute_import + +import base64 +import logging +import time + +from requests import Session +from requests.utils import quote +from six.moves.urllib.parse import urlparse + +from esipy.events import AFTER_TOKEN_REFRESH +from esipy.exceptions import APIException +LOGGER = logging.getLogger(__name__) + + +class EsiSecurityProxy(object): + """ Contains all the OAuth2 knowledge for ESI use. + Based on pyswagger Security object, to be used with pyswagger BaseClient + implementation. + """ + + def __init__( + self, + redirect_uri, + **kwargs): + """ Init the ESI Security Object + + :param redirect_uri: the uri to redirect the user after login into SSO + :param sso_url: the default sso URL used when no "app" is provided + :param esi_url: the default esi URL used for verify endpoint + :param app: (optionnal) the pyswagger app object + :param security_name: (optionnal) the name of the object holding the + informations in the securityDefinitions, used to check authed endpoint + """ + + app = kwargs.pop('app', None) + sso_url = kwargs.pop('sso_url', "https://login.eveonline.com") + esi_url = kwargs.pop('esi_url', "https://esi.tech.ccp.is") + + self.security_name = kwargs.pop('security_name', 'evesso') + self.redirect_uri = redirect_uri + + # we provide app object, so we don't use sso_url + if app is not None: + # check if the security_name exists in the securityDefinition + security = app.root.securityDefinitions.get( + self.security_name, + None + ) + if security is None: + raise NameError( + "%s is not defined in the securityDefinitions" % + self.security_name + ) + + self.oauth_authorize = security.authorizationUrl + + # some URL we still need to "manually" define... sadly + # we parse the authUrl so we don't care if it's TQ or SISI. + # https://github.com/ccpgames/esi-issues/issues/92 + parsed_uri = urlparse(security.authorizationUrl) + self.oauth_token = '%s://%s/oauth/token' % ( + parsed_uri.scheme, + parsed_uri.netloc + ) + + # no app object is provided, so we use direct URLs + else: + if sso_url is None or sso_url == "": + raise AttributeError("sso_url cannot be None or empty " + "without app parameter") + + self.oauth_authorize = '%s/oauth/authorize' % sso_url + self.oauth_token = '%s/oauth/token' % sso_url + + # use ESI url for verify, since it's better for caching + if esi_url is None or esi_url == "": + raise AttributeError("esi_url cannot be None or empty") + self.oauth_verify = '%s/verify/' % esi_url + + # session request stuff + self._session = Session() + self._session.headers.update({ + 'Accept': 'application/json', + 'User-Agent': ( + 'EsiPy/Security/ - ' + 'https://github.com/Kyria/EsiPy' + ) + }) + + # token data + self.refresh_token = None + self.access_token = None + self.token_expiry = None + + def __get_oauth_header(self): + """ Return the Bearer Authorization header required in oauth calls + + :return: a dict with the authorization header + """ + return {'Authorization': 'Bearer %s' % self.access_token} + + def __make_token_request_parameters(self, params): + """ Return the token uri from the securityDefinition + + :param params: the data given to the request + :return: the oauth/token uri + """ + request_params = { + 'data': params, + 'url': self.oauth_token, + } + + return request_params + + def get_auth_uri(self, state=None, redirect='http://localhost:8080'): + """ Constructs the full auth uri and returns it. + + :param state: The state to pass through the auth process + :param redirect: The URI that the proxy server will redirect to + :return: the authorizationUrl with the correct parameters. + """ + + return '%s?redirect=%s%s' % ( + self.oauth_authorize, + quote(redirect, safe=''), + '&state=%s' % state if state else '' + ) + + def get_refresh_token_params(self): + """ Return the param object for the post() call to get the access_token + from the refresh_token + + :param code: the refresh token + :return: a dict with the url, params and header + """ + if self.refresh_token is None: + raise AttributeError('No refresh token is defined.') + + return self.__make_token_request_parameters( + { + 'grant_type': 'refresh_token', + 'refresh_token': self.refresh_token, + } + ) + + def update_token(self, response_json): + """ Update access_token, refresh_token and token_expiry from the + response body. + The response must be converted to a json object before being passed as + a parameter + + :param response_json: the response body to use. + """ + self.access_token = response_json['access_token'] + self.token_expiry = int(time.time()) + response_json['expires_in'] + + if 'refresh_token' in response_json: + self.refresh_token = response_json['refresh_token'] + + def is_token_expired(self, offset=0): + """ Return true if the token is expired. + + The offset can be used to change the expiry time: + - positive value decrease the time (sooner) + - negative value increase the time (later) + If the expiry is not set, always return True. This case allow the users + to define a security object, only knowing the refresh_token and get + a new access_token / expiry_time without errors. + + :param offset: the expiry offset (in seconds) [default: 0] + :return: boolean true if expired, else false. + """ + if self.token_expiry is None: + return True + return int(time.time()) >= (self.token_expiry - offset) + + def refresh(self): + """ Update the auth data (tokens) using the refresh token in auth. + """ + request_data = self.get_refresh_token_params() + res = self._session.post(**request_data) + if res.status_code != 200: + raise APIException( + request_data['url'], + res.status_code, + res.json() + ) + json_res = res.json() + self.update_token(json_res) + return json_res + + def verify(self): + """ Make a get call to the oauth/verify endpoint to get the user data + + :return: the json with the data. + """ + res = self._session.get( + self.oauth_verify, + headers=self.__get_oauth_header() + ) + if res.status_code != 200: + raise APIException( + self.oauth_verify, + res.status_code, + res.json() + ) + return res.json() + + def __call__(self, request): + """ Check if the request need security header and apply them. + Required for pyswagger.core.BaseClient.request(). + + :param request: the pyswagger request object to check + :return: the updated request. + """ + if not request._security: + return request + + if self.is_token_expired(): + json_response = self.refresh() + AFTER_TOKEN_REFRESH.send(**json_response) + + for security in request._security: + if self.security_name not in security: + LOGGER.warning( + "Missing Securities: [%s]" % ", ".join(security.keys()) + ) + continue + if self.access_token is not None: + request._p['header'].update(self.__get_oauth_header()) + + return request From 3179016aeda76922973f25976145e29c673c80bf Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 1 Feb 2018 21:58:29 -0500 Subject: [PATCH 104/212] Enhance the update modal to show a web view with html-rendered markdown of release ntoes --- gui/updateDialog.py | 89 +++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 83b8426b3..6713bec7d 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -22,12 +22,32 @@ import wx # noinspection PyPackageRequirements import dateutil.parser from service.settings import UpdateSettings as svc_UpdateSettings +import wx.html2 +import webbrowser +import re +import markdown2 + +# HTML template. We link to a bootstrap cdn for quick and easy css, and include some additional teaks. +html_tmpl = """ + + +

    pyfa {0}

    +

    {1}

    +
    +{2} +{3} +""" 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 Available", pos=wx.DefaultPosition, + size=wx.Size(550, 450), style=wx.DEFAULT_DIALOG_STYLE) self.UpdateSettings = svc_UpdateSettings.getInstance() self.releaseInfo = release @@ -35,51 +55,28 @@ class UpdateDialog(wx.Dialog): mainSizer = wx.BoxSizer(wx.VERTICAL) - 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)) - - 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) - - 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)) - 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.Wrap(-1) - - 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)) - - versionSizer.Add(self.versionText, 1, wx.ALL, 5) - - mainSizer.Add(versionSizer, 0, wx.EXPAND, 0) - 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) + self.browser = wx.html2.WebView.New(self) + self.browser.Bind(wx.html2.EVT_WEBVIEW_NEWWINDOW, self.OnNewWindow) - notesSizer.Add(self.notesTextCtrl, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) + link_patterns = [ + (re.compile("#(\d+)", re.I), r"https://github.com/pyfa-org/Pyfa/issues/\1"), + (re.compile("@(\w+)", re.I), r"https://github.com/\1") + ] + + markdowner = markdown2.Markdown( + extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'], + link_patterns=link_patterns) + + self.browser.SetPage(html_tmpl.format( + self.releaseInfo['tag_name'], + releaseDate.strftime('%B %d, %Y'), + "

    This is a pre-release, be prepared for unstable features

    " if self.releaseInfo['prerelease'] else "", + markdowner.convert(self.releaseInfo['body']) + ),"") + + notesSizer.Add(self.browser, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) mainSizer.Add(notesSizer, 1, wx.EXPAND, 5) self.supressCheckbox = wx.CheckBox(self, wx.ID_ANY, "Don't remind me again for this release", @@ -117,6 +114,10 @@ class UpdateDialog(wx.Dialog): def OnClose(self, e): self.Close() + def OnNewWindow(self, event): + url = event.GetURL() + webbrowser.open(url) + def SuppressChange(self, e): if self.supressCheckbox.IsChecked(): self.UpdateSettings.set('version', self.releaseInfo['tag_name']) From 2e8d7d36103afad3fa65f2ccf79187fcc126fbc3 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 1 Feb 2018 23:09:08 -0500 Subject: [PATCH 105/212] add markdown2 to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 694a2d348..e57add8c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ python-dateutil urllib3 requests == 2.0.0 sqlalchemy == 1.0.5 +markdown2 \ No newline at end of file From 23761483807f1c241cfa3094b61a27b7e8fa1729 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 2 Feb 2018 02:05:49 -0500 Subject: [PATCH 106/212] Start ripping CREST stuff out, add new SSOCharacter stuff in. Long process ahead. --- config.py | 5 +++ eos/db/saveddata/crest.py | 18 +++++++---- eos/db/saveddata/queries.py | 16 +++++----- eos/saveddata/crestchar.py | 34 -------------------- eos/saveddata/ssocharacter.py | 60 +++++++++++++++++++++++++++++++++++ gui/mainFrame.py | 12 ++----- gui/mainMenuBar.py | 18 +++++------ service/crest.py | 22 ++++--------- service/esi.py | 2 +- 9 files changed, 101 insertions(+), 86 deletions(-) delete mode 100644 eos/saveddata/crestchar.py create mode 100644 eos/saveddata/ssocharacter.py diff --git a/config.py b/config.py index 339a19e0a..1127b72ae 100644 --- a/config.py +++ b/config.py @@ -3,6 +3,7 @@ import sys from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, \ StreamHandler, TimedRotatingFileHandler, WARNING +import hashlib pyfalog = Logger(__name__) @@ -43,6 +44,10 @@ LOGLEVEL_MAP = { } +def getClientSecret(): + return hashlib.sha3_256("This is a secret, this will not remain in here for long".encode('utf-8')).hexdigest() + + def isFrozen(): if hasattr(sys, 'frozen'): return True diff --git a/eos/db/saveddata/crest.py b/eos/db/saveddata/crest.py index 28f77a983..035cdb60d 100644 --- a/eos/db/saveddata/crest.py +++ b/eos/db/saveddata/crest.py @@ -22,13 +22,17 @@ from sqlalchemy.orm import mapper import datetime from eos.db import saveddata_meta -from eos.saveddata.crestchar import CrestChar +from eos.saveddata.ssocharacter import SsoCharacter -crest_table = Table("crest", saveddata_meta, +sso_table = Table("ssoCharacter", saveddata_meta, Column("ID", Integer, primary_key=True), - Column("name", String, nullable=False, unique=True), - Column("refresh_token", String, nullable=False), - # These records aren't updated. Instead, they are dropped and created, hence we don't have a modified field - Column("created", DateTime, nullable=True, default=datetime.datetime.now)) + Column("client", String, nullable=False), + Column("characterID", Integer, nullable=False, unique=True), + Column("characterName", String, nullable=False, unique=True), + Column("refreshToken", String, nullable=False), + Column("accessToken", String, nullable=False), + Column("accessTokenExpires", DateTime, nullable=False), + Column("created", DateTime, nullable=True, default=datetime.datetime.now), + Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)) -mapper(CrestChar, crest_table) +mapper(SsoCharacter, sso_table) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 98902007f..b3c1840e8 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -27,7 +27,7 @@ from eos.db.saveddata.fit import projectedFits_table from eos.db.util import processEager, processWhere from eos.saveddata.price import Price from eos.saveddata.user import User -from eos.saveddata.crestchar import CrestChar +from eos.saveddata.ssocharacter import SsoCharacter from eos.saveddata.damagePattern import DamagePattern from eos.saveddata.targetResists import TargetResists from eos.saveddata.character import Character @@ -467,27 +467,27 @@ def getProjectedFits(fitID): raise TypeError("Need integer as argument") -def getCrestCharacters(eager=None): +def getSsoCharacters(eager=None): eager = processEager(eager) with sd_lock: - characters = saveddata_session.query(CrestChar).options(*eager).all() + characters = saveddata_session.query(SsoCharacter).options(*eager).all() return characters -@cachedQuery(CrestChar, 1, "lookfor") -def getCrestCharacter(lookfor, eager=None): +@cachedQuery(SsoCharacter, 1, "lookfor") +def getSsoCharacter(lookfor, eager=None): if isinstance(lookfor, int): if eager is None: with sd_lock: - character = saveddata_session.query(CrestChar).get(lookfor) + character = saveddata_session.query(SsoCharacter).get(lookfor) else: eager = processEager(eager) with sd_lock: - character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.ID == lookfor).first() + character = saveddata_session.query(SsoCharacter).options(*eager).filter(SsoCharacter.ID == lookfor).first() elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: - character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.name == lookfor).first() + character = saveddata_session.query(SsoCharacter).options(*eager).filter(SsoCharacter.name == lookfor).first() else: raise TypeError("Need integer or string as argument") return character diff --git a/eos/saveddata/crestchar.py b/eos/saveddata/crestchar.py deleted file mode 100644 index ce6ab14fe..000000000 --- a/eos/saveddata/crestchar.py +++ /dev/null @@ -1,34 +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 . -# =============================================================================== - -from sqlalchemy.orm import reconstructor - - -# from tomorrow import threads - - -class CrestChar(object): - def __init__(self, id, name, refresh_token=None): - self.ID = id - self.name = name - self.refresh_token = refresh_token - - @reconstructor - def init(self): - pass diff --git a/eos/saveddata/ssocharacter.py b/eos/saveddata/ssocharacter.py new file mode 100644 index 000000000..8fca830cb --- /dev/null +++ b/eos/saveddata/ssocharacter.py @@ -0,0 +1,60 @@ +# =============================================================================== +# 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 sqlalchemy.orm import reconstructor +import datetime +import time + +# from tomorrow import threads + + +class SsoCharacter(object): + def __init__(self, id, charID, name, client, accessToken, refreshToken=None): + self.ID = id + self.characterID = charID + self.characterName = name + self.client = client + self.accessToken = accessToken + self.refreshToken = refreshToken + + @reconstructor + def init(self): + pass + + def get_sso_data(self): + """ Little "helper" function to get formated data for esipy security + """ + return { + 'access_token': self.accessToken, + 'refresh_token': self.refreshToken, + 'expires_in': ( + self.accessTokenExpires - datetime.utcnow() + ).total_seconds() + } + + def update_token(self, tokenResponse): + """ helper function to update token data from SSO response """ + self.accessToken = tokenResponse['access_token'] + self.accessTokenExpires = datetime.fromtimestamp( + time.time() + tokenResponse['expires_in'], + ) + if 'refresh_token' in tokenResponse: + self.refreshToken = tokenResponse['refresh_token'] + if self.esi_client is not None: + self.esi_client.security.update_token(tokenResponse) \ No newline at end of file diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 2aa6bb750..735db0d80 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -662,16 +662,8 @@ class MainFrame(wx.Frame): menu.Enable(menu.exportToEveId, not enable) def ssoHandler(self, event): - sCrest = Crest.getInstance() - if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - if sCrest.implicitCharacter is not None: - sCrest.logout() - else: - uri = sCrest.startServer() - webbrowser.open(uri) - else: - dlg = CrestMgmt(self) - dlg.Show() + dlg = CrestMgmt(self) + dlg.Show() def exportToEve(self, event): dlg = ExportToEve(self) diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index be9322010..8b5b77e60 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -28,8 +28,8 @@ import gui.globalEvents as GE from gui.bitmap_loader import BitmapLoader from logbook import Logger -from service.crest import Crest -from service.crest import CrestModes +# from service.crest import Crest +# from service.crest import CrestModes pyfalog = Logger(__name__) @@ -134,21 +134,19 @@ class MainMenuBar(wx.MenuBar): preferencesItem.SetBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) windowMenu.Append(preferencesItem) - self.sCrest = Crest.getInstance() + # self.sCrest = Crest.getInstance() # CREST Menu crestMenu = wx.Menu() self.Append(crestMenu, "&CREST") - if self.sCrest.settings.get('mode') != CrestModes.IMPLICIT: - crestMenu.Append(self.ssoLoginId, "Manage Characters") - else: - crestMenu.Append(self.ssoLoginId, "Login to EVE") + + crestMenu.Append(self.ssoLoginId, "Manage Characters") crestMenu.Append(self.eveFittingsId, "Browse EVE Fittings") crestMenu.Append(self.exportToEveId, "Export To EVE") - if self.sCrest.settings.get('mode') == CrestModes.IMPLICIT or len(self.sCrest.getCrestCharacters()) == 0: - self.Enable(self.eveFittingsId, False) - self.Enable(self.exportToEveId, False) + # if self.sCrest.settings.get('mode') == CrestModes.IMPLICIT or len(self.sCrest.getCrestCharacters()) == 0: + self.Enable(self.eveFittingsId, False) + self.Enable(self.exportToEveId, False) if not self.mainFrame.disableOverrideEditor: windowMenu.AppendSeparator() diff --git a/service/crest.py b/service/crest.py index ff1765298..51c9f1309 100644 --- a/service/crest.py +++ b/service/crest.py @@ -8,7 +8,7 @@ import time import eos.db from eos.enum import Enum -from eos.saveddata.crestchar import CrestChar +from eos.saveddata.ssocharacter import SsoCharacter import gui.globalEvents as GE from service.settings import CRESTSettings from service.server import StoppableHTTPServer, AuthHandler @@ -100,40 +100,30 @@ class Crest(object): return self.settings.get('server') == Servers.SISI def delCrestCharacter(self, charID): - char = eos.db.getCrestCharacter(charID) + char = eos.db.getSsoCharacter(charID) del self.charCache[char.ID] eos.db.remove(char) wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=len(self.charCache))) def delAllCharacters(self): - chars = eos.db.getCrestCharacters() + chars = eos.db.getSsoCharacters() for char in chars: eos.db.remove(char) self.charCache = {} wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=0)) def getCrestCharacters(self): - chars = eos.db.getCrestCharacters() - # I really need to figure out that DB cache problem, this is ridiculous - chars2 = [self.getCrestCharacter(char.ID) for char in chars] - return chars2 + chars = eos.db.getSsoCharacters() + return chars 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.") - return self.implicitCharacter - if charID in self.charCache: return self.charCache.get(charID) - char = eos.db.getCrestCharacter(charID) - if char and not hasattr(char, "eve"): - char.eve = EVE(**self.eve_options) - char.eve.temptoken_authorize(refresh_token=char.refresh_token) + char = eos.db.getSsoCharacter(charID) self.charCache[charID] = char return char diff --git a/service/esi.py b/service/esi.py index 0125c42c4..0f375a591 100644 --- a/service/esi.py +++ b/service/esi.py @@ -8,7 +8,7 @@ import time import eos.db from eos.enum import Enum -from eos.saveddata.crestchar import CrestChar +from eos.saveddata.ssocharacter import CrestChar import gui.globalEvents as GE from service.settings import CRESTSettings from service.server import StoppableHTTPServer, AuthHandler From c7360c8cc36c1ef5c46ba806bdd426e78901a408 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 7 Feb 2018 00:44:37 -0500 Subject: [PATCH 107/212] Get logging into EVE working and SSO characters saving, along with client hashes being stored alongside characters. --- config.py | 3 + eos/db/saveddata/crest.py | 11 ++-- eos/db/saveddata/queries.py | 27 +++++---- eos/saveddata/ssocharacter.py | 6 +- gui/crestFittings.py | 8 ++- service/crest.py | 105 +++++++++++++++++++--------------- service/esi_security_proxy.py | 3 - service/server.py | 10 ++-- 8 files changed, 95 insertions(+), 78 deletions(-) diff --git a/config.py b/config.py index 1127b72ae..bac448989 100644 --- a/config.py +++ b/config.py @@ -35,6 +35,9 @@ logPath = None loggingLevel = None logging_setup = None +ESI_AUTH_PROXY = "http://localhost:5015" # "https://blitzmann.pythonanywhere.com" // need to get this set up, and actually put on it's own domain +ESI_CACHE = 'esi_cache' + LOGLEVEL_MAP = { "critical": CRITICAL, "error": ERROR, diff --git a/eos/db/saveddata/crest.py b/eos/db/saveddata/crest.py index 035cdb60d..429da243d 100644 --- a/eos/db/saveddata/crest.py +++ b/eos/db/saveddata/crest.py @@ -17,7 +17,7 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy import Table, Column, Integer, String, DateTime +from sqlalchemy import Table, Column, Integer, String, DateTime, UniqueConstraint from sqlalchemy.orm import mapper import datetime @@ -27,12 +27,15 @@ from eos.saveddata.ssocharacter import SsoCharacter sso_table = Table("ssoCharacter", saveddata_meta, Column("ID", Integer, primary_key=True), Column("client", String, nullable=False), - Column("characterID", Integer, nullable=False, unique=True), - Column("characterName", String, nullable=False, unique=True), + Column("characterID", Integer, nullable=False), + Column("characterName", String, nullable=False), Column("refreshToken", String, nullable=False), Column("accessToken", String, nullable=False), Column("accessTokenExpires", DateTime, nullable=False), Column("created", DateTime, nullable=True, default=datetime.datetime.now), - Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)) + Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now), + UniqueConstraint('client', 'characterID', name='uix_client_characterID'), + UniqueConstraint('client', 'characterName', name='uix_client_characterName') + ) mapper(SsoCharacter, sso_table) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index b3c1840e8..9d4eaf55f 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -467,29 +467,28 @@ def getProjectedFits(fitID): raise TypeError("Need integer as argument") -def getSsoCharacters(eager=None): +def getSsoCharacters(clientHash, eager=None): eager = processEager(eager) with sd_lock: - characters = saveddata_session.query(SsoCharacter).options(*eager).all() + characters = saveddata_session.query(SsoCharacter).filter(SsoCharacter.client == clientHash).options(*eager).all() return characters @cachedQuery(SsoCharacter, 1, "lookfor") -def getSsoCharacter(lookfor, eager=None): +def getSsoCharacter(lookfor, clientHash, eager=None): + filter = SsoCharacter.client == clientHash + if isinstance(lookfor, int): - if eager is None: - with sd_lock: - character = saveddata_session.query(SsoCharacter).get(lookfor) - else: - eager = processEager(eager) - with sd_lock: - character = saveddata_session.query(SsoCharacter).options(*eager).filter(SsoCharacter.ID == lookfor).first() + filter = and_(filter, SsoCharacter.characterID == lookfor) elif isinstance(lookfor, str): - eager = processEager(eager) - with sd_lock: - character = saveddata_session.query(SsoCharacter).options(*eager).filter(SsoCharacter.name == lookfor).first() + filter = and_(filter, SsoCharacter.characterName == lookfor) else: raise TypeError("Need integer or string as argument") + + eager = processEager(eager) + with sd_lock: + character = saveddata_session.query(SsoCharacter).options(*eager).filter(filter).first() + return character @@ -544,7 +543,7 @@ def commit(): try: saveddata_session.commit() saveddata_session.flush() - except Exception: + except Exception as ex: saveddata_session.rollback() exc_info = sys.exc_info() raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) diff --git a/eos/saveddata/ssocharacter.py b/eos/saveddata/ssocharacter.py index 8fca830cb..88fcc4a24 100644 --- a/eos/saveddata/ssocharacter.py +++ b/eos/saveddata/ssocharacter.py @@ -25,13 +25,13 @@ import time class SsoCharacter(object): - def __init__(self, id, charID, name, client, accessToken, refreshToken=None): - self.ID = id + def __init__(self, charID, name, client, accessToken=None, refreshToken=None): self.characterID = charID self.characterName = name self.client = client self.accessToken = accessToken self.refreshToken = refreshToken + self.esi_client = None @reconstructor def init(self): @@ -51,7 +51,7 @@ class SsoCharacter(object): def update_token(self, tokenResponse): """ helper function to update token data from SSO response """ self.accessToken = tokenResponse['access_token'] - self.accessTokenExpires = datetime.fromtimestamp( + self.accessTokenExpires = datetime.datetime.fromtimestamp( time.time() + tokenResponse['expires_in'], ) if 'refresh_token' in tokenResponse: diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 718b6654b..902d827e4 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -310,7 +310,9 @@ class CrestMgmt(wx.Dialog): 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.lcCharacters.InsertColumn(1, heading='Character ID') + self.lcCharacters.InsertColumn(2, heading='Access Token') + self.lcCharacters.InsertColumn(3, heading='Refresh Token') self.popCharList() @@ -347,8 +349,8 @@ class CrestMgmt(wx.Dialog): self.lcCharacters.DeleteAllItems() for index, char in enumerate(chars): - self.lcCharacters.InsertItem(index, char.name) - self.lcCharacters.SetStringItem(index, 1, char.refresh_token) + self.lcCharacters.InsertItem(index, char.characterName) + self.lcCharacters.SetStringItem(index, 1, char.refreshToken) self.lcCharacters.SetItemData(index, char.ID) self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE) diff --git a/service/crest.py b/service/crest.py index 51c9f1309..2642b1438 100644 --- a/service/crest.py +++ b/service/crest.py @@ -5,6 +5,9 @@ import threading import copy import uuid import time +import config +import base64 +import json import eos.db from eos.enum import Enum @@ -14,8 +17,20 @@ from service.settings import CRESTSettings from service.server import StoppableHTTPServer, AuthHandler from service.pycrest.eve import EVE +from .esi_security_proxy import EsiSecurityProxy +from esipy import EsiClient +from esipy.cache import FileCache +import os + pyfalog = Logger(__name__) +server = "https://blitzmann.pythonanywhere.com" +cache_path = os.path.join(config.savePath, config.ESI_CACHE) + +if not os.path.exists(cache_path): + os.mkdir(cache_path) + +file_cache = FileCache(cache_path) class Servers(Enum): TQ = 0 @@ -39,6 +54,14 @@ class Crest(object): _instance = None + @classmethod + def genEsiClient(cls, security=None): + return EsiClient( + security=EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) if security is None else security, + cache=file_cache, + headers={'User-Agent': 'pyfa esipy'} + ) + @classmethod def getInstance(cls): if cls._instance is None: @@ -113,32 +136,30 @@ class Crest(object): wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=0)) def getCrestCharacters(self): - chars = eos.db.getSsoCharacters() + chars = eos.db.getSsoCharacters(config.getClientSecret()) return chars - def getCrestCharacter(self, charID): + def getSsoCharacter(self, charID): """ Get character, and modify to include the eve connection """ - if charID in self.charCache: - return self.charCache.get(charID) - - char = eos.db.getSsoCharacter(charID) - self.charCache[charID] = char + char = eos.db.getSsoCharacter(charID, config.getClientSecret()) + if char.esi_client is None: + char.esi_client = Crest.genEsiClient() return char def getFittings(self, charID): - char = self.getCrestCharacter(charID) + char = self.getSsoCharacter(charID) 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/ - char = self.getCrestCharacter(charID) + char = self.getSsoCharacter(charID) return char.eve.post('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID), data=json) def delFitting(self, charID, fittingID): - char = self.getCrestCharacter(charID) + char = self.getSsoCharacter(charID) return char.eve.delete('%scharacters/%d/fittings/%d/' % (char.eve._authed_endpoint, char.ID, fittingID)) def logout(self): @@ -154,19 +175,26 @@ class Crest(object): def startServer(self): pyfalog.debug("Starting server") + + # we need this to ensure that the previous get_request finishes, and then the socket will close 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 = StoppableHTTPServer(('localhost', 6461), AuthHandler) + + self.state = str(uuid.uuid4()) + self.httpd = StoppableHTTPServer(('localhost', 0), AuthHandler) + port = self.httpd.socket.getsockname()[1] + + esisecurity = EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) + + uri = esisecurity.get_auth_uri(state=self.state, redirect='http://localhost:{}'.format(port)) self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleLogin,)) - self.serverThread.name = "CRESTServer" + self.serverThread.name = "SsoCallbackServer" self.serverThread.daemon = True self.serverThread.start() - self.state = str(uuid.uuid4()) - return self.eve.auth_uri(scopes=self.scopes, state=self.state) + return uri def handleLogin(self, message): if not message: @@ -178,41 +206,26 @@ class Crest(object): pyfalog.debug("Handling CREST login with: {0}", message) - if 'access_token' in message: # implicit - eve = EVE(**self.eve_options) - eve.temptoken_authorize( - access_token=message['access_token'][0], - expires_in=int(message['expires_in'][0]) - ) - self.ssoTimer = threading.Timer(int(message['expires_in'][0]), self.logout) - self.ssoTimer.start() + auth_response = json.loads(base64.b64decode(message['SSOInfo'][0])) - eve() - info = eve.whoami() + # We need to preload the ESI Security object beforehand with the auth response so that we can use verify to + # get character information + # init the security object + esisecurity = EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) - pyfalog.debug("Got character info: {0}", info) + esisecurity.update_token(auth_response) - self.implicitCharacter = CrestChar(info['CharacterID'], info['CharacterName']) - self.implicitCharacter.eve = eve - # self.implicitCharacter.fetchImage() + # we get the character information + cdata = esisecurity.verify() + print(cdata) - wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.IMPLICIT)) - elif 'code' in message: - eve = EVE(**self.eve_options) - eve.authorize(message['code'][0]) - eve() - info = eve.whoami() + currentCharacter = self.getSsoCharacter(cdata['CharacterID']) - pyfalog.debug("Got character info: {0}", info) + if currentCharacter is None: + currentCharacter = SsoCharacter(cdata['CharacterID'], cdata['CharacterName'], config.getClientSecret()) + currentCharacter.esi_client = Crest.genEsiClient(esisecurity) + currentCharacter.update_token(auth_response) # this also sets the esi security token - # check if we have character already. If so, simply replace refresh_token - char = self.getCrestCharacter(int(info['CharacterID'])) - if char: - char.refresh_token = eve.refresh_token - else: - char = CrestChar(info['CharacterID'], info['CharacterName'], eve.refresh_token) - char.eve = eve - self.charCache[int(info['CharacterID'])] = char - eos.db.save(char) + eos.db.save(currentCharacter) - wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.USER)) + wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.USER)) # todo: remove user / implicit authentication diff --git a/service/esi_security_proxy.py b/service/esi_security_proxy.py index f6263d5e8..21d0f8510 100644 --- a/service/esi_security_proxy.py +++ b/service/esi_security_proxy.py @@ -26,11 +26,9 @@ class EsiSecurityProxy(object): def __init__( self, - redirect_uri, **kwargs): """ Init the ESI Security Object - :param redirect_uri: the uri to redirect the user after login into SSO :param sso_url: the default sso URL used when no "app" is provided :param esi_url: the default esi URL used for verify endpoint :param app: (optionnal) the pyswagger app object @@ -43,7 +41,6 @@ class EsiSecurityProxy(object): esi_url = kwargs.pop('esi_url', "https://esi.tech.ccp.is") self.security_name = kwargs.pop('security_name', 'evesso') - self.redirect_uri = redirect_uri # we provide app object, so we don't use sso_url if app is not None: diff --git a/service/server.py b/service/server.py index 76c5a964e..34261294a 100644 --- a/service/server.py +++ b/service/server.py @@ -82,14 +82,14 @@ class AuthHandler(http.server.BaseHTTPRequestHandler): try: if step2: self.server.callback(parts) - pyfalog.info("Successfully logged into CREST.") + pyfalog.info("Successfully logged into EVE.") msg = "If you see this message then it means you should be logged into CREST. You may close this window and return to the application." else: # For implicit mode, we have to serve up the page which will take the hash and redirect useing a querystring pyfalog.info("Processing response from EVE Online.") msg = "Processing response from EVE Online" except Exception as ex: - pyfalog.error("Error in CREST AuthHandler") + pyfalog.error("Error logging into EVE") pyfalog.error(ex) msg = "

    Error

    \n

    {}

    ".format(ex.message) finally: @@ -109,10 +109,10 @@ class AuthHandler(http.server.BaseHTTPRequestHandler): class StoppableHTTPServer(http.server.HTTPServer): def server_bind(self): http.server.HTTPServer.server_bind(self) - self.settings = CRESTSettings.getInstance() + # self.settings = CRESTSettings.getInstance() # Allow listening for x seconds - sec = self.settings.get('timeout') + sec = 120 pyfalog.debug("Running server for {0} seconds", sec) self.socket.settimeout(1) @@ -131,7 +131,7 @@ class StoppableHTTPServer(http.server.HTTPServer): pass def stop(self): - pyfalog.warning("Setting CREST server to stop.") + pyfalog.warning("Setting pyfa server to stop.") self.run = False def handle_timeout(self): From eea80195939b1ff4e114e70e7cda42dadc91e574 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 7 Feb 2018 01:21:22 -0500 Subject: [PATCH 108/212] Get fitting browser to show up with proper characters (still not functional). Start testing ways to store the esipy app on the service (it can take a few seconds to initialize due to network calls) --- gui/crestFittings.py | 21 +++++++-------------- gui/mainFrame.py | 2 +- gui/mainMenuBar.py | 4 ++-- service/crest.py | 21 +++++++++++++++++++-- utils/timer.py | 2 +- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 902d827e4..f01258f80 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -34,16 +34,9 @@ class CrestFittings(wx.Frame): 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) - - 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) - self.updateCharList() + self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) + characterSelectSizer.Add(self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) + self.updateCharList() self.fetchBtn = wx.Button(self, wx.ID_ANY, "Fetch Fits", wx.DefaultPosition, wx.DefaultSize, 5) characterSelectSizer.Add(self.fetchBtn, 0, wx.ALL, 5) @@ -99,14 +92,14 @@ class CrestFittings(wx.Frame): def updateCharList(self): sCrest = Crest.getInstance() - chars = sCrest.getCrestCharacters() + chars = sCrest.getSsoCharacters() if len(chars) == 0: self.Close() self.charChoice.Clear() for char in chars: - self.charChoice.Append(char.name, char.ID) + self.charChoice.Append(char.characterName, char.characterID) self.charChoice.SetSelection(0) @@ -232,7 +225,7 @@ class ExportToEve(wx.Frame): def updateCharList(self): sCrest = Crest.getInstance() - chars = sCrest.getCrestCharacters() + chars = sCrest.getSsoCharacters() if len(chars) == 0: self.Close() @@ -344,7 +337,7 @@ class CrestMgmt(wx.Dialog): def popCharList(self): sCrest = Crest.getInstance() - chars = sCrest.getCrestCharacters() + chars = sCrest.getSsoCharacters() self.lcCharacters.DeleteAllItems() diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 735db0d80..95c57e119 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -657,7 +657,7 @@ class MainFrame(wx.Frame): menu.Enable(menu.exportToEveId, False) else: menu.SetLabel(menu.ssoLoginId, "Manage Characters") - enable = len(sCrest.getCrestCharacters()) == 0 + enable = len(sCrest.getSsoCharacters()) == 0 menu.Enable(menu.eveFittingsId, not enable) menu.Enable(menu.exportToEveId, not enable) diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 8b5b77e60..e9e63b82b 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -145,8 +145,8 @@ class MainMenuBar(wx.MenuBar): crestMenu.Append(self.exportToEveId, "Export To EVE") # if self.sCrest.settings.get('mode') == CrestModes.IMPLICIT or len(self.sCrest.getCrestCharacters()) == 0: - self.Enable(self.eveFittingsId, False) - self.Enable(self.exportToEveId, False) + self.Enable(self.eveFittingsId, True) + self.Enable(self.exportToEveId, True) if not self.mainFrame.disableOverrideEditor: windowMenu.AppendSeparator() diff --git a/service/crest.py b/service/crest.py index 2642b1438..60d043707 100644 --- a/service/crest.py +++ b/service/crest.py @@ -18,9 +18,11 @@ from service.server import StoppableHTTPServer, AuthHandler from service.pycrest.eve import EVE from .esi_security_proxy import EsiSecurityProxy -from esipy import EsiClient +from esipy import EsiClient, EsiApp from esipy.cache import FileCache import os +import logging + pyfalog = Logger(__name__) @@ -41,6 +43,7 @@ class CrestModes(Enum): IMPLICIT = 0 USER = 1 +from utils.timer import Timer class Crest(object): clientIDs = { @@ -54,6 +57,16 @@ class Crest(object): _instance = None + @classmethod + def initEsiApp(cls): + with Timer() as t: + cls.esiapp = EsiApp(cache=None) + + with Timer() as t: + cls.esi_v1 = cls.esiapp.get_v1_swagger + with Timer() as t: + cls.esi_v4 = cls.esiapp.get_v4_swagger + @classmethod def genEsiClient(cls, security=None): return EsiClient( @@ -135,7 +148,7 @@ class Crest(object): self.charCache = {} wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=0)) - def getCrestCharacters(self): + def getSsoCharacters(self): chars = eos.db.getSsoCharacters(config.getClientSecret()) return chars @@ -150,6 +163,10 @@ class Crest(object): def getFittings(self, charID): char = self.getSsoCharacter(charID) + op = esi_v1.op['get_characters_character_id_fittings']( + character_id=self.currentCharacter.character_id + ) + resp = self.currentCharacter.esi_client.request(op) return char.eve.get('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID)) def postFitting(self, charID, json): diff --git a/utils/timer.py b/utils/timer.py index 9b7daf4dc..da7042f59 100644 --- a/utils/timer.py +++ b/utils/timer.py @@ -19,7 +19,7 @@ class Timer(object): def checkpoint(self, name=''): text = 'Timer - {timer} - {checkpoint} - {last:.2f}ms ({elapsed:.2f}ms elapsed)'.format( timer=self.name, - checkpoint=str(name, "utf-8"), + checkpoint=name, last=self.last, elapsed=self.elapsed ).strip() From e77dddc15be3221f45ae8f8c6a03f2c7d7727c4b Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 7 Feb 2018 02:07:42 -0500 Subject: [PATCH 109/212] More work on getting fittings form a character via ESI. It's starting to be come a pain working with this client with having to background it until it initializes. Thinking about rolling my own, considering we only need a few calls and not a whole package. --- eos/saveddata/ssocharacter.py | 4 ++-- gui/crestFittings.py | 5 ----- gui/mainFrame.py | 2 ++ service/crest.py | 29 +++++++++++++++++++++++++---- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/eos/saveddata/ssocharacter.py b/eos/saveddata/ssocharacter.py index 88fcc4a24..95314f5e1 100644 --- a/eos/saveddata/ssocharacter.py +++ b/eos/saveddata/ssocharacter.py @@ -35,7 +35,7 @@ class SsoCharacter(object): @reconstructor def init(self): - pass + self.esi_client = None def get_sso_data(self): """ Little "helper" function to get formated data for esipy security @@ -44,7 +44,7 @@ class SsoCharacter(object): 'access_token': self.accessToken, 'refresh_token': self.refreshToken, 'expires_in': ( - self.accessTokenExpires - datetime.utcnow() + self.accessTokenExpires - datetime.datetime.utcnow() ).total_seconds() } diff --git a/gui/crestFittings.py b/gui/crestFittings.py index f01258f80..dc46476f7 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -126,11 +126,6 @@ class CrestFittings(wx.Frame): event.Skip() def getActiveCharacter(self): - sCrest = Crest.getInstance() - - if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - return sCrest.implicitCharacter.ID - selection = self.charChoice.GetCurrentSelection() return self.charChoice.GetClientData(selection) if selection is not None else None diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 95c57e119..9d3185c07 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -166,6 +166,8 @@ class MainFrame(wx.Frame): i = wx.Icon(BitmapLoader.getBitmap("pyfa", "gui")) self.SetIcon(i) + sCrest = Crest() + # Create the layout and windows mainSizer = wx.BoxSizer(wx.HORIZONTAL) diff --git a/service/crest.py b/service/crest.py index 60d043707..3b920a23b 100644 --- a/service/crest.py +++ b/service/crest.py @@ -34,6 +34,9 @@ if not os.path.exists(cache_path): file_cache = FileCache(cache_path) +esiRdy = threading.Event() + + class Servers(Enum): TQ = 0 SISI = 1 @@ -45,6 +48,17 @@ class CrestModes(Enum): from utils.timer import Timer + + +class EsiInitThread(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + self.name = "EsiInitThread" + + def run(self): + Crest.initEsiApp() + + class Crest(object): clientIDs = { Servers.TQ : 'f9be379951c046339dc13a00e6be7704', @@ -67,6 +81,8 @@ class Crest(object): with Timer() as t: cls.esi_v4 = cls.esiapp.get_v4_swagger + esiRdy.set() + @classmethod def genEsiClient(cls, security=None): return EsiClient( @@ -103,6 +119,10 @@ class Crest(object): characters still in the cache (if USER mode) """ + prefetch = EsiInitThread() + prefetch.daemon = True + prefetch.start() + self.settings = CRESTSettings.getInstance() self.scopes = ['characterFittingsRead', 'characterFittingsWrite'] @@ -159,15 +179,16 @@ class Crest(object): char = eos.db.getSsoCharacter(charID, config.getClientSecret()) if char.esi_client is None: char.esi_client = Crest.genEsiClient() + char.esi_client.security.update_token(char.get_sso_data()) return char def getFittings(self, charID): char = self.getSsoCharacter(charID) - op = esi_v1.op['get_characters_character_id_fittings']( - character_id=self.currentCharacter.character_id + op = Crest.esi_v1.op['get_characters_character_id_fittings']( + character_id=charID ) - resp = self.currentCharacter.esi_client.request(op) - return char.eve.get('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID)) + resp = char.esi_client.request(op) + return resp def postFitting(self, charID, json): # @todo: new fitting ID can be recovered from Location header, From e025bff99b86b26d2293e882cf099c818c017936 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 8 Feb 2018 01:24:40 -0500 Subject: [PATCH 110/212] Get ESI fitting import working completely. Use a file cache for EsiApp to prevent long startup times (possibly cache for about a week or so, and start in background if it needs to be gotten again?) --- gui/crestFittings.py | 17 +++++++++-------- gui/mainFrame.py | 2 -- service/crest.py | 30 +++++++++++------------------- service/port.py | 12 ++++++------ 4 files changed, 26 insertions(+), 35 deletions(-) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index dc46476f7..4f3d8ea42 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -134,9 +134,9 @@ class CrestFittings(wx.Frame): try: waitDialog = wx.BusyInfo("Fetching fits, please wait...", parent=self) fittings = sCrest.getFittings(self.getActiveCharacter()) - self.cacheTime = fittings.get('cached_until') - self.updateCacheStatus(None) - self.cacheTimer.Start(1000) + # self.cacheTime = fittings.get('cached_until') + # self.updateCacheStatus(None) + # self.cacheTimer.Start(1000) self.fitTree.populateSkillTree(fittings) del waitDialog except requests.exceptions.ConnectionError: @@ -385,11 +385,12 @@ class FittingsTreeView(wx.Panel): tree.DeleteChildren(root) dict = {} - fits = data['items'] + fits = data for fit in fits: - if fit['ship']['name'] not in dict: - dict[fit['ship']['name']] = [] - dict[fit['ship']['name']].append(fit) + ship = getItem(fit['ship_type_id']) + if ship.name not in dict: + dict[ship.name ] = [] + dict[ship.name ].append(fit) for name, fits in dict.items(): shipID = tree.AppendItem(root, name) @@ -412,7 +413,7 @@ class FittingsTreeView(wx.Panel): for item in fit['items']: try: - cargo = Cargo(getItem(item['type']['id'])) + cargo = Cargo(getItem(item['type_id'])) cargo.amount = item['quantity'] list.append(cargo) except Exception as e: diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 9d3185c07..95c57e119 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -166,8 +166,6 @@ class MainFrame(wx.Frame): i = wx.Icon(BitmapLoader.getBitmap("pyfa", "gui")) self.SetIcon(i) - sCrest = Crest() - # Create the layout and windows mainSizer = wx.BoxSizer(wx.HORIZONTAL) diff --git a/service/crest.py b/service/crest.py index 3b920a23b..5072ed782 100644 --- a/service/crest.py +++ b/service/crest.py @@ -50,15 +50,6 @@ from utils.timer import Timer -class EsiInitThread(threading.Thread): - def __init__(self): - threading.Thread.__init__(self) - self.name = "EsiInitThread" - - def run(self): - Crest.initEsiApp() - - class Crest(object): clientIDs = { Servers.TQ : 'f9be379951c046339dc13a00e6be7704', @@ -73,15 +64,14 @@ class Crest(object): @classmethod def initEsiApp(cls): - with Timer() as t: - cls.esiapp = EsiApp(cache=None) - - with Timer() as t: + with Timer("Main EsiApp") as t: + cls.esiapp = EsiApp(cache=file_cache) + with Timer('ESI v1') as t: cls.esi_v1 = cls.esiapp.get_v1_swagger - with Timer() as t: + with Timer('ESI v4') as t: cls.esi_v4 = cls.esiapp.get_v4_swagger - esiRdy.set() + # esiRdy.set() @classmethod def genEsiClient(cls, security=None): @@ -118,10 +108,12 @@ class Crest(object): mode. The mode is sent as an argument, as well as the umber of characters still in the cache (if USER mode) """ + Crest.initEsiApp() - prefetch = EsiInitThread() - prefetch.daemon = True - prefetch.start() + + # prefetch = EsiInitThread() + # prefetch.daemon = True + # prefetch.start() self.settings = CRESTSettings.getInstance() self.scopes = ['characterFittingsRead', 'characterFittingsWrite'] @@ -188,7 +180,7 @@ class Crest(object): character_id=charID ) resp = char.esi_client.request(op) - return resp + return resp.data def postFitting(self, charID, json): # @todo: new fitting ID can be recovered from Location header, diff --git a/service/port.py b/service/port.py index c61237b5c..1623d7005 100644 --- a/service/port.py +++ b/service/port.py @@ -491,7 +491,7 @@ class Port(object): # If JSON-style start, parse as CREST/JSON if firstLine[0] == '{': - return "JSON", (cls.importCrest(string),) + return "JSON", (cls.importESI(string),) # If we've got source file name which is used to describe ship name # and first line contains something like [setup name], detect as eft config file @@ -509,7 +509,7 @@ class Port(object): return "DNA", (cls.importDna(string),) @staticmethod - def importCrest(str_): + def importESI(str_): sMkt = Market.getInstance() fitobj = Fit() @@ -521,11 +521,11 @@ class Port(object): fitobj.notes = refobj['description'] try: - refobj = refobj['ship']['id'] + ship = refobj['ship_type_id'] try: - fitobj.ship = Ship(sMkt.getItem(refobj)) + fitobj.ship = Ship(sMkt.getItem(ship)) except ValueError: - fitobj.ship = Citadel(sMkt.getItem(refobj)) + fitobj.ship = Citadel(sMkt.getItem(ship)) except: pyfalog.warning("Caught exception in importCrest") return None @@ -535,7 +535,7 @@ class Port(object): moduleList = [] for module in items: try: - item = sMkt.getItem(module['type']['id'], eager="group.category") + item = sMkt.getItem(module['type_id'], eager="group.category") if module['flag'] == INV_FLAG_DRONEBAY: d = Drone(item) d.amount = module['quantity'] From 5fbe623ae63ac0349d95c51e46f737d51613512b Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 8 Feb 2018 01:38:24 -0500 Subject: [PATCH 111/212] get fit deletion working. Need to be aware that we are still using cached fit listing... --- eos/db/saveddata/queries.py | 2 +- gui/crestFittings.py | 8 ++++---- service/crest.py | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 9d4eaf55f..087561070 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -474,7 +474,7 @@ def getSsoCharacters(clientHash, eager=None): return characters -@cachedQuery(SsoCharacter, 1, "lookfor") +@cachedQuery(SsoCharacter, 1, "lookfor", "clientHash") def getSsoCharacter(lookfor, clientHash, eager=None): filter = SsoCharacter.client == clientHash diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 4f3d8ea42..760cf40b6 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -161,12 +161,12 @@ class CrestFittings(wx.Frame): data = json.loads(self.fitTree.fittingsTreeCtrl.GetItemData(selection)) dlg = wx.MessageDialog(self, - "Do you really want to delete %s (%s) from EVE?" % (data['name'], data['ship']['name']), + "Do you really want to delete %s (%s) from EVE?" % (data['name'], getItem(data['ship_type_id']).name), "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) if dlg.ShowModal() == wx.ID_YES: try: - sCrest.delFitting(self.getActiveCharacter(), data['fittingID']) + sCrest.delFitting(self.getActiveCharacter(), data['fitting_id']) except requests.exceptions.ConnectionError: msg = "Connection error, please check your internet connection" pyfalog.error(msg) @@ -389,8 +389,8 @@ class FittingsTreeView(wx.Panel): for fit in fits: ship = getItem(fit['ship_type_id']) if ship.name not in dict: - dict[ship.name ] = [] - dict[ship.name ].append(fit) + dict[ship.name] = [] + dict[ship.name].append(fit) for name, fits in dict.items(): shipID = tree.AppendItem(root, name) diff --git a/service/crest.py b/service/crest.py index 5072ed782..ce06a0e6c 100644 --- a/service/crest.py +++ b/service/crest.py @@ -60,6 +60,10 @@ class Crest(object): clientCallback = 'http://localhost:6461' clientTest = True + esiapp = None + esi_v1 = None + esi_v4 = None + _instance = None @classmethod @@ -176,6 +180,7 @@ class Crest(object): def getFittings(self, charID): char = self.getSsoCharacter(charID) + print(repr(char)) op = Crest.esi_v1.op['get_characters_character_id_fittings']( character_id=charID ) @@ -190,7 +195,15 @@ class Crest(object): def delFitting(self, charID, fittingID): char = self.getSsoCharacter(charID) - return char.eve.delete('%scharacters/%d/fittings/%d/' % (char.eve._authed_endpoint, char.ID, fittingID)) + print(repr(char)) + op = Crest.esi_v1.op['delete_characters_character_id_fittings_fitting_id']( + character_id=charID, + fitting_id=fittingID + ) + + resp = char.esi_client.request(op) + return resp.data + def logout(self): """Logout of implicit character""" From dfba03319087e82fe45c25313defd51d8b2d7a6e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 8 Feb 2018 01:50:52 -0500 Subject: [PATCH 112/212] Implement posting fit to EVE server --- gui/crestFittings.py | 39 ++++++++++++++------------------------- gui/mainFrame.py | 2 +- service/crest.py | 15 +++++++++++---- service/port.py | 26 +++++++------------------- 4 files changed, 33 insertions(+), 49 deletions(-) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 760cf40b6..fdd82312f 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -185,17 +185,10 @@ class ExportToEve(wx.Frame): mainSizer = wx.BoxSizer(wx.VERTICAL) 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) - - 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) - self.updateCharList() - self.charChoice.SetSelection(0) + self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) + 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, "Export Fit", wx.DefaultPosition, wx.DefaultSize, 5) hSizer.Add(self.exportBtn, 0, wx.ALL, 5) @@ -227,7 +220,7 @@ class ExportToEve(wx.Frame): self.charChoice.Clear() for char in chars: - self.charChoice.Append(char.name, char.ID) + self.charChoice.Append(char.characterName, char.characterID) self.charChoice.SetSelection(0) @@ -249,11 +242,6 @@ class ExportToEve(wx.Frame): event.Skip() def getActiveCharacter(self): - sCrest = Crest.getInstance() - - if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - return sCrest.implicitCharacter.ID - selection = self.charChoice.GetCurrentSelection() return self.charChoice.GetClientData(selection) if selection is not None else None @@ -272,16 +260,17 @@ class ExportToEve(wx.Frame): try: sFit = Fit.getInstance() - data = sPort.exportCrest(sFit.getFit(fitID)) + data = sPort.exportESI(sFit.getFit(fitID)) res = sCrest.postFitting(self.getActiveCharacter(), data) - self.statusbar.SetStatusText("%d: %s" % (res.status_code, res.reason), 0) - try: - text = json.loads(res.text) - self.statusbar.SetStatusText(text['message'], 1) - except ValueError: - pyfalog.warning("Value error on loading JSON.") - self.statusbar.SetStatusText("", 1) + self.statusbar.SetStatusText("", 0) + self.statusbar.SetStatusText("", 1) + # try: + # text = json.loads(res.text) + # self.statusbar.SetStatusText(text['message'], 1) + # except ValueError: + # pyfalog.warning("Value error on loading JSON.") + # self.statusbar.SetStatusText("", 1) except requests.exceptions.ConnectionError: msg = "Connection error, please check your internet connection" pyfalog.error(msg) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 95c57e119..46e376976 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -740,7 +740,7 @@ class MainFrame(wx.Frame): def clipboardCrest(self): fit = db_getFit(self.getActiveFit()) - toClipboard(Port.exportCrest(fit)) + toClipboard(Port.exportESI(fit)) def clipboardXml(self): fit = db_getFit(self.getActiveFit()) diff --git a/service/crest.py b/service/crest.py index ce06a0e6c..2f85815b8 100644 --- a/service/crest.py +++ b/service/crest.py @@ -187,11 +187,18 @@ class Crest(object): resp = char.esi_client.request(op) return resp.data - 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/ + def postFitting(self, charID, json_str): + # @todo: new fitting ID can be recovered from resp.data, char = self.getSsoCharacter(charID) - return char.eve.post('%scharacters/%d/fittings/' % (char.eve._authed_endpoint, char.ID), data=json) + + op = Crest.esi_v1.op['post_characters_character_id_fittings']( + character_id=char.characterID, + fitting=json.loads(json_str) + ) + + resp = char.esi_client.request(op) + + return resp.data def delFitting(self, charID, fittingID): char = self.getSsoCharacter(charID) diff --git a/service/port.py b/service/port.py index 1623d7005..c3cf352c7 100644 --- a/service/port.py +++ b/service/port.py @@ -381,7 +381,7 @@ class Port(object): """Service which houses all import/export format functions""" @classmethod - def exportCrest(cls, ofit, callback=None): + def exportESI(cls, ofit, callback=None): # A few notes: # max fit name length is 50 characters # Most keys are created simply because they are required, but bogus data is okay @@ -396,9 +396,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']['id'] = ofit.ship.item.ID - fit['ship']['name'] = '' + fit['ship_type_id'] = ofit.ship.item.ID # 2017/03/29 NOTE: "<" or "<" is Ignored # fit['description'] = "" % ofit.ID @@ -426,9 +424,7 @@ class Port(object): slotNum[slot] += 1 item['quantity'] = 1 - item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, module.item.ID) - item['type']['id'] = module.item.ID - item['type']['name'] = '' + item['type_id'] = module.item.ID fit['items'].append(item) if module.charge and sFit.serviceFittingOptions["exportCharges"]: @@ -441,36 +437,28 @@ 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']['id'] = cargo.item.ID - item['type']['name'] = '' + item['type_id'] = cargo.item.ID fit['items'].append(item) for chargeID, amount in list(charges.items()): item = nested_dict() item['flag'] = INV_FLAG_CARGOBAY item['quantity'] = amount - item['type']['href'] = "%sinventory/types/%d/" % (eve._authed_endpoint, chargeID) - item['type']['id'] = chargeID - item['type']['name'] = '' + item['type_id'] = chargeID fit['items'].append(item) for drone in ofit.drones: 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']['id'] = drone.item.ID - item['type']['name'] = '' + item['type_id'] = drone.item.ID 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 + item['type_id'] = fighter.item.ID fit['items'].append(item) return json.dumps(fit) From cb392e7e5f2bc5cb7ed089f3764a085e5033046d Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 8 Feb 2018 01:52:38 -0500 Subject: [PATCH 113/212] Rename a few things --- .../pyfaCrestPreferences.py | 8 +- gui/crestFittings.py | 22 +- gui/mainFrame.py | 8 +- service/crest.py | 281 ------------------ service/esi.py | 243 ++++++++++++++- service/port.py | 4 +- 6 files changed, 260 insertions(+), 306 deletions(-) delete mode 100644 service/crest.py diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py index f2dbe694d..f3da3dbeb 100644 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ b/gui/builtinPreferenceViews/pyfaCrestPreferences.py @@ -11,7 +11,7 @@ from service.settings import CRESTSettings # noinspection PyPackageRequirements from wx.lib.intctrl import IntCtrl -from service.crest import Crest +from service.esi import Esi class PFCrestPref(PreferenceView): @@ -119,16 +119,16 @@ class PFCrestPref(PreferenceView): def OnModeChange(self, event): self.settings.set('mode', event.GetInt()) self.ToggleProxySettings(self.settings.get('mode')) - Crest.restartService() + Esi.restartService() def OnServerChange(self, event): self.settings.set('server', event.GetInt()) - Crest.restartService() + Esi.restartService() def OnBtnApply(self, event): self.settings.set('clientID', self.inputClientID.GetValue().strip()) self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip()) - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() sCrest.delAllCharacters() def ToggleProxySettings(self, mode): diff --git a/gui/crestFittings.py b/gui/crestFittings.py index fdd82312f..69e8b6085 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -16,7 +16,7 @@ import gui.globalEvents as GE from logbook import Logger import calendar -from service.crest import Crest, CrestModes +from service.esi import Esi, CrestModes pyfalog = Logger(__name__) @@ -30,7 +30,7 @@ class CrestFittings(wx.Frame): self.mainFrame = parent mainSizer = wx.BoxSizer(wx.VERTICAL) - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() characterSelectSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -91,7 +91,7 @@ class CrestFittings(wx.Frame): event.Skip() def updateCharList(self): - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() chars = sCrest.getSsoCharacters() if len(chars) == 0: @@ -130,7 +130,7 @@ class CrestFittings(wx.Frame): return self.charChoice.GetClientData(selection) if selection is not None else None def fetchFittings(self, event): - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() try: waitDialog = wx.BusyInfo("Fetching fits, please wait...", parent=self) fittings = sCrest.getFittings(self.getActiveCharacter()) @@ -154,7 +154,7 @@ class CrestFittings(wx.Frame): self.mainFrame._openAfterImport(fits) def deleteFitting(self, event): - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() selection = self.fitView.fitSelection if not selection: return @@ -181,7 +181,7 @@ class ExportToEve(wx.Frame): self.mainFrame = parent self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() mainSizer = wx.BoxSizer(wx.VERTICAL) hSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -212,7 +212,7 @@ class ExportToEve(wx.Frame): self.Centre(wx.BOTH) def updateCharList(self): - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() chars = sCrest.getSsoCharacters() if len(chars) == 0: @@ -256,7 +256,7 @@ class ExportToEve(wx.Frame): return self.statusbar.SetStatusText("Sending request and awaiting response", 1) - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() try: sFit = Fit.getInstance() @@ -320,7 +320,7 @@ class CrestMgmt(wx.Dialog): event.Skip() def popCharList(self): - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() chars = sCrest.getSsoCharacters() self.lcCharacters.DeleteAllItems() @@ -335,7 +335,7 @@ class CrestMgmt(wx.Dialog): @staticmethod def addChar(event): - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() uri = sCrest.startServer() webbrowser.open(uri) @@ -343,7 +343,7 @@ class CrestMgmt(wx.Dialog): item = self.lcCharacters.GetFirstSelected() if item > -1: charID = self.lcCharacters.GetItemData(item) - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() sCrest.delCrestCharacter(charID) self.popCharList() diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 46e376976..7cef8ea3f 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -82,8 +82,8 @@ import threading import webbrowser import wx.adv -from service.crest import Crest -from service.crest import CrestModes +from service.esi import Esi +from service.esi import CrestModes from gui.crestFittings import CrestFittings, ExportToEve, CrestMgmt disableOverrideEditor = False @@ -614,7 +614,7 @@ class MainFrame(wx.Frame): dlg.Show() def updateTitle(self, event): - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() char = sCrest.implicitCharacter if char: t = time.gmtime(char.eve.expires - time.time()) @@ -649,7 +649,7 @@ class MainFrame(wx.Frame): self.SetTitle(self.title) menu = self.GetMenuBar() - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() if type == CrestModes.IMPLICIT: menu.SetLabel(menu.ssoLoginId, "Login to EVE") diff --git a/service/crest.py b/service/crest.py deleted file mode 100644 index 2f85815b8..000000000 --- a/service/crest.py +++ /dev/null @@ -1,281 +0,0 @@ -# noinspection PyPackageRequirements -import wx -from logbook import Logger -import threading -import copy -import uuid -import time -import config -import base64 -import json - -import eos.db -from eos.enum import Enum -from eos.saveddata.ssocharacter import SsoCharacter -import gui.globalEvents as GE -from service.settings import CRESTSettings -from service.server import StoppableHTTPServer, AuthHandler -from service.pycrest.eve import EVE - -from .esi_security_proxy import EsiSecurityProxy -from esipy import EsiClient, EsiApp -from esipy.cache import FileCache -import os -import logging - - -pyfalog = Logger(__name__) - -server = "https://blitzmann.pythonanywhere.com" -cache_path = os.path.join(config.savePath, config.ESI_CACHE) - -if not os.path.exists(cache_path): - os.mkdir(cache_path) - -file_cache = FileCache(cache_path) - -esiRdy = threading.Event() - - -class Servers(Enum): - TQ = 0 - SISI = 1 - - -class CrestModes(Enum): - IMPLICIT = 0 - USER = 1 - -from utils.timer import Timer - - - -class Crest(object): - clientIDs = { - Servers.TQ : 'f9be379951c046339dc13a00e6be7704', - Servers.SISI: 'af87365240d644f7950af563b8418bad' - } - - # @todo: move this to settings - clientCallback = 'http://localhost:6461' - clientTest = True - - esiapp = None - esi_v1 = None - esi_v4 = None - - _instance = None - - @classmethod - def initEsiApp(cls): - with Timer("Main EsiApp") as t: - cls.esiapp = EsiApp(cache=file_cache) - with Timer('ESI v1') as t: - cls.esi_v1 = cls.esiapp.get_v1_swagger - with Timer('ESI v4') as t: - cls.esi_v4 = cls.esiapp.get_v4_swagger - - # esiRdy.set() - - @classmethod - def genEsiClient(cls, security=None): - return EsiClient( - security=EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) if security is None else security, - cache=file_cache, - headers={'User-Agent': 'pyfa esipy'} - ) - - @classmethod - def getInstance(cls): - if cls._instance is None: - cls._instance = Crest() - - return cls._instance - - @classmethod - def restartService(cls): - # This is here to reseed pycrest values when changing preferences - # We first stop the server n case one is running, as creating a new - # instance doesn't do this. - if cls._instance.httpd: - cls._instance.stopServer() - cls._instance = Crest() - cls._instance.mainFrame.updateCrestMenus(type=cls._instance.settings.get('mode')) - return cls._instance - - def __init__(self): - """ - A note on login/logout events: the character login events happen - whenever a characters is logged into via the SSO, regardless of mod. - However, the mode should be send as an argument. Similarily, - the Logout even happens whenever the character is deleted for either - mode. The mode is sent as an argument, as well as the umber of - characters still in the cache (if USER mode) - """ - Crest.initEsiApp() - - - # prefetch = EsiInitThread() - # prefetch.daemon = True - # prefetch.start() - - self.settings = CRESTSettings.getInstance() - self.scopes = ['characterFittingsRead', 'characterFittingsWrite'] - - # these will be set when needed - self.httpd = None - self.state = None - self.ssoTimer = None - - self.eve_options = { - '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 - } - - # Base EVE connection that is copied to all characters - self.eve = EVE(**self.eve_options) - - self.implicitCharacter = None - - # The database cache does not seem to be working for some reason. Use - # this as a temporary measure - self.charCache = {} - - # need these here to post events - import gui.mainFrame # put this here to avoid loop - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - @property - def isTestServer(self): - return self.settings.get('server') == Servers.SISI - - def delCrestCharacter(self, charID): - char = eos.db.getSsoCharacter(charID) - del self.charCache[char.ID] - eos.db.remove(char) - wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=len(self.charCache))) - - def delAllCharacters(self): - chars = eos.db.getSsoCharacters() - for char in chars: - eos.db.remove(char) - self.charCache = {} - wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=0)) - - def getSsoCharacters(self): - chars = eos.db.getSsoCharacters(config.getClientSecret()) - return chars - - def getSsoCharacter(self, charID): - """ - Get character, and modify to include the eve connection - """ - char = eos.db.getSsoCharacter(charID, config.getClientSecret()) - if char.esi_client is None: - char.esi_client = Crest.genEsiClient() - char.esi_client.security.update_token(char.get_sso_data()) - return char - - def getFittings(self, charID): - char = self.getSsoCharacter(charID) - print(repr(char)) - op = Crest.esi_v1.op['get_characters_character_id_fittings']( - character_id=charID - ) - resp = char.esi_client.request(op) - return resp.data - - def postFitting(self, charID, json_str): - # @todo: new fitting ID can be recovered from resp.data, - char = self.getSsoCharacter(charID) - - op = Crest.esi_v1.op['post_characters_character_id_fittings']( - character_id=char.characterID, - fitting=json.loads(json_str) - ) - - resp = char.esi_client.request(op) - - return resp.data - - def delFitting(self, charID, fittingID): - char = self.getSsoCharacter(charID) - print(repr(char)) - op = Crest.esi_v1.op['delete_characters_character_id_fittings_fitting_id']( - character_id=charID, - fitting_id=fittingID - ) - - resp = char.esi_client.request(op) - return resp.data - - - def logout(self): - """Logout of implicit character""" - pyfalog.debug("Character logout") - self.implicitCharacter = None - wx.PostEvent(self.mainFrame, GE.SsoLogout(type=self.settings.get('mode'))) - - def stopServer(self): - pyfalog.debug("Stopping Server") - self.httpd.stop() - self.httpd = None - - def startServer(self): - pyfalog.debug("Starting server") - - # we need this to ensure that the previous get_request finishes, and then the socket will close - if self.httpd: - self.stopServer() - time.sleep(1) - - self.state = str(uuid.uuid4()) - self.httpd = StoppableHTTPServer(('localhost', 0), AuthHandler) - port = self.httpd.socket.getsockname()[1] - - esisecurity = EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) - - uri = esisecurity.get_auth_uri(state=self.state, redirect='http://localhost:{}'.format(port)) - - self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleLogin,)) - self.serverThread.name = "SsoCallbackServer" - self.serverThread.daemon = True - self.serverThread.start() - - return uri - - def handleLogin(self, message): - if not message: - raise Exception("Could not parse out querystring parameters.") - - if message['state'][0] != self.state: - pyfalog.warn("OAUTH state mismatch") - raise Exception("OAUTH State Mismatch.") - - pyfalog.debug("Handling CREST login with: {0}", message) - - auth_response = json.loads(base64.b64decode(message['SSOInfo'][0])) - - # We need to preload the ESI Security object beforehand with the auth response so that we can use verify to - # get character information - # init the security object - esisecurity = EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) - - esisecurity.update_token(auth_response) - - # we get the character information - cdata = esisecurity.verify() - print(cdata) - - currentCharacter = self.getSsoCharacter(cdata['CharacterID']) - - if currentCharacter is None: - currentCharacter = SsoCharacter(cdata['CharacterID'], cdata['CharacterName'], config.getClientSecret()) - currentCharacter.esi_client = Crest.genEsiClient(esisecurity) - currentCharacter.update_token(auth_response) # this also sets the esi security token - - eos.db.save(currentCharacter) - - wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.USER)) # todo: remove user / implicit authentication diff --git a/service/esi.py b/service/esi.py index 0f375a591..d065ffb90 100644 --- a/service/esi.py +++ b/service/esi.py @@ -5,17 +5,37 @@ import threading import copy import uuid import time +import config +import base64 +import json import eos.db from eos.enum import Enum -from eos.saveddata.ssocharacter import CrestChar +from eos.saveddata.ssocharacter import SsoCharacter import gui.globalEvents as GE from service.settings import CRESTSettings from service.server import StoppableHTTPServer, AuthHandler from service.pycrest.eve import EVE +from .esi_security_proxy import EsiSecurityProxy +from esipy import EsiClient, EsiApp +from esipy.cache import FileCache +import os +import logging + + pyfalog = Logger(__name__) +server = "https://blitzmann.pythonanywhere.com" +cache_path = os.path.join(config.savePath, config.ESI_CACHE) + +if not os.path.exists(cache_path): + os.mkdir(cache_path) + +file_cache = FileCache(cache_path) + +esiRdy = threading.Event() + class Servers(Enum): TQ = 0 @@ -26,21 +46,236 @@ class CrestModes(Enum): IMPLICIT = 0 USER = 1 +from utils.timer import Timer + + + +class Esi(object): + clientIDs = { + Servers.TQ : 'f9be379951c046339dc13a00e6be7704', + Servers.SISI: 'af87365240d644f7950af563b8418bad' + } -class ESI(object): # @todo: move this to settings clientCallback = 'http://localhost:6461' clientTest = True + esiapp = None + esi_v1 = None + esi_v4 = None + _instance = None + @classmethod + def initEsiApp(cls): + with Timer("Main EsiApp") as t: + cls.esiapp = EsiApp(cache=file_cache) + with Timer('ESI v1') as t: + cls.esi_v1 = cls.esiapp.get_v1_swagger + with Timer('ESI v4') as t: + cls.esi_v4 = cls.esiapp.get_v4_swagger + + # esiRdy.set() + + @classmethod + def genEsiClient(cls, security=None): + return EsiClient( + security=EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) if security is None else security, + cache=file_cache, + headers={'User-Agent': 'pyfa esipy'} + ) + @classmethod def getInstance(cls): if cls._instance is None: - cls._instance = ESI() + cls._instance = Esi() return cls._instance + @classmethod + def restartService(cls): + # This is here to reseed pycrest values when changing preferences + # We first stop the server n case one is running, as creating a new + # instance doesn't do this. + if cls._instance.httpd: + cls._instance.stopServer() + cls._instance = Esi() + cls._instance.mainFrame.updateCrestMenus(type=cls._instance.settings.get('mode')) + return cls._instance def __init__(self): - pass + """ + A note on login/logout events: the character login events happen + whenever a characters is logged into via the SSO, regardless of mod. + However, the mode should be send as an argument. Similarily, + the Logout even happens whenever the character is deleted for either + mode. The mode is sent as an argument, as well as the umber of + characters still in the cache (if USER mode) + """ + Esi.initEsiApp() + + + # prefetch = EsiInitThread() + # prefetch.daemon = True + # prefetch.start() + + self.settings = CRESTSettings.getInstance() + self.scopes = ['characterFittingsRead', 'characterFittingsWrite'] + + # these will be set when needed + self.httpd = None + self.state = None + self.ssoTimer = None + + self.eve_options = { + '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 + } + + # Base EVE connection that is copied to all characters + self.eve = EVE(**self.eve_options) + + self.implicitCharacter = None + + # The database cache does not seem to be working for some reason. Use + # this as a temporary measure + self.charCache = {} + + # need these here to post events + import gui.mainFrame # put this here to avoid loop + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + @property + def isTestServer(self): + return self.settings.get('server') == Servers.SISI + + def delCrestCharacter(self, charID): + char = eos.db.getSsoCharacter(charID) + del self.charCache[char.ID] + eos.db.remove(char) + wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=len(self.charCache))) + + def delAllCharacters(self): + chars = eos.db.getSsoCharacters() + for char in chars: + eos.db.remove(char) + self.charCache = {} + wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=0)) + + def getSsoCharacters(self): + chars = eos.db.getSsoCharacters(config.getClientSecret()) + return chars + + def getSsoCharacter(self, charID): + """ + Get character, and modify to include the eve connection + """ + char = eos.db.getSsoCharacter(charID, config.getClientSecret()) + if char.esi_client is None: + char.esi_client = Esi.genEsiClient() + char.esi_client.security.update_token(char.get_sso_data()) + return char + + def getFittings(self, charID): + char = self.getSsoCharacter(charID) + print(repr(char)) + op = Esi.esi_v1.op['get_characters_character_id_fittings']( + character_id=charID + ) + resp = char.esi_client.request(op) + return resp.data + + def postFitting(self, charID, json_str): + # @todo: new fitting ID can be recovered from resp.data, + char = self.getSsoCharacter(charID) + + op = Esi.esi_v1.op['post_characters_character_id_fittings']( + character_id=char.characterID, + fitting=json.loads(json_str) + ) + + resp = char.esi_client.request(op) + + return resp.data + + def delFitting(self, charID, fittingID): + char = self.getSsoCharacter(charID) + print(repr(char)) + op = Esi.esi_v1.op['delete_characters_character_id_fittings_fitting_id']( + character_id=charID, + fitting_id=fittingID + ) + + resp = char.esi_client.request(op) + return resp.data + + + def logout(self): + """Logout of implicit character""" + pyfalog.debug("Character logout") + self.implicitCharacter = None + wx.PostEvent(self.mainFrame, GE.SsoLogout(type=self.settings.get('mode'))) + + def stopServer(self): + pyfalog.debug("Stopping Server") + self.httpd.stop() + self.httpd = None + + def startServer(self): + pyfalog.debug("Starting server") + + # we need this to ensure that the previous get_request finishes, and then the socket will close + if self.httpd: + self.stopServer() + time.sleep(1) + + self.state = str(uuid.uuid4()) + self.httpd = StoppableHTTPServer(('localhost', 0), AuthHandler) + port = self.httpd.socket.getsockname()[1] + + esisecurity = EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) + + uri = esisecurity.get_auth_uri(state=self.state, redirect='http://localhost:{}'.format(port)) + + self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleLogin,)) + self.serverThread.name = "SsoCallbackServer" + self.serverThread.daemon = True + self.serverThread.start() + + return uri + + def handleLogin(self, message): + if not message: + raise Exception("Could not parse out querystring parameters.") + + if message['state'][0] != self.state: + pyfalog.warn("OAUTH state mismatch") + raise Exception("OAUTH State Mismatch.") + + pyfalog.debug("Handling CREST login with: {0}", message) + + auth_response = json.loads(base64.b64decode(message['SSOInfo'][0])) + + # We need to preload the ESI Security object beforehand with the auth response so that we can use verify to + # get character information + # init the security object + esisecurity = EsiSecurityProxy(sso_url=config.ESI_AUTH_PROXY) + + esisecurity.update_token(auth_response) + + # we get the character information + cdata = esisecurity.verify() + print(cdata) + + currentCharacter = self.getSsoCharacter(cdata['CharacterID']) + + if currentCharacter is None: + currentCharacter = SsoCharacter(cdata['CharacterID'], cdata['CharacterName'], config.getClientSecret()) + currentCharacter.esi_client = Esi.genEsiClient(esisecurity) + currentCharacter.update_token(auth_response) # this also sets the esi security token + + eos.db.save(currentCharacter) + + wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.USER)) # todo: remove user / implicit authentication diff --git a/service/port.py b/service/port.py index c3cf352c7..8f2851bf6 100644 --- a/service/port.py +++ b/service/port.py @@ -49,7 +49,7 @@ from service.market import Market from utils.strfunctions import sequential_rep, replace_ltgt from abc import ABCMeta, abstractmethod -from service.crest import Crest +from service.esi import Esi from collections import OrderedDict pyfalog = Logger(__name__) @@ -388,7 +388,7 @@ class Port(object): nested_dict = lambda: collections.defaultdict(nested_dict) fit = nested_dict() - sCrest = Crest.getInstance() + sCrest = Esi.getInstance() sFit = svcFit.getInstance() eve = sCrest.eve From 33bf5234d09a22af5a7d5cfba7f24a22520c0dd3 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Thu, 8 Feb 2018 23:05:01 -0500 Subject: [PATCH 114/212] Goodbye eveapi! You have served us well all these years! Start stripping XML API stuff and implement ESI skill fetching. --- eos/db/saveddata/character.py | 8 +- eos/db/saveddata/queries.py | 2 +- eos/saveddata/character.py | 5 +- .../pyfaCrestPreferences.py | 2 - gui/characterEditor.py | 4 +- gui/characterSelection.py | 8 +- gui/crestFittings.py | 6 +- service/character.py | 129 +-- service/esi.py | 133 +-- service/eveapi.py | 1016 ----------------- service/pycrest/__init__.py | 1 - service/pycrest/compat.py | 24 - service/pycrest/errors.py | 2 - service/pycrest/eve.py | 318 ------ service/pycrest/weak_ciphers.py | 132 --- 15 files changed, 93 insertions(+), 1697 deletions(-) delete mode 100644 service/eveapi.py delete mode 100644 service/pycrest/__init__.py delete mode 100644 service/pycrest/compat.py delete mode 100644 service/pycrest/errors.py delete mode 100644 service/pycrest/eve.py delete mode 100644 service/pycrest/weak_ciphers.py diff --git a/eos/db/saveddata/character.py b/eos/db/saveddata/character.py index c87817541..8bf7d59c3 100644 --- a/eos/db/saveddata/character.py +++ b/eos/db/saveddata/character.py @@ -27,14 +27,12 @@ from eos.effectHandlerHelpers import HandledImplantBoosterList from eos.saveddata.implant import Implant from eos.saveddata.user import User from eos.saveddata.character import Character, Skill +from eos.saveddata.ssocharacter import SsoCharacter characters_table = Table("characters", saveddata_meta, Column("ID", Integer, primary_key=True), Column("name", String, nullable=False), - Column("apiID", Integer), - Column("apiKey", String), - Column("defaultChar", Integer), - Column("chars", String, nullable=True), + Column("ssoCharacterID", ForeignKey("ssoCharacter.ID"), nullable=True), Column("defaultLevel", Integer, nullable=True), Column("alphaCloneID", Integer, nullable=True), Column("ownerID", ForeignKey("users.ID"), nullable=True), @@ -62,6 +60,6 @@ mapper(Character, characters_table, single_parent=True, primaryjoin=charImplants_table.c.charID == characters_table.c.ID, secondaryjoin=charImplants_table.c.implantID == Implant.ID, - secondary=charImplants_table), + secondary=charImplants_table) } ) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 087561070..e582eef87 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -479,7 +479,7 @@ def getSsoCharacter(lookfor, clientHash, eager=None): filter = SsoCharacter.client == clientHash if isinstance(lookfor, int): - filter = and_(filter, SsoCharacter.characterID == lookfor) + filter = and_(filter, SsoCharacter.ID == lookfor) elif isinstance(lookfor, str): filter = and_(filter, SsoCharacter.characterName == lookfor) else: diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 278a05d92..77551a5ed 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -119,12 +119,9 @@ class Character(object): return all0 - def apiUpdateCharSheet(self, skills, secStatus=0): + def clearSkills(self): del self.__skills[:] self.__skillIdMap.clear() - for skillRow in skills: - self.addSkill(Skill(self, skillRow["typeID"], skillRow["level"])) - self.secStatus = secStatus @property def ro(self): diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py index f3da3dbeb..c94cc1328 100644 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ b/gui/builtinPreferenceViews/pyfaCrestPreferences.py @@ -119,11 +119,9 @@ class PFCrestPref(PreferenceView): def OnModeChange(self, event): self.settings.set('mode', event.GetInt()) self.ToggleProxySettings(self.settings.get('mode')) - Esi.restartService() def OnServerChange(self, event): self.settings.set('server', event.GetInt()) - Esi.restartService() def OnBtnApply(self, event): self.settings.set('clientID', self.inputClientID.GetValue().strip()) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 080940c7d..f8b79ae8c 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -158,11 +158,11 @@ class CharacterEditor(wx.Frame): self.sview = SkillTreeView(self.viewsNBContainer) self.iview = ImplantEditorView(self.viewsNBContainer, self) - self.aview = APIView(self.viewsNBContainer) + # self.aview = APIView(self.viewsNBContainer) self.viewsNBContainer.AddPage(self.sview, "Skills") self.viewsNBContainer.AddPage(self.iview, "Implants") - self.viewsNBContainer.AddPage(self.aview, "API") + # self.viewsNBContainer.AddPage(self.aview, "API") mainSizer.Add(self.viewsNBContainer, 1, wx.EXPAND | wx.ALL, 5) diff --git a/gui/characterSelection.py b/gui/characterSelection.py index aba1c68fb..a75306436 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -151,9 +151,7 @@ class CharacterSelection(wx.Panel): def refreshApi(self, event): self.btnRefresh.Enable(False) sChar = Character.getInstance() - ID, key, charName, chars = sChar.getApiDetails(self.getActiveCharacter()) - if charName: - sChar.apiFetch(self.getActiveCharacter(), charName, self.refreshAPICallback) + sChar.apiFetch(self.getActiveCharacter(), self.refreshAPICallback) def refreshAPICallback(self, e=None): self.btnRefresh.Enable(True) @@ -178,7 +176,9 @@ class CharacterSelection(wx.Panel): self.charChoice.SetSelection(self.charCache) self.mainFrame.showCharacterEditor(event) return - if sChar.getCharName(charID) not in ("All 0", "All 5") and sChar.apiEnabled(charID): + + char = sChar.getCharacter(charID) + if sChar.getCharName(charID) not in ("All 0", "All 5") and char.ssoCharacterID is not None: self.btnRefresh.Enable(True) else: self.btnRefresh.Enable(False) diff --git a/gui/crestFittings.py b/gui/crestFittings.py index 69e8b6085..ed8750355 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -99,7 +99,7 @@ class CrestFittings(wx.Frame): self.charChoice.Clear() for char in chars: - self.charChoice.Append(char.characterName, char.characterID) + self.charChoice.Append(char.characterName, char.ID) self.charChoice.SetSelection(0) @@ -220,7 +220,7 @@ class ExportToEve(wx.Frame): self.charChoice.Clear() for char in chars: - self.charChoice.Append(char.characterName, char.characterID) + self.charChoice.Append(char.characterName, char.ID) self.charChoice.SetSelection(0) @@ -344,7 +344,7 @@ class CrestMgmt(wx.Dialog): if item > -1: charID = self.lcCharacters.GetItemData(item) sCrest = Esi.getInstance() - sCrest.delCrestCharacter(charID) + sCrest.delSsoCharacter(charID) self.popCharList() diff --git a/service/character.py b/service/character.py index 0a36bb42a..b6562032e 100644 --- a/service/character.py +++ b/service/character.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . # ============================================================================= - import sys import copy import itertools @@ -34,10 +33,10 @@ import wx import config import eos.db -from service.eveapi import EVEAPIConnection, ParseXML +from service.esi import Esi from eos.saveddata.implant import Implant as es_Implant -from eos.saveddata.character import Character as es_Character +from eos.saveddata.character import Character as es_Character, Skill from eos.saveddata.module import Slot as es_Slot, Module as es_Module from eos.saveddata.fighter import Fighter as es_Fighter @@ -52,6 +51,9 @@ class CharacterImportThread(threading.Thread): self.callback = callback def run(self): + wx.CallAfter(self.callback) + # todo: Fix character import (don't need CCP SML anymore, only support evemon?) + return paths = self.paths sCharacter = Character.getInstance() all5_character = es_Character("All 5", 5) @@ -62,43 +64,34 @@ class CharacterImportThread(threading.Thread): for path in paths: try: - # we try to parse api XML data first - with open(path, mode='r') as charFile: - sheet = ParseXML(charFile) - char = sCharacter.new(sheet.name + " (imported)") - sCharacter.apiUpdateCharSheet(char.ID, sheet.skills) - except: - # if it's not api XML data, try this - # this is a horrible logic flow, but whatever - try: - charFile = open(path, mode='r').read() - doc = minidom.parseString(charFile) - if doc.documentElement.tagName not in ("SerializableCCPCharacter", "SerializableUriCharacter"): - pyfalog.error("Incorrect EVEMon XML sheet") - raise RuntimeError("Incorrect EVEMon XML sheet") - name = doc.getElementsByTagName("name")[0].firstChild.nodeValue - securitystatus = doc.getElementsByTagName("securityStatus")[0].firstChild.nodeValue or 0 - skill_els = doc.getElementsByTagName("skill") - skills = [] - for skill in skill_els: - if int(skill.getAttribute("typeID")) in all_skill_ids and (0 <= int(skill.getAttribute("level")) <= 5): - skills.append({ - "typeID": int(skill.getAttribute("typeID")), - "level": int(skill.getAttribute("level")), - }) - else: - pyfalog.error( - "Attempted to import unknown skill {0} (ID: {1}) (Level: {2})", - skill.getAttribute("name"), - skill.getAttribute("typeID"), - skill.getAttribute("level"), - ) - char = sCharacter.new(name + " (EVEMon)") - sCharacter.apiUpdateCharSheet(char.ID, skills, securitystatus) - except Exception as e: - pyfalog.error("Exception on character import:") - pyfalog.error(e) - continue + charFile = open(path, mode='r').read() + doc = minidom.parseString(charFile) + if doc.documentElement.tagName not in ("SerializableCCPCharacter", "SerializableUriCharacter"): + pyfalog.error("Incorrect EVEMon XML sheet") + raise RuntimeError("Incorrect EVEMon XML sheet") + name = doc.getElementsByTagName("name")[0].firstChild.nodeValue + securitystatus = doc.getElementsByTagName("securityStatus")[0].firstChild.nodeValue or 0 + skill_els = doc.getElementsByTagName("skill") + skills = [] + for skill in skill_els: + if int(skill.getAttribute("typeID")) in all_skill_ids and (0 <= int(skill.getAttribute("level")) <= 5): + skills.append({ + "typeID": int(skill.getAttribute("typeID")), + "level": int(skill.getAttribute("level")), + }) + else: + pyfalog.error( + "Attempted to import unknown skill {0} (ID: {1}) (Level: {2})", + skill.getAttribute("name"), + skill.getAttribute("typeID"), + skill.getAttribute("level"), + ) + char = sCharacter.new(name + " (EVEMon)") + sCharacter.apiUpdateCharSheet(char.ID, skills, securitystatus) + except Exception as e: + pyfalog.error("Exception on character import:") + pyfalog.error(e) + continue wx.CallAfter(self.callback) @@ -344,6 +337,8 @@ class Character(object): @staticmethod def getApiDetails(charID): + # todo: fix this (or get rid of?) + return ("", "", "", []) char = eos.db.getCharacter(charID) if char.chars is not None: chars = json.loads(char.chars) @@ -351,27 +346,8 @@ class Character(object): chars = None 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 "" - - @staticmethod - def apiCharList(charID, userID, apiKey): - char = eos.db.getCharacter(charID) - - char.apiID = userID - char.apiKey = apiKey - - api = EVEAPIConnection() - auth = api.auth(keyID=userID, vCode=apiKey) - apiResult = auth.account.Characters() - charList = [str(c.name) for c in apiResult.characters] - - char.chars = json.dumps(charList) - return charList - - def apiFetch(self, charID, charName, callback): - thread = UpdateAPIThread(charID, charName, (self.apiFetchCallback, callback)) + def apiFetch(self, charID, callback): + thread = UpdateAPIThread(charID, (self.apiFetchCallback, callback)) thread.start() def apiFetchCallback(self, guiCallback, e=None): @@ -469,35 +445,30 @@ class Character(object): class UpdateAPIThread(threading.Thread): - def __init__(self, charID, charName, callback): + def __init__(self, charID, callback): threading.Thread.__init__(self) self.name = "CheckUpdate" self.callback = callback self.charID = charID - self.charName = charName def run(self): try: - dbChar = eos.db.getCharacter(self.charID) - dbChar.defaultChar = self.charName + char = eos.db.getCharacter(self.charID) - api = EVEAPIConnection() - auth = api.auth(keyID=dbChar.apiID, vCode=dbChar.apiKey) - apiResult = auth.account.Characters() - charID = None - for char in apiResult.characters: - if char.name == self.charName: - charID = char.characterID - break + sEsi = Esi.getInstance() + resp = sEsi.getSkills(char.ssoCharacterID) - if charID is None: - return + # todo: check if alpha. if so, pop up a question if they want to apply it as alpha. Use threading events to set the answer? + char.clearSkills() + for skillRow in resp["skills"]: + char.addSkill(Skill(char, skillRow["skill_id"], skillRow["trained_skill_level"])) - sheet = auth.character(charID).CharacterSheet() - charInfo = api.eve.CharacterInfo(characterID=charID) + resp = sEsi.getSecStatus(char.ssoCharacterID) + + char.secStatus = resp['security_status'] - dbChar.apiUpdateCharSheet(sheet.skills, charInfo.securityStatus) self.callback[0](self.callback[1]) - except Exception: + except Exception as ex: + pyfalog.warn(ex) self.callback[0](self.callback[1], sys.exc_info()) diff --git a/service/esi.py b/service/esi.py index d065ffb90..3e47af41a 100644 --- a/service/esi.py +++ b/service/esi.py @@ -2,12 +2,12 @@ import wx from logbook import Logger import threading -import copy import uuid import time import config import base64 import json +import os import eos.db from eos.enum import Enum @@ -15,18 +15,13 @@ from eos.saveddata.ssocharacter import SsoCharacter import gui.globalEvents as GE from service.settings import CRESTSettings from service.server import StoppableHTTPServer, AuthHandler -from service.pycrest.eve import EVE from .esi_security_proxy import EsiSecurityProxy from esipy import EsiClient, EsiApp from esipy.cache import FileCache -import os -import logging - pyfalog = Logger(__name__) -server = "https://blitzmann.pythonanywhere.com" cache_path = os.path.join(config.savePath, config.ESI_CACHE) if not os.path.exists(cache_path): @@ -34,32 +29,13 @@ if not os.path.exists(cache_path): file_cache = FileCache(cache_path) -esiRdy = threading.Event() - class Servers(Enum): TQ = 0 SISI = 1 -class CrestModes(Enum): - IMPLICIT = 0 - USER = 1 - -from utils.timer import Timer - - - class Esi(object): - clientIDs = { - Servers.TQ : 'f9be379951c046339dc13a00e6be7704', - Servers.SISI: 'af87365240d644f7950af563b8418bad' - } - - # @todo: move this to settings - clientCallback = 'http://localhost:6461' - clientTest = True - esiapp = None esi_v1 = None esi_v4 = None @@ -68,12 +44,9 @@ class Esi(object): @classmethod def initEsiApp(cls): - with Timer("Main EsiApp") as t: - cls.esiapp = EsiApp(cache=file_cache) - with Timer('ESI v1') as t: - cls.esi_v1 = cls.esiapp.get_v1_swagger - with Timer('ESI v4') as t: - cls.esi_v4 = cls.esiapp.get_v4_swagger + cls.esiapp = EsiApp(cache=file_cache) + cls.esi_v1 = cls.esiapp.get_v1_swagger + cls.esi_v4 = cls.esiapp.get_v4_swagger # esiRdy.set() @@ -92,51 +65,16 @@ class Esi(object): return cls._instance - @classmethod - def restartService(cls): - # This is here to reseed pycrest values when changing preferences - # We first stop the server n case one is running, as creating a new - # instance doesn't do this. - if cls._instance.httpd: - cls._instance.stopServer() - cls._instance = Esi() - cls._instance.mainFrame.updateCrestMenus(type=cls._instance.settings.get('mode')) - return cls._instance - def __init__(self): - """ - A note on login/logout events: the character login events happen - whenever a characters is logged into via the SSO, regardless of mod. - However, the mode should be send as an argument. Similarily, - the Logout even happens whenever the character is deleted for either - mode. The mode is sent as an argument, as well as the umber of - characters still in the cache (if USER mode) - """ Esi.initEsiApp() - - # prefetch = EsiInitThread() - # prefetch.daemon = True - # prefetch.start() - self.settings = CRESTSettings.getInstance() - self.scopes = ['characterFittingsRead', 'characterFittingsWrite'] # these will be set when needed self.httpd = None self.state = None self.ssoTimer = None - self.eve_options = { - '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 - } - - # Base EVE connection that is copied to all characters - self.eve = EVE(**self.eve_options) - self.implicitCharacter = None # The database cache does not seem to be working for some reason. Use @@ -151,73 +89,61 @@ class Esi(object): def isTestServer(self): return self.settings.get('server') == Servers.SISI - def delCrestCharacter(self, charID): - char = eos.db.getSsoCharacter(charID) - del self.charCache[char.ID] + def delSsoCharacter(self, id): + char = eos.db.getSsoCharacter(id) eos.db.remove(char) - wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=len(self.charCache))) - - def delAllCharacters(self): - chars = eos.db.getSsoCharacters() - for char in chars: - eos.db.remove(char) - self.charCache = {} - wx.PostEvent(self.mainFrame, GE.SsoLogout(type=CrestModes.USER, numChars=0)) def getSsoCharacters(self): chars = eos.db.getSsoCharacters(config.getClientSecret()) return chars - def getSsoCharacter(self, charID): + def getSsoCharacter(self, id): """ Get character, and modify to include the eve connection """ - char = eos.db.getSsoCharacter(charID, config.getClientSecret()) - if char.esi_client is None: + char = eos.db.getSsoCharacter(id, config.getClientSecret()) + if char is not None and char.esi_client is None: char.esi_client = Esi.genEsiClient() char.esi_client.security.update_token(char.get_sso_data()) return char - def getFittings(self, charID): - char = self.getSsoCharacter(charID) - print(repr(char)) - op = Esi.esi_v1.op['get_characters_character_id_fittings']( - character_id=charID - ) + def getSkills(self, id): + char = self.getSsoCharacter(id) + op = Esi.esi_v4.op['get_characters_character_id_skills'](character_id=char.characterID) resp = char.esi_client.request(op) return resp.data - def postFitting(self, charID, json_str): - # @todo: new fitting ID can be recovered from resp.data, - char = self.getSsoCharacter(charID) + def getSecStatus(self, id): + char = self.getSsoCharacter(id) + op = Esi.esi_v4.op['get_characters_character_id'](character_id=char.characterID) + resp = char.esi_client.request(op) + return resp.data + def getFittings(self, id): + char = self.getSsoCharacter(id) + op = Esi.esi_v1.op['get_characters_character_id_fittings'](character_id=char.characterID) + resp = char.esi_client.request(op) + return resp.data + + def postFitting(self, id, json_str): + # @todo: new fitting ID can be recovered from resp.data, + char = self.getSsoCharacter(id) op = Esi.esi_v1.op['post_characters_character_id_fittings']( character_id=char.characterID, fitting=json.loads(json_str) ) - resp = char.esi_client.request(op) - return resp.data - def delFitting(self, charID, fittingID): - char = self.getSsoCharacter(charID) - print(repr(char)) + def delFitting(self, id, fittingID): + char = self.getSsoCharacter(id) op = Esi.esi_v1.op['delete_characters_character_id_fittings_fitting_id']( - character_id=charID, + character_id=char.characterID, fitting_id=fittingID ) - resp = char.esi_client.request(op) return resp.data - - def logout(self): - """Logout of implicit character""" - pyfalog.debug("Character logout") - self.implicitCharacter = None - wx.PostEvent(self.mainFrame, GE.SsoLogout(type=self.settings.get('mode'))) - def stopServer(self): pyfalog.debug("Stopping Server") self.httpd.stop() @@ -278,4 +204,3 @@ class Esi(object): eos.db.save(currentCharacter) - wx.PostEvent(self.mainFrame, GE.SsoLogin(type=CrestModes.USER)) # todo: remove user / implicit authentication diff --git a/service/eveapi.py b/service/eveapi.py deleted file mode 100644 index a06cf2efe..000000000 --- a/service/eveapi.py +++ /dev/null @@ -1,1016 +0,0 @@ -# ----------------------------------------------------------------------------- -# eveapi - EVE Online API access -# -# Copyright (c)2007-2014 Jamie "Entity" van den Berge -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation -# files (the "Software"), to deal in the Software without -# restriction, including without limitation the rights to use, -# copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following -# conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# 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 -# to be used for any requests by the library. If this function is not used, -# a warning will be thrown for every API request. -# -# Version: 1.2.9 - 14 September 2013 -# - Updated error handling: Raise an AuthenticationError in case -# the API returns HTTP Status Code 403 - Forbidden -# -# Version: 1.2.8 - 9 August 2013 -# - the XML value cast function (_autocast) can now be changed globally to a -# custom one using the set_cast_func(func) module-level function. -# -# Version: 1.2.7 - 3 September 2012 -# - Added get() method to Row object. -# -# Version: 1.2.6 - 29 August 2012 -# - Added finer error handling + added setup.py to allow distributing eveapi -# through pypi. -# -# Version: 1.2.5 - 1 August 2012 -# - Row objects now have __hasattr__ and __contains__ methods -# -# Version: 1.2.4 - 12 April 2012 -# - API version of XML response now available as _meta.version -# -# Version: 1.2.3 - 10 April 2012 -# - fix for tags of the form -# -# Version: 1.2.2 - 27 February 2012 -# - fix for the workaround in 1.2.1. -# -# Version: 1.2.1 - 23 February 2012 -# - added workaround for row tags missing attributes that were defined -# in their rowset (this should fix ContractItems) -# -# Version: 1.2.0 - 18 February 2012 -# - fix handling of empty XML tags. -# - improved proxy support a bit. -# -# Version: 1.1.9 - 2 September 2011 -# - added workaround for row tags with attributes that were not defined -# in their rowset (this should fix AssetList) -# -# Version: 1.1.8 - 1 September 2011 -# - fix for inconsistent columns attribute in rowsets. -# -# Version: 1.1.7 - 1 September 2011 -# - auth() method updated to work with the new authentication scheme. -# -# Version: 1.1.6 - 27 May 2011 -# - Now supports composite keys for IndexRowsets. -# - Fixed calls not working if a path was specified in the root url. -# -# Version: 1.1.5 - 27 Januari 2011 -# - Now supports (and defaults to) HTTPS. Non-SSL proxies will still work by -# explicitly specifying http:// in the url. -# -# Version: 1.1.4 - 1 December 2010 -# - Empty explicit CDATA tags are now properly handled. -# - _autocast now receives the name of the variable it's trying to typecast, -# enabling custom/future casting functions to make smarter decisions. -# -# Version: 1.1.3 - 6 November 2010 -# - Added support for anonymous CDATA inside row tags. This makes the body of -# mails in the rows of char/MailBodies available through the .data attribute. -# -# Version: 1.1.2 - 2 July 2010 -# - Fixed __str__ on row objects to work properly with unicode strings. -# -# Version: 1.1.1 - 10 Januari 2010 -# - Fixed bug that causes nested tags to not appear in rows of rowsets created -# from normal Elements. This should fix the corp.MemberSecurity method, -# which now returns all data for members. [jehed] -# -# Version: 1.1.0 - 15 Januari 2009 -# - Added Select() method to Rowset class. Using it avoids the creation of -# temporary row instances, speeding up iteration considerably. -# - Added ParseXML() function, which can be passed arbitrary API XML file or -# string objects. -# - Added support for proxy servers. A proxy can be specified globally or -# per api connection instance. [suggestion by graalman] -# - Some minor refactoring. -# - Fixed deprecation warning when using Python 2.6. -# -# Version: 1.0.7 - 14 November 2008 -# - Added workaround for rowsets that are missing the (required!) columns -# attribute. If missing, it will use the columns found in the first row. -# Note that this is will still break when expecting columns, if the rowset -# is empty. [Flux/Entity] -# -# Version: 1.0.6 - 18 July 2008 -# - Enabled expat text buffering to avoid content breaking up. [BigWhale] -# -# Version: 1.0.5 - 03 February 2008 -# - Added workaround to make broken XML responses (like the "row:name" bug in -# eve/CharacterID) work as intended. -# - Bogus datestamps before the epoch in XML responses are now set to 0 to -# avoid breaking certain date/time functions. [Anathema Matou] -# -# Version: 1.0.4 - 23 December 2007 -# - Changed _autocast() to use timegm() instead of mktime(). [Invisible Hand] -# - Fixed missing attributes of elements inside rows. [Elandra Tenari] -# -# Version: 1.0.3 - 13 December 2007 -# - Fixed keyless columns bugging out the parser (in CorporationSheet for ex.) -# -# Version: 1.0.2 - 12 December 2007 -# - Fixed parser not working with indented XML. -# -# Version: 1.0.1 -# - Some micro optimizations -# -# Version: 1.0 -# - Initial release -# -# Requirements: -# Python 2.4+ -# -# ----------------------------------------------------------------------------- - - -# ----------------------------------------------------------------------------- -# This eveapi has been modified for pyfa. -# -# Specifically, the entire network request/response has been substituted for -# pyfa's own implementation in service.network -# -# Additionally, various other parts have been changed to support urllib2 -# responses instead of httplib -# ----------------------------------------------------------------------------- - - -import urllib.parse -import copy - -from xml.parsers import expat -from time import strptime -from calendar import timegm - -from service.network import Network - -proxy = None -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. - The function must have 2 arguments; key and value. It should return a - value or object of the type appropriate for the given attribute name/key. - func may be None and will cause the default _autocast function to be used. - """ - 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(Exception): - def __init__(self, code, message): - self.code = code - self.args = (message.rstrip("."),) - - def __unicode__(self): - return '%s [code=%s]' % (self.args[0], self.code) - - -class RequestError(Error): - pass - - -class AuthenticationError(Error): - pass - - -class ServerError(Error): - pass - - -def EVEAPIConnection(url="api.eveonline.com", cacheHandler=None, proxy=None, proxySSL=False): - # Creates an API object through which you can call remote functions. - # - # The following optional arguments may be provided: - # - # url - root location of the EVEAPI server - # - # proxy - (host,port) specifying a proxy server through which to request - # the API pages. Specifying a proxy overrides default proxy. - # - # proxySSL - True if the proxy requires SSL, False otherwise. - # - # cacheHandler - an object which must support the following interface: - # - # retrieve(host, path, params) - # - # Called when eveapi wants to fetch a document. - # host is the address of the server, path is the full path to - # the requested document, and params is a dict containing the - # parameters passed to this api call (keyID, vCode, etc). - # The method MUST return one of the following types: - # - # None - if your cache did not contain this entry - # str/unicode - eveapi will parse this as XML - # Element - previously stored object as provided to store() - # file-like object - eveapi will read() XML from the stream. - # - # store(host, path, params, doc, obj) - # - # Called when eveapi wants you to cache this item. - # You can use obj to get the info about the object (cachedUntil - # and currentTime, etc) doc is the XML document the object - # was generated from. It's generally best to cache the XML, not - # the object, unless you pickle the object. Note that this method - # will only be called if you returned None in the retrieve() for - # this object. - # - - if not url.startswith("http"): - url = "https://" + url - p = urllib.parse.urlparse(url, "https") - if p.path and p.path[-1] == "/": - p.path = p.path[:-1] - ctx = _RootContext(None, p.path, {}, {}) - ctx._handler = cacheHandler - ctx._scheme = p.scheme - ctx._host = p.netloc - ctx._proxy = proxy or globals()["proxy"] - ctx._proxySSL = proxySSL or globals()["proxySSL"] - return ctx - - -def ParseXML(file_or_string): - try: - return _ParseXML(file_or_string, False, None) - except TypeError: - raise TypeError("XML data must be provided as string or file-like object") - - -def _ParseXML(response, fromContext, storeFunc): - # pre/post-process XML or Element data - - if fromContext and isinstance(response, Element): - obj = response - elif type(response) in (str, str): - obj = _Parser().Parse(response, False) - elif hasattr(response, "read"): - obj = _Parser().Parse(response, True) - else: - raise TypeError("retrieve method must return None, string, file-like object or an Element instance") - - error = getattr(obj, "error", False) - if error: - if error.code >= 500: - raise ServerError(error.code, error.data) - elif error.code >= 200: - raise AuthenticationError(error.code, error.data) - elif error.code >= 100: - raise RequestError(error.code, error.data) - else: - raise Error(error.code, error.data) - - result = getattr(obj, "result", False) - if not result: - raise RuntimeError("API object does not contain result") - - if fromContext and storeFunc: - # call the cache handler to store this object - storeFunc(obj) - - # make metadata available to caller somehow - result._meta = obj - - return result - - -# ----------------------------------------------------------------------------- -# API Classes -# ----------------------------------------------------------------------------- - - -_listtypes = (list, tuple, dict) -_unspecified = [] - - -class _Context(object): - def __init__(self, root, path, parentDict, newKeywords=None): - self._root = root or self - self._path = path - if newKeywords: - if parentDict: - self.parameters = parentDict.copy() - else: - self.parameters = {} - self.parameters.update(newKeywords) - else: - self.parameters = parentDict or {} - - def context(self, *args, **kw): - if kw or args: - path = self._path - if args: - path += "/" + "/".join(args) - return self.__class__(self._root, path, self.parameters, kw) - else: - return self - - def __getattr__(self, this): - # perform arcane attribute majick trick - return _Context(self._root, self._path + "/" + this, self.parameters) - - def __call__(self, **kw): - if kw: - # specified keywords override contextual ones - for k, v in self.parameters.items(): - if k not in kw: - kw[k] = v - else: - # no keywords provided, just update with contextual ones. - kw.update(self.parameters) - - # now let the root context handle it further - return self._root(self._path, **kw) - - -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}) - - def corporation(self, characterID): - # same as character except for the folder "/corp" - 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) - raise ValueError("Must specify keyID and vCode") - - def setcachehandler(self, handler): - self._root._handler = handler - - def __call__(self, path, **kw): - # convert list type arguments to something the API likes - for k, v in kw.items(): - if isinstance(v, _listtypes): - kw[k] = ','.join(map(str, list(v))) - - cache = self._root._handler - - # now send the request - path += ".xml.aspx" - - if cache: - response = cache.retrieve(self._host, path, kw) - else: - response = None - - if response is None: - network = Network.getInstance() - - req = self._scheme + '://' + self._host + path - - response = network.request(req, network.EVE, kw) - - if cache: - store = True - response = response.read() - else: - store = False - else: - store = False - - retrieve_fallback = cache and getattr(cache, "retrieve_fallback", False) - if retrieve_fallback: - # implementor is handling fallbacks... - try: - 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: - return response - raise - else: - # 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. - try: - if value.strip("-").isdigit(): - return int(value) - except ValueError: - pass - - try: - return float(value) - except ValueError: - pass - - if len(value) == 19 and value[10] == ' ': - # it could be a date string - try: - return max(0, int(timegm(strptime(value, "%Y-%m-%d %H:%M:%S")))) - except OverflowError: - pass - except ValueError: - pass - - # 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 - p = expat.ParserCreate() - p.StartElementHandler = self.tag_start - p.CharacterDataHandler = self.tag_cdata - p.StartCdataSectionHandler = self.tag_cdatasection_enter - p.EndCdataSectionHandler = self.tag_cdatasection_exit - p.EndElementHandler = self.tag_end - p.ordered_attributes = True - p.buffer_text = True - - if isStream: - p.ParseFile(data) - else: - p.Parse(data, True) - return self.root - - def tag_cdatasection_enter(self): - # encountered an explicit CDATA tag. - self._cdata = True - - def tag_cdatasection_exit(self): - if self._cdata: - # explicit CDATA without actual data. expat doesn't seem - # to trigger an event for this case, so do it manually. - # (_cdata is set False by this call) - self.tag_cdata("") - else: - self._cdata = False - - def tag_start(self, name, attributes): - # - # If there's a colon in the tag name, cut off the name from the colon - # onward. This is a workaround to make certain bugged XML responses - # (such as eve/CharacterID.xml.aspx) work. - if ":" in name: - name = name[:name.index(":")] - # - - if name == "rowset": - # for rowsets, use the given name - try: - 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] - 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. - else: - this = Element() - this._name = name - - this.__parent = self.container - - if self.root is None: - # We're at the root. The first tag has to be "eveapi" or we can't - # really assume the rest of the xml is going to be what we expect. - if name != "eveapi": - raise RuntimeError("Invalid API response") - try: - this.version = attributes[attributes.index("version") + 1] - except KeyError: - raise RuntimeError("Invalid API response") - self.root = this - - if isinstance(self.container, Rowset) and (self.container.__catch == this._name): - # - # - check for missing columns attribute (see above). - # - check for missing row attributes. - # - check for extra attributes that were not defined in the rowset, - # 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 - 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 - for col in self.container._cols: - if col == attributes[row_idx]: - fixed.append(_castfunc(col, attributes[row_idx + 1])) - row_idx += 2 - else: - fixed.append(None) - hdr_idx += 1 - self.container.append(fixed) - else: - 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 range(0, len(attributes), 2)] - ) - # - - this._isrow = True - this._attributes = this._attributes2 = None - else: - this._isrow = False - this._attributes = attributes - this._attributes2 = [] - - self.container = self._last = this - self.has_cdata = False - - def tag_cdata(self, data): - self.has_cdata = True - if self._cdata: - # unset cdata flag to indicate it's been handled. - self._cdata = False - else: - if data in ("\r\n", "\n") or data.strip() != data: - return - - this = self.container - data = _castfunc(this._name, data) - - if this._isrow: - # sigh. anonymous data inside rows makes Entity cry. - # for the love of Jove, CCP, learn how to use rowsets. - parent = this.__parent - _row = parent._rows[-1] - _row.append(data) - if len(parent._cols) < len(_row): - parent._cols.append("data") - - elif this._attributes: - # this tag has attributes, so we can't simply assign the cdata - # as an attribute to the parent tag, as we'll lose the current - # tag's attributes then. instead, we'll assign the data as - # attribute of this tag. - this.data = data - else: - # this was a simple data without attributes. - # we won't be doing anything with this actual tag so we can just - # bind it to its parent (done by __tag_end) - setattr(this.__parent, this._name, data) - - def tag_end(self, name): - this = self.container - - if this is self.root: - del this._attributes - # this.__dict__.pop("_attributes", None) - return - - # we're done with current tag, so we can pop it off. This means that - # self.container will now point to the container of element 'this'. - self.container = this.__parent - del this.__parent - - attributes = this.__dict__.pop("_attributes") - attributes2 = this.__dict__.pop("_attributes2") - if attributes is None: - # already processed this tag's closure early, in tag_start() - return - - if self.container._isrow: - # Special case here. tags inside a row! Such tags have to be - # added as attributes of the row. - parent = self.container.__parent - - # get the row line for this element from its parent rowset - _row = parent._rows[-1] - - # add this tag's value to the end of the row - _row.append(getattr(self.container, this._name, this)) - - # fix columns if neccessary. - if len(parent._cols) < len(_row): - parent._cols.append(this._name) - else: - # see if there's already an attribute with this name (this shouldn't - # really happen, but it doesn't hurt to handle this case! - sibling = getattr(self.container, this._name, None) - if sibling is None: - if (not self.has_cdata) and (self._last is this) and (name != "rowset"): - if attributes: - # tag of the form - e = Element() - e._name = this._name - setattr(self.container, this._name, e) - for i in range(0, len(attributes), 2): - setattr(e, attributes[i], attributes[i + 1]) - else: - # tag of the form: , treat as empty string. - setattr(self.container, this._name, "") - else: - self.container._attributes2.append(this._name) - setattr(self.container, this._name, this) - - # Note: there aren't supposed to be any NON-rowset tags containing - # 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 range(0, len(attributes), 2)] - row.extend([getattr(this, col) for col in attributes2]) - sibling.append(row) - elif isinstance(sibling, Element): - # parent attribute is an element. This means we're dealing - # with multiple of the same sub-tag. Change the attribute - # 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 range(0, len(attributes), 2)] + \ - [getattr(this, col) for col in attributes2] - rs.append(row) - row = [getattr(sibling, attributes[i]) for i in range(0, len(attributes), 2)] + \ - [getattr(sibling, col) for col in attributes2] - rs.append(row) - rs._cols = [attributes[i] for i in range(0, len(attributes), 2)] + [col for col in attributes2] - setattr(self.container, this._name, rs) - else: - # something else must have set this attribute already. - # (typically the data case in tag_data()) - pass - - # Now fix up the attributes and be done with it. - for i in range(0, len(attributes), 2): - 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): - _name = None - - # Element is a namespace for attributes and nested tags - def __str__(self): - return "" % self._name - - -_fmt = "%s:%s".__mod__ - - -def _cmp(self, a, b): - return (a > b) - (a < b) - - -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 - # column name. - # - # To conserve resources, Row objects are only created on-demand. This is - # typically done by Rowsets (e.g. when iterating over the rowset). - - def __init__(self, cols=None, row=None): - self._cols = cols or [] - self._row = row or [] - - def __bool__(self): - return True - - def __ne__(self, other): - return self.__cmp__(other) - - def __eq__(self, other): - return self.__cmp__(other) == 0 - - def __cmp__(self, other): - if type(other) != type(self): - raise TypeError("Incompatible comparison type") - return _cmp(self._cols, other._cols) or _cmp(self._row, other._row) - - def __hasattr__(self, this): - if this in self._cols: - return self._cols.index(this) < len(self._row) - return False - - __contains__ = __hasattr__ - - def get(self, this, default=None): - if (this in self._cols) and (self._cols.index(this) < len(self._row)): - return self._row[self._cols.index(this)] - return default - - def __getattr__(self, this): - try: - return self._row[self._cols.index(this)] - except: - raise AttributeError(this) - - def __getitem__(self, this): - return self._row[self._cols.index(this)] - - def __str__(self): - return "Row(" + ','.join(map(_fmt, list(zip(self._cols, self._row)))) + ")" - - -class Rowset(object): - # Rowsets are collections of Row objects. - # - # Rowsets support most of the list interface: - # iteration, indexing and slicing - # - # As well as the following methods: - # - # IndexedBy(column) - # Returns an IndexRowset keyed on given column. Requires the column to - # be usable as primary key. - # - # GroupedBy(column) - # Returns a FilterRowset keyed on given column. FilterRowset objects - # can be accessed like dicts. See FilterRowset class below. - # - # SortBy(column, reverse=True) - # Sorts rowset in-place on given column. for a descending sort, - # specify reversed=True. - # - # SortedBy(column, reverse=True) - # Same as SortBy, except this returns a new rowset object instead of - # sorting in-place. - # - # Select(columns, row=False) - # Yields a column values tuple (value, ...) for each row in the rowset. - # If only one column is requested, then just the column value is - # provided instead of the values tuple. - # When row=True, each result will be decorated with the entire row. - # - - def IndexedBy(self, column): - return IndexRowset(self._cols, self._rows, column) - - def GroupedBy(self, column): - return FilterRowset(self._cols, self._rows, column) - - def SortBy(self, column, reverse=False): - ix = self._cols.index(column) - self.sort(key=lambda e: e[ix], reverse=reverse) - - def SortedBy(self, column, reverse=False): - rs = self[:] - rs.SortBy(column, reverse) - return rs - - def Select(self, *columns, **options): - if len(columns) == 1: - i = self._cols.index(columns[0]) - if options.get("row", False): - for line in self._rows: - yield (line, line[i]) - else: - for line in self._rows: - yield line[i] - else: - i = list(map(self._cols.index, columns)) - if options.get("row", False): - for line in self._rows: - yield line, [line[x] for x in i] - else: - for line in self._rows: - yield [line[x] for x in i] - - # ------------- - - def __init__(self, cols=None, rows=None): - self._cols = cols or [] - self._rows = rows or [] - - def append(self, row): - if isinstance(row, list): - self._rows.append(row) - elif isinstance(row, Row) and len(row._cols) == len(self._cols): - self._rows.append(row._row) - else: - raise TypeError("incompatible row type") - - def __add__(self, other): - if isinstance(other, Rowset): - if len(other._cols) == len(self._cols): - self._rows += other._rows - raise TypeError("rowset instance expected") - - def __bool__(self): - return not not self._rows - - def __len__(self): - return len(self._rows) - - def copy(self): - return self[:] - - def __getitem__(self, ix): - if type(ix) is slice: - return Rowset(self._cols, self._rows[ix]) - return Row(self._cols, self._rows[ix]) - - def sort(self, *args, **kw): - self._rows.sort(*args, **kw) - - def __str__(self): - return "Rowset(columns=[%s], rows=%d)" % (','.join(self._cols), len(self)) - - def __getstate__(self): - return self._cols, self._rows - - def __setstate__(self, state): - self._cols, self._rows = state - - -class IndexRowset(Rowset): - # An IndexRowset is a Rowset that keeps an index on a column. - # - # The interface is the same as Rowset, but provides an additional method: - # - # Get(key [, default]) - # Returns the Row mapped to provided key in the index. If there is no - # such key in the index, KeyError is raised unless a default value was - # specified. - # - - def Get(self, key, *default): - row = self._items.get(key, None) - if row is None: - if default: - return default[0] - raise KeyError(key) - return Row(self._cols, row) - - # ------------- - - def __init__(self, cols=None, rows=None, key=None): - try: - if "," in key: - self._ki = ki = [cols.index(k) for k in key.split(",")] - self.composite = True - else: - self._ki = ki = cols.index(key) - self.composite = False - except IndexError: - raise ValueError("Rowset has no column %s" % key) - - Rowset.__init__(self, cols, rows) - self._key = key - - if self.composite: - self._items = dict((tuple([row[k] for k in ki]), row) for row in self._rows) - else: - self._items = dict((row[ki], row) for row in self._rows) - - def __getitem__(self, ix): - if type(ix) is slice: - return IndexRowset(self._cols, self._rows[ix], self._key) - return Rowset.__getitem__(self, ix) - - def append(self, row): - Rowset.append(self, row) - if self.composite: - self._items[tuple([row[k] for k in self._ki])] = row - else: - self._items[row[self._ki]] = row - - def __getstate__(self): - return Rowset.__getstate__(self), self._items, self._ki - - def __setstate__(self, state): - state, self._items, self._ki = state - Rowset.__setstate__(self, state) - - -class FilterRowset(object): - # A FilterRowset works much like an IndexRowset, with the following - # differences: - # - FilterRowsets are accessed much like dicts - # - 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_ - 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) - else: - items[id_] = [row] - else: - idfield2 = cols.index(key2) - for row in rows: - id_ = row[idfield] - if id_ in items: - items[id_][row[idfield2]] = row - else: - items[id_] = {row[idfield2]: row} - - self._cols = cols - self.key = key - self.key2 = key2 - self._bind() - - def _bind(self): - items = self._items - self.keys = items.keys - self.iterkeys = items.iterkeys - self.__contains__ = items.__contains__ - self.has_key = items.has_key - self.__len__ = items.__len__ - self.__iter__ = items.__iter__ - - def copy(self): - return FilterRowset(self._cols[:], None, self.key, self.key2, dict_=copy.deepcopy(self._items)) - - def get(self, key, default=_unspecified): - try: - return self[key] - except KeyError: - if default is _unspecified: - raise - return default - - def __getitem__(self, i): - if self.key2: - return IndexRowset(self._cols, None, self.key2, self._items.get(i, {})) - return Rowset(self._cols, self._items[i]) - - def __getstate__(self): - return self._cols, self._rows, self._items, self.key, self.key2 - - def __setstate__(self, state): - self._cols, self._rows, self._items, self.key, self.key2 = state - self._bind() diff --git a/service/pycrest/__init__.py b/service/pycrest/__init__.py deleted file mode 100644 index 5820a7290..000000000 --- a/service/pycrest/__init__.py +++ /dev/null @@ -1 +0,0 @@ -version = "0.0.1" diff --git a/service/pycrest/compat.py b/service/pycrest/compat.py deleted file mode 100644 index 8f4a35e0b..000000000 --- a/service/pycrest/compat.py +++ /dev/null @@ -1,24 +0,0 @@ -import sys - -PY3 = sys.version_info[0] == 3 - -if PY3: # pragma: no cover - string_types = str, - text_type = str - binary_type = bytes -else: # pragma: no cover - string_types = str, - text_type = str - binary_type = str - - -def text_(s, encoding='latin-1', errors='strict'): # pragma: no cover - if isinstance(s, binary_type): - return s.decode(encoding, errors) - return s - - -def bytes_(s, encoding='latin-1', errors='strict'): # pragma: no cover - if isinstance(s, text_type): - return s.encode(encoding, errors) - return s diff --git a/service/pycrest/errors.py b/service/pycrest/errors.py deleted file mode 100644 index 4216deabb..000000000 --- a/service/pycrest/errors.py +++ /dev/null @@ -1,2 +0,0 @@ -class APIException(Exception): - pass diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py deleted file mode 100644 index 9ea88ce23..000000000 --- a/service/pycrest/eve.py +++ /dev/null @@ -1,318 +0,0 @@ -import base64 -from logbook import Logger -import os -import re -import time -import zlib - -import requests -from requests.adapters import HTTPAdapter - -import config -from service.pycrest.compat import bytes_, text_ -from service.pycrest.errors import APIException - -from urllib.parse import urlparse, urlunparse, parse_qsl - -try: - import pickle -except ImportError: # pragma: no cover - # noinspection PyPep8Naming - import pickle as pickle - -pyfalog = Logger(__name__) -cache_re = re.compile(r'max-age=([0-9]+)') - - -class APICache(object): - def put(self, key, value): - raise NotImplementedError - - def get(self, key): - raise NotImplementedError - - def invalidate(self, key): - raise NotImplementedError - - -class FileCache(APICache): - def __init__(self, path): - self._cache = {} - self.path = path - if not os.path.isdir(self.path): - os.mkdir(self.path, 0o700) - - def _getpath(self, key): - return os.path.join(self.path, str(hash(key)) + '.cache') - - def put(self, key, value): - with open(self._getpath(key), 'wb') as f: - f.write(zlib.compress(pickle.dumps(value, -1))) - self._cache[key] = value - - def get(self, key): - if key in self._cache: - return self._cache[key] - - try: - with open(self._getpath(key), 'rb') as f: - return pickle.loads(zlib.decompress(f.read())) - except IOError as ex: - pyfalog.debug("IO error opening zip file. (May not exist yet)") - if ex.errno == 2: # file does not exist (yet) - return None - else: - raise - - def invalidate(self, key): - self._cache.pop(key, None) - - try: - os.unlink(self._getpath(key)) - except OSError as ex: - pyfalog.debug("Caught exception in invalidate") - pyfalog.debug(ex) - if ex.errno == 2: # does not exist - pass - else: - raise - - -class DictCache(APICache): - def __init__(self): - self._dict = {} - - def get(self, key): - return self._dict.get(key, None) - - def put(self, key, value): - self._dict[key] = value - - def invalidate(self, key): - self._dict.pop(key, None) - - -class APIConnection(object): - def __init__(self, additional_headers=None, user_agent=None, cache_dir=None, cache=None): - # Set up a Requests Session - session = requests.Session() - if additional_headers is None: - additional_headers = {} - if user_agent is None: - user_agent = "pyfa/{0} ({1})".format(config.version, config.tag) - session.headers.update({ - "User-Agent": user_agent, - "Accept": "application/json", - }) - session.headers.update(additional_headers) - session.mount('https://public-crest.eveonline.com', HTTPAdapter()) - self._session = session - if cache: - if isinstance(cache, APICache): - self.cache = cache # Inherit from parents - elif isinstance(cache, type): - self.cache = cache() # Instantiate a new cache - elif cache_dir: - self.cache_dir = cache_dir - self.cache = FileCache(self.cache_dir) - else: - self.cache = DictCache() - - def get(self, resource, params=None): - pyfalog.debug('Getting resource {0}', resource) - if params is None: - params = {} - - # remove params from resource URI (needed for paginated stuff) - parsed_uri = urlparse(resource) - qs = parsed_uri.query - resource = urlunparse(parsed_uri._replace(query='')) - prms = {} - for tup in parse_qsl(qs): - prms[tup[0]] = tup[1] - - # params supplied to self.get() override parsed params - for key in params: - prms[key] = params[key] - - # check cache - key = (resource, frozenset(list(self._session.headers.items())), frozenset(list(prms.items()))) - cached = self.cache.get(key) - if cached and cached['cached_until'] > time.time(): - pyfalog.debug('Cache hit for resource {0} (params={1})', resource, prms) - return cached - elif cached: - pyfalog.debug('Cache stale for resource {0} (params={1})', resource, prms) - self.cache.invalidate(key) - else: - pyfalog.debug('Cache miss for resource {0} (params={1})', resource, prms) - - pyfalog.debug('Getting resource {0} (params={1})', resource, prms) - res = self._session.get(resource, params=prms) - if res.status_code != 200: - raise APIException("Got unexpected status code from server: {0}" % res.status_code) - - ret = res.json() - - # cache result - expires = self._get_expires(res) - if expires > 0: - ret.update({'cached_until': time.time() + expires}) - self.cache.put(key, ret) - - return ret - - @staticmethod - def _get_expires(response): - if 'Cache-Control' not in response.headers: - return 0 - if any([s in response.headers['Cache-Control'] for s in ['no-cache', 'no-store']]): - return 0 - match = cache_re.search(response.headers['Cache-Control']) - if match: - return int(match.group(1)) - return 0 - - -class EVE(APIConnection): - def __init__(self, **kwargs): - self.api_key = kwargs.pop('api_key', None) - self.client_id = kwargs.pop('client_id', None) - self.redirect_uri = kwargs.pop('redirect_uri', None) - if kwargs.pop('testing', False): - self._public_endpoint = "http://public-crest-sisi.testeveonline.com/" - self._authed_endpoint = "https://api-sisi.testeveonline.com/" - self._image_server = "https://image.testeveonline.com/" - self._oauth_endpoint = "https://sisilogin.testeveonline.com/oauth" - else: - self._public_endpoint = "https://public-crest.eveonline.com/" - self._authed_endpoint = "https://crest-tq.eveonline.com/" - self._image_server = "https://image.eveonline.com/" - self._oauth_endpoint = "https://login.eveonline.com/oauth" - self._endpoint = self._public_endpoint - self._cache = {} - self._data = None - self.token = None - self.refresh_token = None - self.expires = None - APIConnection.__init__(self, **kwargs) - - def __call__(self): - if not self._data: - self._data = APIObject(self.get(self._endpoint), self) - return self._data - - def __getattr__(self, item): - return self._data.__getattr__(item) - - def auth_uri(self, scopes=None, state=None): - s = [] if not scopes else scopes - grant_type = "token" if self.api_key is None else "code" - - return "%s/authorize?response_type=%s&redirect_uri=%s&client_id=%s%s%s" % ( - self._oauth_endpoint, - grant_type, - self.redirect_uri, - self.client_id, - "&scope=%s" % '+'.join(s) if scopes else '', - "&state=%s" % state if state else '' - ) - - def _authorize(self, params): - auth = text_(base64.b64encode(bytes_("%s:%s" % (self.client_id, self.api_key)))) - headers = {"Authorization": "Basic %s" % auth} - res = self._session.post("%s/token" % self._oauth_endpoint, params=params, headers=headers) - if res.status_code != 200: - raise APIException("Got unexpected status code from API: %i" % res.status_code) - return res.json() - - def set_auth_values(self, res): - self.__class__ = AuthedConnection - self.token = res['access_token'] - self.refresh_token = res['refresh_token'] - self.expires = int(time.time()) + res['expires_in'] - self._endpoint = self._authed_endpoint - self._session.headers.update({"Authorization": "Bearer %s" % self.token}) - - def authorize(self, code): - res = self._authorize(params={"grant_type": "authorization_code", "code": code}) - self.set_auth_values(res) - - def refr_authorize(self, refresh_token): - res = self._authorize(params={"grant_type": "refresh_token", "refresh_token": refresh_token}) - self.set_auth_values(res) - - 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}) - - -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: - # print "Setting this whoami cache" - # self._cache['whoami'] = self.get("%s/verify" % self._oauth_endpoint) - return self.get("%s/verify" % self._oauth_endpoint) - - def get(self, resource, params=None): - if self.refresh_token and int(time.time()) >= self.expires: - self.refr_authorize(self.refresh_token) - return super(self.__class__, self).get(resource, params) - - def post(self, resource, data, params=None): - if self.refresh_token and int(time.time()) >= self.expires: - self.refr_authorize(self.refresh_token) - return self._session.post(resource, data=data, params=params) - - def delete(self, resource, params=None): - if self.refresh_token and int(time.time()) >= self.expires: - self.refr_authorize(self.refresh_token) - return self._session.delete(resource, params=params) - - -class APIObject(object): - def __init__(self, parent, connection): - self._dict = {} - self.connection = connection - for k, v in list(parent.items()): - if type(v) is dict: - self._dict[k] = APIObject(v, connection) - elif type(v) is list: - self._dict[k] = self._wrap_list(v) - else: - self._dict[k] = v - - def _wrap_list(self, list_): - new = [] - for item in list_: - if type(item) is dict: - new.append(APIObject(item, self.connection)) - elif type(item) is list: - new.append(self._wrap_list(item)) - else: - new.append(item) - return new - - def __getattr__(self, item): - if item in self._dict: - return self._dict[item] - raise AttributeError(item) - - def __call__(self, **kwargs): - # Caching is now handled by APIConnection - if 'href' in self._dict: - return APIObject(self.connection.get(self._dict['href'], params=kwargs), self.connection) - else: - return self - - def __str__(self): # pragma: no cover - return self._dict.__str__() - - def __repr__(self): # pragma: no cover - return self._dict.__repr__() diff --git a/service/pycrest/weak_ciphers.py b/service/pycrest/weak_ciphers.py deleted file mode 100644 index b18a1a552..000000000 --- a/service/pycrest/weak_ciphers.py +++ /dev/null @@ -1,132 +0,0 @@ -import datetime -import ssl -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 -except: - import urllib3 - from urllib3.util import ssl_ - from urllib3.exceptions import SystemTimeWarning, SecurityWarning - from urllib3.packages.ssl_match_hostname import match_hostname - - -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 - # TLS_RSA_WITH_RC4_128_MD5 (as of 5 Jan 2015). The cipher list below is - # nearly identical except for allowing that cipher as a last resort (and - # excluding export versions of ciphers). - DEFAULT_CIPHERS = ( - 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:' - 'ECDH+HIGH:DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:' - 'RSA+3DES:ECDH+RC4:DH+RC4:RSA+RC4:!aNULL:!eNULL:!EXP:-MD5:RSA+RC4+MD5' - ) - - def __init__(self, host, port, ciphers=None, **kwargs): - self.ciphers = ciphers if ciphers is not None else self.DEFAULT_CIPHERS - super(WeakCiphersHTTPSConnection, self).__init__(host, port, **kwargs) - - def connect(self): - # Yup, copied in VerifiedHTTPSConnection.connect just to change the - # default cipher list. - - # Add certificate verification - conn = self._new_conn() - - resolved_cert_reqs = ssl_.resolve_cert_reqs(self.cert_reqs) - resolved_ssl_version = ssl_.resolve_ssl_version(self.ssl_version) - - hostname = self.host - if getattr(self, '_tunnel_host', None): - # _tunnel_host was added in Python 2.6.3 - # (See: http://hg.python.org/cpython/rev/0f57b30a152f) - - self.sock = conn - # Calls self._set_hostport(), so self.host is - # self._tunnel_host below. - self._tunnel() - # Mark this connection as not reusable - self.auto_open = 0 - - # Override the host with the one we're requesting data from. - hostname = self._tunnel_host - - is_time_off = datetime.date.today() < urllib3.connection.RECENT_DATE - if is_time_off: - warnings.warn(( - 'System time is way off (before {0}). This will probably ' - 'lead to SSL verification errors').format( - urllib3.connection.RECENT_DATE), - SystemTimeWarning - ) - - # 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, - ) - - if self.assert_fingerprint: - ssl_.assert_fingerprint(self.sock.getpeercert(binary_form=True), - self.assert_fingerprint) - elif resolved_cert_reqs != ssl.CERT_NONE \ - and self.assert_hostname is not False: - cert = self.sock.getpeercert() - if not cert.get('subjectAltName', ()): - warnings.warn(( - 'Certificate has no `subjectAltName`, falling back to check for a `commonName` for now. ' - 'This feature is being removed by major browsers and deprecated by RFC 2818. ' - '(See https://github.com/shazow/urllib3/issues/497 for details.)'), - SecurityWarning - ) - match_hostname(cert, self.assert_hostname or hostname) - - self.is_verified = (resolved_cert_reqs == ssl.CERT_REQUIRED or self.assert_fingerprint is not None) - - -class WeakCiphersHTTPSConnectionPool(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) - 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): - # 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 - ) From 7b0f672f049fd3a64d856a31ea4d911323e2e36c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 9 Feb 2018 18:25:53 -0500 Subject: [PATCH 115/212] Get encrypted refresh tokens working --- config.py | 19 ++++++++++++++++++- eos/saveddata/ssocharacter.py | 13 ++----------- gui/crestFittings.py | 2 +- gui/mainFrame.py | 1 - service/esi.py | 25 +++++++++++++++++++++++-- 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/config.py b/config.py index bac448989..fb834f33e 100644 --- a/config.py +++ b/config.py @@ -5,6 +5,8 @@ from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, StreamHandler, TimedRotatingFileHandler, WARNING import hashlib +from cryptography.fernet import Fernet + pyfalog = Logger(__name__) # Load variable overrides specific to distribution type @@ -34,6 +36,8 @@ gameDB = None logPath = None loggingLevel = None logging_setup = None +cipher = None +clientHash = None ESI_AUTH_PROXY = "http://localhost:5015" # "https://blitzmann.pythonanywhere.com" // need to get this set up, and actually put on it's own domain ESI_CACHE = 'esi_cache' @@ -48,7 +52,7 @@ LOGLEVEL_MAP = { def getClientSecret(): - return hashlib.sha3_256("This is a secret, this will not remain in here for long".encode('utf-8')).hexdigest() + return clientHash def isFrozen(): @@ -90,6 +94,8 @@ def defPaths(customSavePath=None): global gameDB global saveInRoot global logPath + global cipher + global clientHash pyfalog.debug("Configuring Pyfa") @@ -114,6 +120,17 @@ def defPaths(customSavePath=None): __createDirs(savePath) + # get cipher object based on secret key of this client (stores encryption cipher for ESI refresh token) + secret_file = os.path.join(savePath, "{}.secret".format(hashlib.sha3_256(pyfaPath.encode('utf-8')).hexdigest())) + if not os.path.exists(secret_file): + with open(secret_file, "wb") as _file: + _file.write(Fernet.generate_key()) + + with open(secret_file, 'rb') as fp: + key = fp.read() + clientHash = hashlib.sha3_256(key).hexdigest() + cipher = Fernet(key) + # if isFrozen(): # os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem") # os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") diff --git a/eos/saveddata/ssocharacter.py b/eos/saveddata/ssocharacter.py index 95314f5e1..db15442d0 100644 --- a/eos/saveddata/ssocharacter.py +++ b/eos/saveddata/ssocharacter.py @@ -31,8 +31,10 @@ class SsoCharacter(object): self.client = client self.accessToken = accessToken self.refreshToken = refreshToken + self.accessTokenExpires = None self.esi_client = None + @reconstructor def init(self): self.esi_client = None @@ -47,14 +49,3 @@ class SsoCharacter(object): self.accessTokenExpires - datetime.datetime.utcnow() ).total_seconds() } - - def update_token(self, tokenResponse): - """ helper function to update token data from SSO response """ - self.accessToken = tokenResponse['access_token'] - self.accessTokenExpires = datetime.datetime.fromtimestamp( - time.time() + tokenResponse['expires_in'], - ) - if 'refresh_token' in tokenResponse: - self.refreshToken = tokenResponse['refresh_token'] - if self.esi_client is not None: - self.esi_client.security.update_token(tokenResponse) \ No newline at end of file diff --git a/gui/crestFittings.py b/gui/crestFittings.py index ed8750355..32c863812 100644 --- a/gui/crestFittings.py +++ b/gui/crestFittings.py @@ -16,7 +16,7 @@ import gui.globalEvents as GE from logbook import Logger import calendar -from service.esi import Esi, CrestModes +from service.esi import Esi pyfalog = Logger(__name__) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 7cef8ea3f..7e6eb9db2 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -83,7 +83,6 @@ import webbrowser import wx.adv from service.esi import Esi -from service.esi import CrestModes from gui.crestFittings import CrestFittings, ExportToEve, CrestMgmt disableOverrideEditor = False diff --git a/service/esi.py b/service/esi.py index 3e47af41a..5014c63c8 100644 --- a/service/esi.py +++ b/service/esi.py @@ -10,6 +10,7 @@ import json import os import eos.db +import datetime from eos.enum import Enum from eos.saveddata.ssocharacter import SsoCharacter import gui.globalEvents as GE @@ -104,7 +105,7 @@ class Esi(object): char = eos.db.getSsoCharacter(id, config.getClientSecret()) if char is not None and char.esi_client is None: char.esi_client = Esi.genEsiClient() - char.esi_client.security.update_token(char.get_sso_data()) + char.esi_client.security.update_token(Esi.get_sso_data(char)) return char def getSkills(self, id): @@ -144,6 +145,26 @@ class Esi(object): resp = char.esi_client.request(op) return resp.data + @staticmethod + def get_sso_data(char): + """ Little "helper" function to get formated data for esipy security + """ + return { + 'access_token': char.accessToken, + 'refresh_token': config.cipher.decrypt(char.refreshToken).decode(), + 'expires_in': (char.accessTokenExpires - datetime.datetime.utcnow()).total_seconds() + } + + @staticmethod + def update_token(char, tokenResponse): + """ helper function to update token data from SSO response """ + char.accessToken = tokenResponse['access_token'] + char.accessTokenExpires = datetime.datetime.fromtimestamp(time.time() + tokenResponse['expires_in']) + if 'refresh_token' in tokenResponse: + char.refreshToken = config.cipher.encrypt(tokenResponse['refresh_token'].encode()) + if char.esi_client is not None: + char.esi_client.security.update_token(tokenResponse) + def stopServer(self): pyfalog.debug("Stopping Server") self.httpd.stop() @@ -200,7 +221,7 @@ class Esi(object): if currentCharacter is None: currentCharacter = SsoCharacter(cdata['CharacterID'], cdata['CharacterName'], config.getClientSecret()) currentCharacter.esi_client = Esi.genEsiClient(esisecurity) - currentCharacter.update_token(auth_response) # this also sets the esi security token + Esi.update_token(currentCharacter, auth_response) # this also sets the esi security token eos.db.save(currentCharacter) From 9c355d8f96f0773b7bee0629c0ef1febef5322bc Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 9 Feb 2018 18:53:14 -0500 Subject: [PATCH 116/212] update requirements --- README.md | 9 ++------- requirements.txt | 5 ++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index fd87aed48..2e09ca7f0 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,8 @@ The following is a list of pyfa packages available for certain distributions. Pl ### Dependencies If you wish to help with development or simply need to run pyfa through a Python interpreter, the following software is required: -* Python 2.7 -* `wxPython` 2.8/3.0 -* `sqlalchemy` >= 1.0.5 -* `dateutil` -* `matplotlib` (for some Linux distributions you may need to install separate wxPython bindings such as `python-matplotlib-wx`) -* `requests` -* `logbook` >= 1.0.0 +* Python 3.6 +* Requirements as listed in `requirements.txt` ## Bug Reporting The preferred method of reporting bugs is through the project's [GitHub Issues interface](https://github.com/pyfa-org/Pyfa/issues). Alternatively, posting a report in the [pyfa thread](http://forums.eveonline.com/default.aspx?g=posts&t=247609) on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/DarkFenX/Pyfa/wiki/Bug-Reporting). diff --git a/requirements.txt b/requirements.txt index e57add8c9..f1a1bb6b9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ wxPython >= 4.0.0b2 logbook >= 1.0.0 matplotlib >= 2.0.0 python-dateutil -urllib3 -requests == 2.0.0 -sqlalchemy == 1.0.5 +requests >= 2.0.0 +sqlalchemy >= 1.0.5 markdown2 \ No newline at end of file From 50dd74dbe865c7f6ca153f858d606df4e57f44d5 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 10 Feb 2018 00:59:25 -0500 Subject: [PATCH 117/212] Add packaging requirement, and use it's PEP 440 version parsing for version comparisons. --- gui/mainFrame.py | 4 ++-- gui/updateDialog.py | 11 ++++++----- requirements.txt | 3 ++- service/update.py | 28 +++++++++------------------- 4 files changed, 19 insertions(+), 27 deletions(-) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 2aa6bb750..7b40c7d11 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -243,8 +243,8 @@ class MainFrame(wx.Frame): self.titleTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.updateTitle, self.titleTimer) - def ShowUpdateBox(self, release): - dlg = UpdateDialog(self, release) + def ShowUpdateBox(self, release, version): + dlg = UpdateDialog(self, release, version) dlg.ShowModal() def LoadPreviousOpenFits(self): diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 6713bec7d..a38de401f 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -33,11 +33,12 @@ html_tmpl = """

    pyfa {0}

    -

    {1}

    +
    {1}

    {2} {3} @@ -45,7 +46,7 @@ hr {{ margin-top: -10px; border: #000 1px solid; }} class UpdateDialog(wx.Dialog): - def __init__(self, parent, release): + def __init__(self, parent, release, version): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="pyfa Update Available", pos=wx.DefaultPosition, size=wx.Size(550, 450), style=wx.DEFAULT_DIALOG_STYLE) @@ -72,7 +73,7 @@ class UpdateDialog(wx.Dialog): self.browser.SetPage(html_tmpl.format( self.releaseInfo['tag_name'], releaseDate.strftime('%B %d, %Y'), - "

    This is a pre-release, be prepared for unstable features

    " if self.releaseInfo['prerelease'] else "", + "

    This is a pre-release, be prepared for unstable features

    " if version.is_prerelease else "", markdowner.convert(self.releaseInfo['body']) ),"") diff --git a/requirements.txt b/requirements.txt index f1a1bb6b9..72c4f7e00 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ matplotlib >= 2.0.0 python-dateutil requests >= 2.0.0 sqlalchemy >= 1.0.5 -markdown2 \ No newline at end of file +markdown2 +packaging \ No newline at end of file diff --git a/service/update.py b/service/update.py index 7196b2a59..e4cdbe03f 100644 --- a/service/update.py +++ b/service/update.py @@ -30,6 +30,8 @@ import config from service.network import Network from service.settings import UpdateSettings from logbook import Logger +from packaging.version import Version + pyfalog = Logger(__name__) @@ -46,7 +48,7 @@ class CheckUpdateThread(threading.Thread): network = Network.getInstance() try: - response = network.request('https://api.github.com/repos/pyfa-org/Pyfa/releases', network.UPDATE) + response = network.request('https://api.github.com/repos/blitzmann/Pyfa/releases', network.UPDATE) jsonResponse = json.loads(response.read()) jsonResponse.sort( key=lambda x: calendar.timegm(dateutil.parser.parse(x['published_at']).utctimetuple()), @@ -54,8 +56,11 @@ class CheckUpdateThread(threading.Thread): ) for release in jsonResponse: + rVersion = Version(release['tag_name']) + cVersion = Version(config.version) + # Suppress pre releases - if release['prerelease'] and self.settings.get('prerelease'): + if rVersion.is_prerelease and self.settings.get('prerelease'): continue # Handle use-case of updating to suppressed version @@ -66,24 +71,9 @@ class CheckUpdateThread(threading.Thread): if release['tag_name'] == self.settings.get('version'): break - # Set the release version that we will be comparing with. - if release['prerelease']: - rVersion = release['tag_name'].replace('singularity-', '', 1) - else: - rVersion = release['tag_name'].replace('v', '', 1) + if rVersion > cVersion: + wx.CallAfter(self.callback, release, rVersion) - 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']: - wx.CallAfter(self.callback, release) # Stable -> Singularity - elif self.versiontuple(rVersion) > self.versiontuple(config.version): - wx.CallAfter(self.callback, release) # Stable -> Stable - else: - if release['prerelease'] and rVersion > config.expansionVersion: - wx.CallAfter(self.callback, release) # Singularity -> Singularity break except Exception as e: pyfalog.error("Caught exception in run") From 320a0230f4426a483be1f4ac9952fbde19612737 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 11 Feb 2018 12:32:34 -0500 Subject: [PATCH 118/212] fix version data file in mac spec file --- .version | 2 +- dist_assets/mac/pyfa.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.version b/.version index 524d0fb50..59d5e729e 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -v1.33.2-104-g2365112 \ No newline at end of file +v1.2.3-221-g50dd74db \ No newline at end of file diff --git a/dist_assets/mac/pyfa.spec b/dist_assets/mac/pyfa.spec index ece015d1c..55471fb2a 100644 --- a/dist_assets/mac/pyfa.spec +++ b/dist_assets/mac/pyfa.spec @@ -23,7 +23,7 @@ added_files = [ ('../../eve.db', '.'), ('../../README.md', '.'), ('../../LICENSE', '.'), - ('../../gitversion', '.'), + ('../../.version', '.'), ] From c329f5eeb8002ed3a46901587fa4529710081e08 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 11 Feb 2018 21:22:35 -0500 Subject: [PATCH 119/212] Bump up the wxPython requirement to 4.0.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 72c4f7e00..5800936c0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -wxPython >= 4.0.0b2 +wxPython >= 4.0.1 logbook >= 1.0.0 matplotlib >= 2.0.0 python-dateutil From 33618f12f70ee0ef688bcce9f7847bddc6e7459c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 11 Feb 2018 23:22:37 -0500 Subject: [PATCH 120/212] Fix issue when error happens before wx is even initialized traceback is never set --- gui/errorDialog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/errorDialog.py b/gui/errorDialog.py index 27bf842fb..706e3c5a3 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -43,7 +43,8 @@ class ErrorHandler(object): if cls.__parent is None: app = wx.App(False) - ErrorFrame(None) + cls.__frame = ErrorFrame(None) + cls.__frame.addException("".join(t)) app.MainLoop() sys.exit() else: From fe1c4cc4d4db5b65a909347780168d91c32d8323 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 12 Feb 2018 21:43:23 -0500 Subject: [PATCH 121/212] Make a callback for token refresh, not sure how I'm gonna handle this one yet --- service/esi.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/service/esi.py b/service/esi.py index 5014c63c8..bf665470f 100644 --- a/service/esi.py +++ b/service/esi.py @@ -25,6 +25,8 @@ pyfalog = Logger(__name__) cache_path = os.path.join(config.savePath, config.ESI_CACHE) +from esipy.events import AFTER_TOKEN_REFRESH + if not os.path.exists(cache_path): os.mkdir(cache_path) @@ -69,6 +71,8 @@ class Esi(object): def __init__(self): Esi.initEsiApp() + AFTER_TOKEN_REFRESH.add_receiver(self.tokenUpdate) + self.settings = CRESTSettings.getInstance() # these will be set when needed @@ -86,6 +90,10 @@ class Esi(object): import gui.mainFrame # put this here to avoid loop self.mainFrame = gui.mainFrame.MainFrame.getInstance() + def tokenUpdate(self, **kwargs): + print(kwargs) + pass + @property def isTestServer(self): return self.settings.get('server') == Servers.SISI @@ -105,7 +113,9 @@ class Esi(object): char = eos.db.getSsoCharacter(id, config.getClientSecret()) if char is not None and char.esi_client is None: char.esi_client = Esi.genEsiClient() - char.esi_client.security.update_token(Esi.get_sso_data(char)) + Esi.update_token(char, Esi.get_sso_data(char)) # don't use update_token on security directly, se still need to apply the values here + print(repr(char)) + eos.db.commit() return char def getSkills(self, id): From 23f0f48c804514ed80a981eb4ed1cfb6d4d2fdb1 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 14 Feb 2018 01:16:49 -0500 Subject: [PATCH 122/212] Use a special dev database for py3 branch --- config.py | 2 +- eos/config.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py b/config.py index 339a19e0a..346e66818 100644 --- a/config.py +++ b/config.py @@ -111,7 +111,7 @@ def defPaths(customSavePath=None): # os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") # The database where we store all the fits etc - saveDB = os.path.join(savePath, "saveddata.db") + saveDB = os.path.join(savePath, "saveddata-py3-dev.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 diff --git a/eos/config.py b/eos/config.py index 2c1dd7e71..6bc31eef8 100644 --- a/eos/config.py +++ b/eos/config.py @@ -18,7 +18,7 @@ if istravis is True or hasattr(sys, '_called_from_test'): # Running in Travis. Run saveddata database in memory. saveddata_connectionstring = 'sqlite:///:memory:' else: - saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")) + saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata-py3-db.db")) pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring) From efc3f1ba7dad6fb79cb208b81f672d41866fff87 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 14 Feb 2018 22:23:42 -0500 Subject: [PATCH 123/212] Don't display console for windows application --- dist_assets/win/pyfa.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist_assets/win/pyfa.spec b/dist_assets/win/pyfa.spec index f3522bd81..598fe546b 100644 --- a/dist_assets/win/pyfa.spec +++ b/dist_assets/win/pyfa.spec @@ -62,7 +62,7 @@ exe = EXE(pyz, a.scripts, exclude_binaries=True, debug=False, - console=True, + console=False, strip=False, upx=True, name='pyfa', From 05f08970c95c9078fe2abc1ccbfb83394a703c43 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 16 Feb 2018 20:40:51 -0500 Subject: [PATCH 124/212] fix character editor not spawning on OSX --- gui/characterEditor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 080940c7d..b4f593a96 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -319,8 +319,8 @@ class SkillTreeView(wx.Panel): self.imageList = wx.ImageList(16, 16) tree.SetImageList(self.imageList) - self.skillBookImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) - self.skillBookDirtyImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small_red", "gui")) + self.skillBookImageId = self.imageList.Add(wx.Icon(BitmapLoader.getBitmap("skill_small", "gui"))) + self.skillBookDirtyImageId = self.imageList.Add(wx.Icon(BitmapLoader.getBitmap("skill_small_red", "gui"))) tree.AppendColumn("Skill") tree.AppendColumn("Level") From cafd92f169a3e325c9e218ffad9d98497ec6e867 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 16 Feb 2018 22:26:02 -0500 Subject: [PATCH 125/212] add a todo --- gui/builtinViews/fittingView.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index f1bb3f89d..8e2816320 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -286,6 +286,7 @@ class FittingView(d.Display): If fit is removed and active, the page is deleted. We also refresh the fit of the new current page in case delete fit caused change in stats (projected) + todo: move this to the notebook, not the page. We don't want the page being responsible for deleting itself """ print('_+_+_+_+_+_ Fit Removed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID)) pyfalog.debug("FittingView::fitRemoved") From e779bb84e2b90f7bd04be26d32b3f87f6eb2a320 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 17 Feb 2018 10:53:29 -0500 Subject: [PATCH 126/212] Fix for #1383. Due to the changed mechanics of None in py3, `getModifiedItemAttr` was defaulted to 0 instead of None. This adds an explicit default of None when trying to find the allowed drone group attribute --- eos/saveddata/drone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index cf9309de2..da4c512f2 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -264,7 +264,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def fits(self, fit): fitDroneGroupLimits = set() for i in range(1, 3): - groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i) + groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i, None) if groneGrp is not None: fitDroneGroupLimits.add(int(groneGrp)) if len(fitDroneGroupLimits) == 0: From 82e3db1ffbdef569c5e08a54a219780fa0779795 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 17 Feb 2018 11:10:42 -0500 Subject: [PATCH 127/212] Centralize version string getting. Still trying to work out how we should handle automatic version increments so we don't have to modify files all the time. Remove version file from repo (should only be a thing when building binaries) --- .gitignore | 1 + .version | 1 - config.py | 15 +++++++++------ gui/mainFrame.py | 2 +- pyfa.py | 8 ++++---- service/pycrest/eve.py | 2 +- 6 files changed, 16 insertions(+), 13 deletions(-) delete mode 100644 .version diff --git a/.gitignore b/.gitignore index 6cf50c952..4a31b497a 100644 --- a/.gitignore +++ b/.gitignore @@ -119,3 +119,4 @@ ENV/ eos.iml gitversion .version +/.version diff --git a/.version b/.version deleted file mode 100644 index 59d5e729e..000000000 --- a/.version +++ /dev/null @@ -1 +0,0 @@ -v1.2.3-221-g50dd74db \ No newline at end of file diff --git a/config.py b/config.py index 814f9ef0c..c951eb44f 100644 --- a/config.py +++ b/config.py @@ -20,8 +20,8 @@ debug = False saveInRoot = False # Version data -version = "1.35.0" -tag = "Stable" +version = "2.0.x" +tag = "git" expansionName = "YC120.2" expansionVersion = "1.1" evemonMinVersion = "4081" @@ -64,10 +64,13 @@ def getPyfaRoot(): return root -def getGitVersion(): - with open(os.path.join(pyfaPath, '.version')) as f: - version = f.readline() - return version +def getVersion(): + if os.path.isfile(os.path.join(pyfaPath, '.version')): + with open(os.path.join(pyfaPath, '.version')) as f: + gitVersion = f.readline() + return gitVersion + # if no version file exists, then user is running from source or not an official build + return version + " (git)" def getDefaultSave(): diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 7b40c7d11..8fbb2e78a 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -359,7 +359,7 @@ class MainFrame(wx.Frame): def ShowAboutBox(self, evt): info = wx.adv.AboutDialogInfo() info.Name = "pyfa" - info.Version = config.getGitVersion() # gui.aboutData.versionString + info.Version = config.getVersion() # gui.aboutData.versionString # # try: # import matplotlib diff --git a/pyfa.py b/pyfa.py index 889178b6e..bbd801e33 100755 --- a/pyfa.py +++ b/pyfa.py @@ -95,10 +95,6 @@ if __name__ == "__main__": if options.rootsavedata is True: config.saveInRoot = True - # set title if it wasn't supplied by argument - 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 config.loggingLevel = config.LOGLEVEL_MAP.get(options.logginglevel.lower(), config.LOGLEVEL_MAP['error']) config.defPaths(options.savepath) @@ -131,6 +127,10 @@ if __name__ == "__main__": from gui.mainFrame import MainFrame + # set title if it wasn't supplied by argument + if options.title is None: + options.title = "pyfa %s - Python Fitting Assistant" % (config.getVersion()) + pyfa = wx.App(False) mf = MainFrame(options.title) ErrorHandler.SetParent(mf) diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index 9ea88ce23..6a30e9162 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -99,7 +99,7 @@ class APIConnection(object): if additional_headers is None: additional_headers = {} if user_agent is None: - user_agent = "pyfa/{0} ({1})".format(config.version, config.tag) + user_agent = "pyfa/{0}".format(config.getVersion) session.headers.update({ "User-Agent": user_agent, "Accept": "application/json", From 5571bae8b2a6867c9cc3889e9ca66caf7de6560b Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 17 Feb 2018 12:47:07 -0500 Subject: [PATCH 128/212] Fix situation in which event propagation was not working properly for structures --- gui/builtinAdditionPanes/boosterView.py | 1 + gui/builtinAdditionPanes/droneView.py | 1 + gui/builtinAdditionPanes/implantView.py | 1 + 3 files changed, 3 insertions(+) diff --git a/gui/builtinAdditionPanes/boosterView.py b/gui/builtinAdditionPanes/boosterView.py index 7802b44bd..8923f83de 100644 --- a/gui/builtinAdditionPanes/boosterView.py +++ b/gui/builtinAdditionPanes/boosterView.py @@ -131,6 +131,7 @@ class BoosterView(d.Display): fit = sFit.getFit(fitID) if not fit or fit.isStructure: + event.Skip() return trigger = sFit.addBooster(fitID, event.itemID) diff --git a/gui/builtinAdditionPanes/droneView.py b/gui/builtinAdditionPanes/droneView.py index 41327618b..e9b893047 100644 --- a/gui/builtinAdditionPanes/droneView.py +++ b/gui/builtinAdditionPanes/droneView.py @@ -210,6 +210,7 @@ class DroneView(Display): fit = sFit.getFit(fitID) if not fit or fit.isStructure: + event.Skip() return trigger = sFit.addDrone(fitID, event.itemID) diff --git a/gui/builtinAdditionPanes/implantView.py b/gui/builtinAdditionPanes/implantView.py index 8e043702f..b1d7bbe66 100644 --- a/gui/builtinAdditionPanes/implantView.py +++ b/gui/builtinAdditionPanes/implantView.py @@ -152,6 +152,7 @@ class ImplantDisplay(d.Display): fit = sFit.getFit(fitID) if not fit or fit.isStructure: + event.Skip() return trigger = sFit.addImplant(fitID, event.itemID) From 72efef818f17c7be93d52ce6e7c1880efaef9d76 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 17 Feb 2018 13:59:19 -0500 Subject: [PATCH 129/212] Fix version stuff --- config.py | 2 +- service/update.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py b/config.py index c42e6b658..0bac0fc2c 100644 --- a/config.py +++ b/config.py @@ -20,7 +20,7 @@ debug = False saveInRoot = False # Version data -version = "2.0.x" +version = "2.0.0b3" tag = "git" expansionName = "YC120.2" expansionVersion = "1.2" diff --git a/service/update.py b/service/update.py index e4cdbe03f..a1f6989c4 100644 --- a/service/update.py +++ b/service/update.py @@ -48,7 +48,7 @@ class CheckUpdateThread(threading.Thread): network = Network.getInstance() try: - response = network.request('https://api.github.com/repos/blitzmann/Pyfa/releases', network.UPDATE) + 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()), From 35ad21da3811430cfb2ac7a251e9d0b7dd373b8c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 17 Feb 2018 16:27:12 -0500 Subject: [PATCH 130/212] Add pattern for commit hashes to update dialog --- gui/updateDialog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gui/updateDialog.py b/gui/updateDialog.py index a38de401f..c575eeaf3 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -62,6 +62,7 @@ class UpdateDialog(wx.Dialog): self.browser.Bind(wx.html2.EVT_WEBVIEW_NEWWINDOW, self.OnNewWindow) link_patterns = [ + (re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"), (re.compile("#(\d+)", re.I), r"https://github.com/pyfa-org/Pyfa/issues/\1"), (re.compile("@(\w+)", re.I), r"https://github.com/\1") ] From 42ccc531662d90a1eeecef8366c62c5b9e7fccf1 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 18 Feb 2018 20:35:46 -0500 Subject: [PATCH 131/212] bit of clean up for reload setting --- gui/builtinContextMenus/factorReload.py | 4 +--- service/fit.py | 6 ++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gui/builtinContextMenus/factorReload.py b/gui/builtinContextMenus/factorReload.py index 64e246413..5600c4c5c 100644 --- a/gui/builtinContextMenus/factorReload.py +++ b/gui/builtinContextMenus/factorReload.py @@ -31,9 +31,7 @@ class FactorReload(ContextMenu): def getBitmap(self, context, selection): sFit = Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(fitID) - if fit.factorReload: + if sFit.serviceFittingOptions["useGlobalForceReload"]: return BitmapLoader.getBitmap("state_active_small", "gui") else: return None diff --git a/service/fit.py b/service/fit.py index 3020183d9..0ca3bb6e2 100644 --- a/service/fit.py +++ b/service/fit.py @@ -1205,10 +1205,12 @@ class Fit(object): def recalc(self, fit): start_time = time() pyfalog.info("=" * 10 + "recalc: {0}" + "=" * 10, fit.name) - if fit.factorReload is not self.serviceFittingOptions["useGlobalForceReload"]: - fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"] + + + fit.factorReload = self.serviceFittingOptions["useGlobalForceReload"] fit.clear() fit.calculateModifiedAttributes() pyfalog.info("=" * 10 + "recalc time: " + str(time() - start_time) + "=" * 10) + From 4bbbd33917ec7a8eb594e41b64ca8bd1a50accd3 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 18 Feb 2018 22:55:31 -0500 Subject: [PATCH 132/212] Return 0 instead of -1 when there is no charge on a weapon. It was changed from None to -1 during the py3 conversion (as None can no longer be compared against non-None types). -1 was throwing the capSim off by quite a bit. See #1405 --- 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 13a593fb3..e57ad9110 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -182,7 +182,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def numShots(self): if self.charge is None: - return -1 + return 0 if self.__chargeCycles is None and self.charge: numCharges = self.numCharges # Usual ammo like projectiles and missiles From d59c897921c62eabeb38881241d93e33eb365baf Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 19 Feb 2018 01:20:57 -0500 Subject: [PATCH 133/212] Fix image list in preferences dialog --- gui/preferenceDialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index 05701987d..916c4497a 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -38,7 +38,7 @@ class PreferenceDialog(wx.Dialog): # self.listview.SetSize((500, -1)) self.imageList = wx.ImageList(32, 32) - self.listbook.SetImageList(self.imageList) + self.listbook.AssignImageList(self.imageList) mainSizer.Add(self.listbook, 1, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.LEFT, 5) From 8abd25fe40356c2d908c3c501b5d53a000e53e0d Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 19 Feb 2018 23:32:44 -0500 Subject: [PATCH 134/212] Use a scolled window in preference pages --- gui/preferenceDialog.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index 916c4497a..37ca5ddaf 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -55,17 +55,17 @@ class PreferenceDialog(wx.Dialog): self.Centre(wx.BOTH) for prefView in PreferenceView.views: - page = wx.Panel(self.listbook) + page = wx.ScrolledWindow(self.listbook) + page.SetScrollbars(1, 1, 20, 20) bmp = prefView.getImage() if bmp: imgID = self.imageList.Add(bmp) else: imgID = -1 prefView.populatePanel(page) + 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 = 550 bestFit = self.GetBestVirtualSize() if minHeight > bestFit[1]: From 201fb4e2412ffceb6cd56ae30b0e943fa3609ae9 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 23 Feb 2018 00:00:53 -0500 Subject: [PATCH 135/212] Remove all encoding bits from fit importing. Might not work 100% of the time, but as 2.0 gets introduced to the community, we'll get reports of possible problems. #1410 --- service/port.py | 65 ++++++------------------------------------------- 1 file changed, 7 insertions(+), 58 deletions(-) diff --git a/service/port.py b/service/port.py index db7dc0964..b0db7018d 100644 --- a/service/port.py +++ b/service/port.py @@ -264,7 +264,7 @@ class Port(object): fits are processed as well as when fits are being saved. returns """ - defcodepage = locale.getpreferredencoding() + sFit = svcFit.getInstance() fit_list = [] @@ -276,63 +276,15 @@ class Port(object): PortProcessing.notify(iportuser, IPortUser.PROCESS_IMPORT | IPortUser.ID_UPDATE, msg) # wx.CallAfter(callback, 1, msg) - with open(path, "r") as file_: + with open(path, "r", encoding='utf-8') as file_: srcString = file_.read() if len(srcString) == 0: # ignore blank files pyfalog.debug("File is blank.") 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): - savebom = None - - 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: - pyfalog.info("Unicode BOM not found in file {0}.", path) - attempt_codecs = (defcodepage, "utf-8", "utf-16", "cp1252") - - for page in attempt_codecs: - try: - pyfalog.info("Attempting to decode file {0} using {1} page.", path, page) - srcString = str(srcString, page) - codec_found = page - pyfalog.info("File {0} decoded using {1} page.", path, page) - except UnicodeDecodeError: - pyfalog.info("Error unicode decoding {0} from page {1}, trying next codec", path, page) - else: - break - else: - pyfalog.info("Unicode BOM detected in {0}, using {1} page.", path, codec_found) - srcString = str(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, iportuser=iportuser, encoding=codec_found) + _, fitsImport = Port.importAuto(srcString, path, iportuser=iportuser) fit_list += fitsImport except xml.parsers.expat.ExpatError: pyfalog.warning("Malformed XML in:\n{0}", path) @@ -477,7 +429,7 @@ class Port(object): return json.dumps(fit) @classmethod - def importAuto(cls, string, path=None, activeFit=None, iportuser=None, encoding=None): + def importAuto(cls, string, path=None, activeFit=None, iportuser=None): # type: (basestring, basestring, object, IPortUser, basestring) -> object # Get first line and strip space symbols of it to avoid possible detection errors firstLine = re.split("[\n\r]+", string.strip(), maxsplit=1)[0] @@ -485,10 +437,7 @@ class Port(object): # If XML-style start of tag encountered, detect as XML if re.search(RE_XML_START, firstLine): - if encoding: - return "XML", cls.importXml(string, iportuser, encoding) - else: - return "XML", cls.importXml(string, iportuser) + return "XML", cls.importXml(string, iportuser) # If JSON-style start, parse as CREST/JSON if firstLine[0] == '{': @@ -1013,10 +962,10 @@ class Port(object): return fits @staticmethod - def importXml(text, iportuser=None, encoding="utf-8"): + def importXml(text, iportuser=None): # type: (basestring, IPortUser, basestring) -> list[eos.saveddata.fit.Fit] sMkt = Market.getInstance() - doc = xml.dom.minidom.parseString(text.encode(encoding)) + doc = xml.dom.minidom.parseString(text) # NOTE: # When L_MARK is included at this point, # Decided to be localized data From dd78a41171a83ab1d05dec93d98198162b223ae2 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 23 Feb 2018 00:13:14 -0500 Subject: [PATCH 136/212] Fix HTML export (more encoding stuff). Also fix old bug in which unicode fit names don't display correctly in the webbrowser. #1411 --- gui/utils/exportHtml.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index 984505021..612df367e 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -63,12 +63,14 @@ class exportHtmlThread(threading.Thread): HTML = self.generateFullHTML(sMkt, sFit, dnaUrl) try: - FILE = open(settings.getPath(), "w") - FILE.write(HTML.encode('utf-8')) + FILE = open(settings.getPath(), "w", encoding='utf-8') + FILE.write(HTML) FILE.close() - except IOError: + except IOError as ex: print(("Failed to write to " + settings.getPath())) pass + except Exception as ex: + pass if self.callback: wx.CallAfter(self.callback, -1) @@ -84,6 +86,7 @@ class exportHtmlThread(threading.Thread): Pyfa Fittings +