Merge branch 'development'

This commit is contained in:
blitzman
2017-03-15 20:01:01 -04:00
117 changed files with 8294 additions and 549 deletions

1
.gitignore vendored
View File

@@ -49,7 +49,6 @@ Pyfa.egg-info/
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt

View File

@@ -1,17 +1,18 @@
import os
import sys
# TODO: move all logging back to pyfa.py main loop
# We moved it here just to avoid rebuilding windows skeleton for now (any change to pyfa.py needs it)
import logging
import logging.handlers
from logbook import Logger
pyfalog = Logger(__name__)
# Load variable overrides specific to distribution type
try:
import configforced
except ImportError:
pyfalog.warning("Failed to import: configforced")
configforced = None
# Turns on debug mode
debug = False
# Defines if our saveddata will be in pyfa root or not
@@ -30,22 +31,6 @@ saveDB = None
gameDB = None
class StreamToLogger(object):
"""
Fake file-like stream object that redirects writes to a logger instance.
From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/
"""
def __init__(self, logger, log_level=logging.INFO):
self.logger = logger
self.log_level = log_level
self.linebuf = ''
def write(self, buf):
for line in buf.rstrip().splitlines():
self.logger.log(self.log_level, line.rstrip())
def isFrozen():
if hasattr(sys, 'frozen'):
return True
@@ -77,10 +62,7 @@ def defPaths(customSavePath):
global gameDB
global saveInRoot
if debug:
logLevel = logging.DEBUG
else:
logLevel = logging.WARN
pyfalog.debug("Configuring Pyfa")
# The main pyfa directory which contains run.py
# Python 2.X uses ANSI by default, so we need to convert the character encoding
@@ -107,26 +89,6 @@ def defPaths(customSavePath):
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')
_format = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s'
logging.basicConfig(format=_format, level=logLevel)
handler = logging.handlers.RotatingFileHandler(os.path.join(savePath, "log.txt"), maxBytes=1000000,
backupCount=3)
formatter = logging.Formatter(_format)
handler.setFormatter(formatter)
logging.getLogger('').addHandler(handler)
logging.info("Starting pyfa")
if hasattr(sys, 'frozen'):
stdout_logger = logging.getLogger('STDOUT')
sl = StreamToLogger(stdout_logger, logging.INFO)
sys.stdout = sl
# This interferes with cx_Freeze's own handling of exceptions. Find a way to fix this.
# stderr_logger = logging.getLogger('STDERR')
# sl = StreamToLogger(stderr_logger, logging.ERROR)
# sys.stderr = sl
# The database where we store all the fits etc
saveDB = os.path.join(savePath, "saveddata.db")
@@ -144,43 +106,6 @@ def defPaths(customSavePath):
eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False"
eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False"
# Keeping disabled code here for now until we can determine with decent certainty that this isn't needed
'''
def getPyfaPath(Append=None):
base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0]
root = os.path.dirname(os.path.realpath(os.path.abspath(base)))
if Append:
path = parsePath(root, Append)
else:
path = parsePath(root)
return path
def getSavePath(Append=None):
root = savePath
if Append:
path = parsePath(root, Append)
else:
path = parsePath(root)
return path
def parsePath(root, Append=None):
if Append:
path = os.path.join(root, Append)
else:
path = root
if type(path) == str: # leave unicode ones alone
try:
path = path.decode('utf8')
except UnicodeDecodeError:
path = path.decode('windows-1252')
return path
'''
# initialize the settings
from service.settings import EOSSettings
eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever

5666
dist_assets/cacert.pem Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,9 @@ gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath
saveddata_connectionstring = 'sqlite:///' + unicode(
realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding())
settings = {
"setting1": True
}
# Autodetect path, only change if the autodetection bugs out.
path = dirname(unicode(__file__, sys.getfilesystemencoding()))

View File

@@ -24,6 +24,9 @@ from sqlalchemy.orm import sessionmaker
import migration
from eos import config
from logbook import Logger
pyfalog = Logger(__name__)
class ReadOnlyException(Exception):
@@ -46,7 +49,9 @@ try:
config.gamedata_version = gamedata_session.execute(
"SELECT `field_value` FROM `metadata` WHERE `field_name` LIKE 'client_build'"
).fetchone()[0]
except:
except Exception as e:
pyfalog.warning("Missing gamedata version.")
pyfalog.critical(e)
config.gamedata_version = None
saveddata_connectionstring = config.saveddata_connectionstring

View File

@@ -23,6 +23,7 @@ from sqlalchemy.sql import and_, or_, select
import eos.config
from eos.db import gamedata_session
from eos.db.gamedata.metaGroup import metatypes_table, items_table
from eos.db.gamedata.group import groups_table
from eos.db.util import processEager, processWhere
from eos.gamedata import AlphaClone, Attribute, Category, Group, Item, MarketGroup, MetaGroup, AttributeInfo, MetaData
@@ -249,7 +250,7 @@ def searchItems(nameLike, where=None, join=None, eager=None):
@cachedQuery(2, "where", "itemids")
def getVariations(itemids, where=None, eager=None):
def getVariations(itemids, groupIDs=None, where=None, eager=None):
for itemid in itemids:
if not isinstance(itemid, int):
raise TypeError("All passed item IDs must be integers")
@@ -262,7 +263,17 @@ def getVariations(itemids, where=None, eager=None):
joinon = items_table.c.typeID == metatypes_table.c.typeID
vars = gamedata_session.query(Item).options(*processEager(eager)).join((metatypes_table, joinon)).filter(
filter).all()
return vars
if vars:
return vars
elif groupIDs:
itemfilter = or_(*(groups_table.c.groupID == groupID for groupID in groupIDs))
filter = processWhere(itemfilter, where)
joinon = items_table.c.groupID == groups_table.c.groupID
vars = gamedata_session.query(Item).options(*processEager(eager)).join((groups_table, joinon)).filter(
filter).all()
return vars
@cachedQuery(1, "attr")

View File

@@ -1,11 +1,11 @@
import logging
from logbook import Logger
import shutil
import time
import config
import migrations
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
def getVersion(db):
@@ -37,7 +37,7 @@ def update(saveddata_engine):
for version in xrange(dbVersion, appVersion):
func = migrations.updates[version + 1]
if func:
logger.info("Applying database update: %d", version + 1)
pyfalog.info("Applying database update: {0}", version + 1)
func(saveddata_engine)
# when all is said and done, set version to current

View File

@@ -18,9 +18,9 @@
# ===============================================================================
from sqlalchemy.exc import DatabaseError
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class DatabaseCleanup(object):
@@ -33,7 +33,7 @@ class DatabaseCleanup(object):
results = saveddata_engine.execute(query)
return results
except DatabaseError:
logger.error("Failed to connect to database or error executing query:\n%s", query)
pyfalog.error("Failed to connect to database or error executing query:\n{0}", query)
return None
@staticmethod
@@ -41,7 +41,7 @@ class DatabaseCleanup(object):
# Find orphaned character skills.
# This solves an issue where the character doesn't exist, but skills for that character do.
# See issue #917
logger.debug("Running database cleanup for character skills.")
pyfalog.debug("Running database cleanup for character skills.")
query = "SELECT COUNT(*) AS num FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -53,14 +53,14 @@ class DatabaseCleanup(object):
if row and row['num']:
query = "DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def OrphanedFitDamagePatterns(saveddata_engine):
# Find orphaned damage patterns.
# This solves an issue where the damage pattern doesn't exist, but fits reference the pattern.
# See issue #777
logger.debug("Running database cleanup for orphaned damage patterns attached to fits.")
pyfalog.debug("Running database cleanup for orphaned damage patterns attached to fits.")
query = "SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -80,20 +80,20 @@ class DatabaseCleanup(object):
rows = uniform_results.fetchall()
if len(rows) == 0:
logger.error("Missing uniform damage pattern.")
pyfalog.error("Missing uniform damage pattern.")
elif len(rows) > 1:
logger.error("More than one uniform damage pattern found.")
pyfalog.error("More than one uniform damage pattern found.")
else:
uniform_damage_pattern_id = rows[0]['ID']
update_query = "UPDATE 'fits' SET 'damagePatternID' = {} " \
"WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL".format(uniform_damage_pattern_id)
update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query)
logger.error("Database corruption found. Cleaning up %d records.", update_results.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount)
@staticmethod
def OrphanedFitCharacterIDs(saveddata_engine):
# Find orphaned character IDs. This solves an issue where the character doesn't exist, but fits reference the pattern.
logger.debug("Running database cleanup for orphaned characters attached to fits.")
pyfalog.debug("Running database cleanup for orphaned characters attached to fits.")
query = "SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -113,22 +113,22 @@ class DatabaseCleanup(object):
rows = all5_results.fetchall()
if len(rows) == 0:
logger.error("Missing 'All 5' character.")
pyfalog.error("Missing 'All 5' character.")
elif len(rows) > 1:
logger.error("More than one 'All 5' character found.")
pyfalog.error("More than one 'All 5' character found.")
else:
all5_id = rows[0]['ID']
update_query = "UPDATE 'fits' SET 'characterID' = " + str(all5_id) + \
" WHERE characterID not in (select ID from characters) OR characterID IS NULL"
update_results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, update_query)
logger.error("Database corruption found. Cleaning up %d records.", update_results.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", update_results.rowcount)
@staticmethod
def NullDamagePatternNames(saveddata_engine):
# Find damage patterns that are missing the name.
# This solves an issue where the damage pattern ends up with a name that is null.
# See issue #949
logger.debug("Running database cleanup for missing damage pattern names.")
pyfalog.debug("Running database cleanup for missing damage pattern names.")
query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name IS NULL OR name = ''"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -140,14 +140,14 @@ class DatabaseCleanup(object):
if row and row['num']:
query = "DELETE FROM damagePatterns WHERE name IS NULL OR name = ''"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def NullTargetResistNames(saveddata_engine):
# Find target resists that are missing the name.
# This solves an issue where the target resist ends up with a name that is null.
# See issue #949
logger.debug("Running database cleanup for missing target resist names.")
pyfalog.debug("Running database cleanup for missing target resist names.")
query = "SELECT COUNT(*) AS num FROM targetResists WHERE name IS NULL OR name = ''"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -159,14 +159,14 @@ class DatabaseCleanup(object):
if row and row['num']:
query = "DELETE FROM targetResists WHERE name IS NULL OR name = ''"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def OrphanedFitIDItemID(saveddata_engine):
# Orphaned items that are missing the fit ID or item ID.
# See issue #954
for table in ['drones', 'cargo', 'fighters']:
logger.debug("Running database cleanup for orphaned %s items.", table)
pyfalog.debug("Running database cleanup for orphaned {0} items.", table)
query = "SELECT COUNT(*) AS num FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(
table)
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -180,10 +180,10 @@ class DatabaseCleanup(object):
query = "DELETE FROM {} WHERE itemID IS NULL OR itemID = '' or itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(
table)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
for table in ['modules']:
logger.debug("Running database cleanup for orphaned %s items.", table)
pyfalog.debug("Running database cleanup for orphaned {0} items.", table)
query = "SELECT COUNT(*) AS num FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(
table)
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -196,7 +196,7 @@ class DatabaseCleanup(object):
if row and row['num']:
query = "DELETE FROM {} WHERE itemID = '0' or fitID IS NULL OR fitID = '' or fitID = '0'".format(table)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def NullDamageTargetPatternValues(saveddata_engine):
@@ -204,7 +204,7 @@ class DatabaseCleanup(object):
# See issue #954
for profileType in ['damagePatterns', 'targetResists']:
for damageType in ['em', 'thermal', 'kinetic', 'explosive']:
logger.debug("Running database cleanup for null %s values. (%s)", profileType, damageType)
pyfalog.debug("Running database cleanup for null {0} values. ({1})", profileType, damageType)
query = "SELECT COUNT(*) AS num FROM {0} WHERE {1}Amount IS NULL OR {1}Amount = ''".format(profileType,
damageType)
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -218,13 +218,13 @@ class DatabaseCleanup(object):
query = "UPDATE '{0}' SET '{1}Amount' = '0' WHERE {1}Amount IS NULL OR Amount = ''".format(profileType,
damageType)
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)
@staticmethod
def DuplicateSelectedAmmoName(saveddata_engine):
# Orphaned items that are missing the fit ID or item ID.
# See issue #954
logger.debug("Running database cleanup for duplicated selected ammo profiles.")
pyfalog.debug("Running database cleanup for duplicated selected ammo profiles.")
query = "SELECT COUNT(*) AS num FROM damagePatterns WHERE name = 'Selected Ammo'"
results = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
@@ -236,4 +236,4 @@ class DatabaseCleanup(object):
if row and row['num'] > 1:
query = "DELETE FROM damagePatterns WHERE name = 'Selected Ammo'"
delete = DatabaseCleanup.ExecuteSQLQuery(saveddata_engine, query)
logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount)
pyfalog.error("Database corruption found. Cleaning up {0} records.", delete.rowcount)

View File

@@ -17,9 +17,9 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class HandledList(list):
@@ -198,7 +198,7 @@ class HandledImplantBoosterList(HandledList):
# if needed, remove booster that was occupying slot
oldObj = next((m for m in self if m.slot == thing.slot), None)
if oldObj:
logging.info("Slot %d occupied with %s, replacing with %s", thing.slot, oldObj.item.name, thing.item.name)
pyfalog.info("Slot {0} occupied with {1}, replacing with {2}", thing.slot, oldObj.item.name, thing.item.name)
oldObj.itemID = 0 # hack to remove from DB. See GH issue #324
self.remove(oldObj)
@@ -222,7 +222,7 @@ class HandledProjectedModList(HandledList):
oldEffect = next((m for m in self if m.item.group.name == "Effect Beacon"), None)
if oldEffect:
logging.info("System effect occupied with %s, replacing with %s", oldEffect.item.name, proj.item.name)
pyfalog.info("System effect occupied with {0}, replacing with {1}", oldEffect.item.name, proj.item.name)
self.remove(oldEffect)
HandledList.append(self, proj)

View File

@@ -2,9 +2,9 @@
#
# Used by:
# Module: Reactive Armor Hardener
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
runTime = "late"
type = "active"
@@ -23,7 +23,7 @@ def handler(fit, module, context):
damagePattern.kineticAmount * fit.ship.getModifiedItemAttr('armorKineticDamageResonance'),
damagePattern.explosiveAmount * fit.ship.getModifiedItemAttr('armorExplosiveDamageResonance'),
)
# logger.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])
# pyfalog.debug("Damage Adjusted for Armor Resists: %f/%f/%f/%f", baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])
resistanceShiftAmount = module.getModifiedItemAttr(
'resistanceShiftAmount') / 100 # The attribute is in percent and we want a fraction
@@ -39,7 +39,7 @@ def handler(fit, module, context):
cycleList = []
loopStart = -20
for num in range(50):
# logger.debug("Starting cycle %d.", num)
# pyfalog.debug("Starting cycle %d.", num)
# The strange order is to emulate the ingame sorting when different types have taken the same amount of damage.
# This doesn't take into account stacking penalties. In a few cases fitting a Damage Control causes an inaccurate result.
damagePattern_tuples = [
@@ -77,7 +77,7 @@ def handler(fit, module, context):
RAHResistance[sortedDamagePattern_tuples[1][0]] = sortedDamagePattern_tuples[1][2] + change1
RAHResistance[sortedDamagePattern_tuples[2][0]] = sortedDamagePattern_tuples[2][2] + change2
RAHResistance[sortedDamagePattern_tuples[3][0]] = sortedDamagePattern_tuples[3][2] + change3
# logger.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3])
# pyfalog.debug("Resistances shifted to %f/%f/%f/%f", RAHResistance[0], RAHResistance[1], RAHResistance[2], RAHResistance[3])
# See if the current RAH profile has been encountered before, indicating a loop.
for i, val in enumerate(cycleList):
@@ -87,7 +87,7 @@ def handler(fit, module, context):
abs(RAHResistance[2] - val[2]) <= tolerance and \
abs(RAHResistance[3] - val[3]) <= tolerance:
loopStart = i
# logger.debug("Loop found: %d-%d", loopStart, num)
# pyfalog.debug("Loop found: %d-%d", loopStart, num)
break
if loopStart >= 0:
break
@@ -95,7 +95,7 @@ def handler(fit, module, context):
cycleList.append(list(RAHResistance))
if loopStart < 0:
logger.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: %f/%f/%f/%f",
pyfalog.error("Reactive Armor Hardener failed to find equilibrium. Damage profile after armor: {0}/{1}/{2}/{3}",
baseDamageTaken[0], baseDamageTaken[1], baseDamageTaken[2], baseDamageTaken[3])
# Average the profiles in the RAH loop, or the last 20 if it didn't find a loop.
@@ -110,7 +110,7 @@ def handler(fit, module, context):
average[i] = round(average[i] / numCycles, 3)
# Set the new resistances
# logger.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3])
# pyfalog.debug("Setting new resist profile: %f/%f/%f/%f", average[0], average[1], average[2],average[3])
for i, attr in enumerate((
'armorEmDamageResonance', 'armorThermalDamageResonance', 'armorKineticDamageResonance',
'armorExplosiveDamageResonance')):

View File

@@ -2,8 +2,10 @@
#
# Used by:
# Modules from group: Tractor Beam (4 of 4)
from eos.config import settings
type = "active"
def handler(fit, module, context):
print settings['setting1']
pass

View File

@@ -18,7 +18,6 @@
# ===============================================================================
import re
import traceback
from sqlalchemy.orm import reconstructor
@@ -30,6 +29,10 @@ try:
except ImportError:
from utils.compat import OrderedDict
from logbook import Logger
pyfalog = Logger(__name__)
class Effect(EqBase):
"""
@@ -64,6 +67,8 @@ class Effect(EqBase):
if not self.__generated:
self.__generateHandler()
pyfalog.debug("Generating effect: {0} ({1}) [runTime: {2}]", self.name, self.effectID, self.runTime)
return self.__handler
@property
@@ -138,7 +143,7 @@ class Effect(EqBase):
@property
def isImplemented(self):
"""
Wether this effect is implemented in code or not,
Whether this effect is implemented in code or not,
unimplemented effects simply do nothing at all when run
"""
return self.handler != effectDummy
@@ -156,36 +161,30 @@ class Effect(EqBase):
"""
try:
self.__effectModule = effectModule = __import__('eos.effects.' + self.handlerName, fromlist=True)
try:
self.__handler = getattr(effectModule, "handler")
except AttributeError:
print "effect {} exists, but no handler".format(self.handlerName)
raise
try:
self.__runTime = getattr(effectModule, "runTime") or "normal"
except AttributeError:
self.__runTime = "normal"
try:
self.__activeByDefault = getattr(effectModule, "activeByDefault")
except AttributeError:
self.__activeByDefault = True
try:
t = getattr(effectModule, "type")
except AttributeError:
t = None
self.__handler = getattr(effectModule, "handler", effectDummy)
self.__runTime = getattr(effectModule, "runTime", "normal")
self.__activeByDefault = getattr(effectModule, "activeByDefault", True)
t = getattr(effectModule, "type", None)
t = t if isinstance(t, tuple) or t is None else (t,)
self.__type = t
except (ImportError, AttributeError):
except (ImportError) as e:
# Effect probably doesn't exist, so create a dummy effect and flag it with a warning.
self.__handler = effectDummy
self.__runTime = "normal"
self.__activeByDefault = True
self.__type = None
pyfalog.warning("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)
except Exception as e:
traceback.print_exc(e)
pyfalog.critical("Exception generating handler:")
pyfalog.critical(e)
self.__generated = True
@@ -285,8 +284,6 @@ class Item(EqBase):
@property
def requiredSkills(self):
if self.__requiredSkills is None:
# This import should be here to make sure it's fully initialized
from eos import db
requiredSkills = OrderedDict()
self.__requiredSkills = requiredSkills
# Map containing attribute IDs we may need for required skills
@@ -297,7 +294,7 @@ class Item(EqBase):
# { attributeID : attributeValue }
skillAttrs = {}
# Get relevant attribute values from db (required skill IDs and levels) for our item
for attrInfo in db.directAttributeRequest((self.ID,), tuple(combinedAttrIDs)):
for attrInfo in eos.db.directAttributeRequest((self.ID,), tuple(combinedAttrIDs)):
attrID = attrInfo[1]
attrVal = attrInfo[2]
skillAttrs[attrID] = attrVal
@@ -308,7 +305,7 @@ class Item(EqBase):
skillID = int(skillAttrs[srqIDAtrr])
skillLvl = skillAttrs[srqLvlAttr]
# Fetch item from database and fill map
item = db.getItem(skillID)
item = eos.db.getItem(skillID)
requiredSkills[item] = skillLvl
return self.__requiredSkills
@@ -341,18 +338,20 @@ class Item(EqBase):
# thus keep old mechanism for now
except KeyError:
# Define race map
map = {1: "caldari",
2: "minmatar",
4: "amarr",
5: "sansha", # Caldari + Amarr
6: "blood", # Minmatar + Amarr
8: "gallente",
9: "guristas", # Caldari + Gallente
10: "angelserp", # Minmatar + Gallente, final race depends on the order of skills
12: "sisters", # Amarr + Gallente
16: "jove",
32: "sansha", # Incrusion Sansha
128: "ore"}
map = {
1 : "caldari",
2 : "minmatar",
4 : "amarr",
5 : "sansha", # Caldari + Amarr
6 : "blood", # Minmatar + Amarr
8 : "gallente",
9 : "guristas", # Caldari + Gallente
10 : "angelserp", # Minmatar + Gallente, final race depends on the order of skills
12 : "sisters", # Amarr + Gallente
16 : "jove",
32 : "sansha", # Incrusion Sansha
128: "ore"
}
# Race is None by default
race = None
# Check primary and secondary required skills' races
@@ -422,7 +421,7 @@ class Item(EqBase):
def __repr__(self):
return "Item(ID={}, name={}) at {}".format(
self.ID, self.name, hex(id(self))
self.ID, self.name, hex(id(self))
)
@@ -447,7 +446,6 @@ class Category(EqBase):
class AlphaClone(EqBase):
@reconstructor
def init(self):
self.skillCache = {}
@@ -475,10 +473,9 @@ class Icon(EqBase):
class MarketGroup(EqBase):
def __repr__(self):
return u"MarketGroup(ID={}, name={}, parent={}) at {}".format(
self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self))
self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self))
).encode('utf8')

View File

@@ -21,6 +21,9 @@ from math import log, sin, radians, exp
from eos.graph import Graph
from eos.saveddata.module import State, Hardpoint
from logbook import Logger
pyfalog = Logger(__name__)
class FitDpsGraph(Graph):
@@ -65,8 +68,9 @@ class FitDpsGraph(Graph):
bonus = values[i]
val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289)
data[attr] = val
except:
pass
except Exception as e:
pyfalog.critical("Caught exception in calcDPS.")
pyfalog.critical(e)
for mod in fit.modules:
dps, _ = mod.damageStats(fit.targetResists)

View File

@@ -1,31 +0,0 @@
# ===============================================================================
# Copyright (C) 2010 Anton Vorobyov
#
# 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 <http://www.gnu.org/licenses/>.
# ===============================================================================
import decimal
def floorFloat(value):
"""Round float down to integer"""
# We have to convert float to str to keep compatibility with
# decimal module in python 2.6
value = str(value)
# Do the conversions for proper rounding down, avoiding float
# representation errors
result = int(decimal.Decimal(value).to_integral_value(rounding=decimal.ROUND_DOWN))
return result

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import reconstructor, validates
@@ -25,7 +25,7 @@ import eos.db
from eos.effectHandlerHelpers import HandledItem
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Booster(HandledItem, ItemAttrShortcut):
@@ -47,11 +47,11 @@ class Booster(HandledItem, ItemAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not a Booser", self.itemID)
pyfalog.error("Item (id: {0}) is not a Booster", self.itemID)
return
self.build()

View File

@@ -18,7 +18,7 @@
# ===============================================================================
import sys
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
@@ -26,7 +26,7 @@ import eos.db
from eos.effectHandlerHelpers import HandledItem
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Cargo(HandledItem, ItemAttrShortcut):
@@ -47,7 +47,7 @@ class Cargo(HandledItem, ItemAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
self.__itemModifiedAttributes = ModifiedAttributeDict()

View File

@@ -18,7 +18,7 @@
# ===============================================================================
import logging
from logbook import Logger
from itertools import chain
from sqlalchemy.orm import validates, reconstructor
@@ -27,7 +27,7 @@ import eos
import eos.db
from eos.effectHandlerHelpers import HandledItem, HandledImplantBoosterList
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Character(object):

View File

@@ -17,16 +17,17 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from eos.saveddata.ship import Ship
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Citadel(Ship):
def validate(self, item):
if item.category.name != "Structure":
pyfalog.error("Passed item '{0}' (category: {1}) is not under Structure category", item.name, item.category.name)
raise ValueError(
'Passed item "%s" (category: (%s)) is not under Structure category' % (item.name, item.category.name))

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
@@ -25,7 +25,7 @@ import eos.db
from eos.effectHandlerHelpers import HandledItem, HandledCharge
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@@ -53,11 +53,11 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not a Drone", self.itemID)
pyfalog.error("Item (id: {0}) is not a Drone", self.itemID)
return
self.build()

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
@@ -27,7 +27,7 @@ from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, C
from eos.saveddata.fighterAbility import FighterAbility
from eos.saveddata.module import Slot
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@@ -61,11 +61,11 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not a Fighter", self.itemID)
pyfalog.error("Item (id: {0}) is not a Fighter", self.itemID)
return
self.build()

View File

@@ -17,11 +17,11 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import reconstructor
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class FighterAbility(object):
@@ -59,7 +59,7 @@ class FighterAbility(object):
if self.effectID:
self.__effect = next((x for x in self.fighter.item.effects.itervalues() if x.ID == self.effectID), None)
if self.__effect is None:
logger.error("Effect (id: %d) does not exist", self.effectID)
pyfalog.error("Effect (id: {0}) does not exist", self.effectID)
return
self.build()

View File

@@ -33,9 +33,9 @@ from eos.saveddata.character import Character
from eos.saveddata.citadel import Citadel
from eos.saveddata.module import Module, State, Slot, Hardpoint
from utils.timer import Timer
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class ImplantLocation(Enum):
@@ -84,7 +84,7 @@ class Fit(object):
if self.shipID:
item = eos.db.getItem(self.shipID)
if item is None:
logger.error("Item (id: %d) does not exist", self.shipID)
pyfalog.error("Item (id: {0}) does not exist", self.shipID)
return
try:
@@ -97,7 +97,7 @@ class Fit(object):
# change all instances in source). Remove this at some point
self.extraAttributes = self.__ship.itemModifiedAttributes
except ValueError:
logger.error("Item (id: %d) is not a Ship", self.shipID)
pyfalog.error("Item (id: {0}) is not a Ship", self.shipID)
return
if self.modeID and self.__ship:
@@ -127,6 +127,12 @@ class Fit(object):
self.__capUsed = None
self.__capRecharge = None
self.__calculatedTargets = []
self.__remoteReps = {
"Armor": None,
"Shield": None,
"Hull": None,
"Capacitor": None,
}
self.factorReload = False
self.boostsFits = set()
self.gangBoosts = None
@@ -392,6 +398,9 @@ class Fit(object):
self.ecmProjectedStr = 1
self.commandBonuses = {}
for remoterep_type in self.__remoteReps:
self.__remoteReps[remoterep_type] = None
del self.__calculatedTargets[:]
del self.__extraDrains[:]
@@ -448,7 +457,7 @@ class Fit(object):
self.commandBonuses[warfareBuffID] = (runTime, value, module, effect)
def __runCommandBoosts(self, runTime="normal"):
logger.debug("Applying gang boosts for %r", self)
pyfalog.debug("Applying gang boosts for {0}", self)
for warfareBuffID in self.commandBonuses.keys():
# Unpack all data required to run effect properly
effect_runTime, value, thing, effect = self.commandBonuses[warfareBuffID]
@@ -539,26 +548,32 @@ class Fit(object):
if warfareBuffID == 22: # Skirmish Burst: Rapid Deployment: AB/MWD Speed Increase
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Afterburner") or mod.item.requiresSkill(
"High Speed Maneuvering"), "speedFactor", value, stackingPenalties=True)
lambda mod: mod.item.requiresSkill("Afterburner")
or mod.item.requiresSkill("High Speed Maneuvering"),
"speedFactor", value, stackingPenalties=True)
if warfareBuffID == 23: # Mining Burst: Mining Laser Field Enhancement: Mining/Survey Range
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill(
"Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "maxRange",
value, stackingPenalties=True)
lambda mod: mod.item.requiresSkill("Mining") or
mod.item.requiresSkill("Ice Harvesting") or
mod.item.requiresSkill("Gas Cloud Harvesting"),
"maxRange", value, stackingPenalties=True)
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("CPU Management"),
"surveyScanRange", value, stackingPenalties=True)
if warfareBuffID == 24: # Mining Burst: Mining Laser Optimization: Mining Capacitor/Duration
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill(
"Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"),
lambda mod: mod.item.requiresSkill("Mining") or
mod.item.requiresSkill("Ice Harvesting") or
mod.item.requiresSkill("Gas Cloud Harvesting"),
"capacitorNeed", value, stackingPenalties=True)
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Mining") or mod.item.requiresSkill(
"Ice Harvesting") or mod.item.requiresSkill("Gas Cloud Harvesting"), "duration",
value, stackingPenalties=True)
lambda mod: mod.item.requiresSkill("Mining") or
mod.item.requiresSkill("Ice Harvesting") or
mod.item.requiresSkill("Gas Cloud Harvesting"),
"duration", value, stackingPenalties=True)
if warfareBuffID == 25: # Mining Burst: Mining Equipment Preservation: Crystal Volatility
self.modules.filteredItemBoost(lambda mod: mod.item.requiresSkill("Mining"),
@@ -630,21 +645,21 @@ class Fit(object):
del self.commandBonuses[warfareBuffID]
def calculateModifiedAttributes(self, targetFit=None, withBoosters=False, dirtyStorage=None):
timer = Timer(u'Fit: {}, {}'.format(self.ID, self.name), logger)
logger.debug("Starting fit calculation on: %r, withBoosters: %s", self, withBoosters)
timer = Timer(u'Fit: {}, {}'.format(self.ID, self.name), pyfalog)
pyfalog.debug("Starting fit calculation on: {0}, withBoosters: {1}", self, withBoosters)
shadow = False
if targetFit and not withBoosters:
logger.debug("Applying projections to target: %r", targetFit)
pyfalog.debug("Applying projections to target: {0}", targetFit)
projectionInfo = self.getProjectionInfo(targetFit.ID)
logger.debug("ProjectionInfo: %s", projectionInfo)
pyfalog.debug("ProjectionInfo: {0}", projectionInfo)
if self == targetFit:
copied = self # original fit
shadow = True
# Don't inspect this, we genuinely want to reassign self
# noinspection PyMethodFirstArgAssignment
self = deepcopy(self)
logger.debug("Handling self projection - making shadow copy of fit. %r => %r", copied, self)
pyfalog.debug("Handling self projection - making shadow copy of fit. {0} => {1}", copied, self)
# we delete the fit because when we copy a fit, flush() is
# called to properly handle projection updates. However, we do
# not want to save this fit to the database, so simply remove it
@@ -679,7 +694,7 @@ class Fit(object):
# projection have modifying stuff applied, such as gang boosts and other
# local modules that may help
if self.__calculated and not projected and not withBoosters:
logger.debug("Fit has already been calculated and is not projected, returning: %r", self)
pyfalog.debug("Fit has already been calculated and is not projected, returning: {0}", self)
return
for runTime in ("early", "normal", "late"):
@@ -730,6 +745,10 @@ class Fit(object):
# targetFit.register(item, origin=self)
item.calculateModifiedAttributes(targetFit, runTime, False, True)
if len(self.commandBonuses) > 0:
pyfalog.info("Command bonuses applied.")
pyfalog.debug(self.commandBonuses)
if not withBoosters and self.commandBonuses:
self.__runCommandBoosts(runTime)
@@ -747,7 +766,7 @@ class Fit(object):
timer.checkpoint('Done with fit calculation')
if shadow:
logger.debug("Delete shadow fit object")
pyfalog.debug("Delete shadow fit object")
del self
def fill(self):
@@ -914,20 +933,19 @@ class Fit(object):
return amount
# Expresses how difficult a target is to probe down with scan probes
# If this is <1.08, the ship is unproabeable
@property
def probeSize(self):
"""
Expresses how difficult a target is to probe down with scan probes
"""
sigRad = self.ship.getModifiedItemAttr("signatureRadius")
sensorStr = float(self.scanStrength)
probeSize = sigRad / sensorStr if sensorStr != 0 else None
# http://www.eveonline.com/ingameboard.asp?a=topic&threadID=1532170&page=2#42
if probeSize is not None:
# http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333691
# http://forum.eve-ru.com/index.php?showtopic=74195&view=findpost&p=1333763
# Tests by tester128 and several conclusions by me, prove that cap is in range
# from 1.1 to 1.12, we're picking average value
probeSize = max(probeSize, 1.11)
# Probe size is capped at 1.08
probeSize = max(probeSize, 1.08)
return probeSize
@property
@@ -1147,6 +1165,67 @@ class Fit(object):
self.__capStable = True
self.__capState = 100
@property
def remoteReps(self):
force_recalc = False
for remote_type in self.__remoteReps:
if self.__remoteReps[remote_type] is None:
force_recalc = True
break
if force_recalc is False:
return self.__remoteReps
# We are rerunning the recalcs. Explicitly set to 0 to make sure we don't duplicate anything and correctly set all values to 0.
for remote_type in self.__remoteReps:
self.__remoteReps[remote_type] = 0
for module in self.modules:
# Skip empty and non-Active modules
if module.isEmpty or module.state < State.ACTIVE:
continue
# Covert cycleTime to seconds
duration = module.cycleTime / 1000
# Skip modules with no duration.
if not duration:
continue
fueledMultiplier = module.getModifiedItemAttr("chargedArmorDamageMultiplier", 1)
remote_module_groups = {
"Remote Armor Repairer" : "Armor",
"Ancillary Remote Armor Repairer": "Armor",
"Remote Hull Repairer" : "Hull",
"Remote Shield Booster" : "Shield",
"Ancillary Remote Shield Booster": "Shield",
"Remote Capacitor Transmitter" : "Capacitor",
}
module_group = module.item.group.name
if module_group in remote_module_groups:
remote_type = remote_module_groups[module_group]
else:
# Module isn't in our list of remote rep modules, bail
continue
if remote_type == "Hull":
hp = module.getModifiedItemAttr("structureDamageAmount", 0)
elif remote_type == "Armor":
hp = module.getModifiedItemAttr("armorDamageAmount", 0)
elif remote_type == "Shield":
hp = module.getModifiedItemAttr("shieldBonus", 0)
elif remote_type == "Capacitor":
hp = module.getModifiedItemAttr("powerTransferAmount", 0)
else:
hp = 0
self.__remoteReps[remote_type] += (hp * fueledMultiplier) / duration
return self.__remoteReps
@property
def hp(self):
hp = {}

View File

@@ -17,7 +17,7 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
@@ -25,7 +25,7 @@ import eos.db
from eos.effectHandlerHelpers import HandledItem
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Implant(HandledItem, ItemAttrShortcut):
@@ -46,11 +46,11 @@ class Implant(HandledItem, ItemAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not an Implant", self.itemID)
pyfalog.error("Item (id: {0}) is not an Implant", self.itemID)
return
self.build()

View File

@@ -17,18 +17,18 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import validates, reconstructor
from math import floor
import eos.db
from eos.effectHandlerHelpers import HandledItem, HandledCharge
from eos.enum import Enum
from eos.mathUtils import floorFloat
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, ChargeAttrShortcut
from eos.saveddata.citadel import Citadel
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class State(Enum):
@@ -94,11 +94,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
if self.isInvalid:
logger.error("Item (id: %d) is not a Module", self.itemID)
pyfalog.error("Item (id: {0}) is not a Module", self.itemID)
return
if self.chargeID:
@@ -172,7 +172,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if chargeVolume is None or containerCapacity is None:
charges = 0
else:
charges = floorFloat(float(containerCapacity) / chargeVolume)
charges = floor(containerCapacity / chargeVolume)
return charges
@property
@@ -216,9 +216,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def __calculateAmmoShots(self):
if self.charge is not None:
# Set number of cycles before reload is needed
# numcycles = math.floor(module_capacity / (module_volume * module_chargerate))
chargeRate = self.getModifiedItemAttr("chargeRate")
numCharges = self.numCharges
numShots = floorFloat(float(numCharges) / chargeRate)
numShots = floor(numCharges / chargeRate)
else:
numShots = None
return numShots
@@ -231,7 +232,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
chance = self.getModifiedChargeAttr("crystalVolatilityChance")
damage = self.getModifiedChargeAttr("crystalVolatilityDamage")
crystals = self.numCharges
numShots = floorFloat(float(crystals * hp) / (damage * chance))
numShots = floor((crystals * hp) / (damage * chance))
else:
# Set 0 (infinite) for permanent crystals like t1 laser crystals
numShots = 0
@@ -665,32 +666,68 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
@property
def cycleTime(self):
reactivation = (self.getModifiedItemAttr("moduleReactivationDelay") or 0)
# Reactivation time starts counting after end of module cycle
speed = self.rawCycleTime + reactivation
if self.charge:
reload = self.reloadTime
else:
reload = 0.0
# Determine if we'll take into account reload time or not
factorReload = self.owner.factorReload if self.forceReload is None else self.forceReload
# If reactivation is longer than 10 seconds then module can be reloaded
# during reactivation time, thus we may ignore reload
if factorReload and reactivation < reload:
numShots = self.numShots
# Time it takes to reload module after end of reactivation time,
# given that we started when module cycle has just over
additionalReloadTime = (reload - reactivation)
# Speed here already takes into consideration reactivation time
speed = (speed * numShots + additionalReloadTime) / numShots if numShots > 0 else speed
numShots = self.numShots
speed = self.rawCycleTime
if factorReload and self.charge:
raw_reload_time = self.reloadTime
else:
raw_reload_time = 0.0
# Module can only fire one shot at a time, think bomb launchers or defender launchers
if self.disallowRepeatingAction:
if numShots > 1:
"""
The actual mechanics behind this is complex. Behavior will be (for 3 ammo):
fire, reactivation delay, fire, reactivation delay, fire, max(reactivation delay, reload)
so your effective reload time depends on where you are at in the cycle.
We can't do that, so instead we'll average it out.
Currently would apply to bomb launchers and defender missiles
"""
effective_reload_time = ((self.reactivationDelay * (numShots - 1)) + max(raw_reload_time, self.reactivationDelay, 0)) / numShots
else:
"""
Applies to MJD/MJFG
"""
effective_reload_time = max(raw_reload_time, self.reactivationDelay, 0)
else:
"""
Currently no other modules would have a reactivation delay, so for sanities sake don't try and account for it.
Okay, technically cloaks do, but they also have 0 cycle time and cap usage so why do you care?
"""
effective_reload_time = raw_reload_time
if numShots > 0 and self.charge:
speed = (speed * numShots + effective_reload_time) / numShots
return speed
@property
def rawCycleTime(self):
speed = self.getModifiedItemAttr("speed") or self.getModifiedItemAttr("duration")
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
)
return speed
@property
def disallowRepeatingAction(self):
return self.getModifiedItemAttr("disallowRepeatingAction", 0)
@property
def reactivationDelay(self):
return self.getModifiedItemAttr("moduleReactivationDelay", 0)
@property
def capUse(self):
capNeed = self.getModifiedItemAttr("capacitorNeed")

View File

@@ -17,14 +17,14 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
from sqlalchemy.orm import reconstructor
import eos.db
from eos.eqBase import EqBase
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Override(EqBase):
@@ -43,13 +43,13 @@ class Override(EqBase):
if self.attrID:
self.__attr = eos.db.getAttributeInfo(self.attrID)
if self.__attr is None:
logger.error("Attribute (id: %d) does not exist", self.attrID)
pyfalog.error("Attribute (id: {0}) does not exist", self.attrID)
return
if self.itemID:
self.__item = eos.db.getItem(self.itemID)
if self.__item is None:
logger.error("Item (id: %d) does not exist", self.itemID)
pyfalog.error("Item (id: {0}) does not exist", self.itemID)
return
@property

View File

@@ -17,14 +17,14 @@
# along with eos. If not, see <http://www.gnu.org/licenses/>.
# ===============================================================================
import logging
from logbook import Logger
import eos.db
from eos.effectHandlerHelpers import HandledItem
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut, cappingAttrKeyCache
from eos.saveddata.mode import Mode
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class Ship(ItemAttrShortcut, HandledItem):
@@ -63,6 +63,7 @@ class Ship(ItemAttrShortcut, HandledItem):
def validate(self, item):
if item.category.name != "Ship":
pyfalog.error("Passed item '{0}' (category: {1}) is not under Ship category", item.name, item.category.name)
raise ValueError(
'Passed item "%s" (category: (%s)) is not under Ship category' % (item.name, item.category.name))

View File

@@ -18,6 +18,9 @@
# ===============================================================================
import re
from logbook import Logger
pyfalog = Logger(__name__)
class TargetResists(object):
@@ -43,7 +46,7 @@ class TargetResists(object):
type, data = line.rsplit('=', 1)
type, data = type.strip(), data.split(',')
except:
# Data isn't in correct format, continue to next line
pyfalog.warning("Data isn't in correct format, continue to next line.")
continue
if type != "TargetResists":
@@ -59,6 +62,7 @@ class TargetResists(object):
assert 0 <= val <= 100
fields["%sAmount" % cls.DAMAGE_TYPES[index]] = val / 100
except:
pyfalog.warning("Caught unhandled exception in import patterns.")
continue
if len(fields) == 4: # Avoid possible blank lines

BIN
eve.db

Binary file not shown.

View File

@@ -26,6 +26,9 @@ import wx
import config
from logbook import Logger
logging = Logger(__name__)
try:
from collections import OrderedDict
except ImportError:
@@ -35,7 +38,9 @@ except ImportError:
class BitmapLoader(object):
try:
archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r')
logging.info("Using zipped image files.")
except IOError:
logging.info("Using local image files.")
archive = None
cachedBitmaps = OrderedDict()

View File

@@ -22,4 +22,6 @@ __all__ = [
"metaSwap",
"implantSets",
"fighterAbilities",
"cargoAmmo",
"droneStack"
]

View File

@@ -4,13 +4,18 @@ import gui.mainFrame
import wx
import gui.globalEvents as GE
from service.fit import Fit
from service.settings import ContextMenuSettings
class AmmoPattern(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('ammoPattern'):
return False
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False

View File

@@ -7,13 +7,18 @@ import wx
from service.fit import Fit
from eos.saveddata.cargo import Cargo as es_Cargo
from eos.saveddata.fighter import Fighter as es_Fighter
from service.settings import ContextMenuSettings
class ChangeAmount(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('amount'):
return False
return srcContext in ("cargoItem", "projectedFit", "fighterItem", "projectedFighter")
def getText(self, itmContext, selection):

View File

@@ -4,20 +4,28 @@ import gui.globalEvents as GE
# noinspection PyPackageRequirements
import wx
from service.fit import Fit
from service.settings import ContextMenuSettings
class Cargo(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('cargo'):
return False
if srcContext not in ("marketItemGroup", "marketItemMisc"):
return False
sFit = Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(fitID)
# Make sure context menu registers in the correct view
if srcContext not in ("marketItemGroup", "marketItemMisc") or not fit or fit.isStructure:
if not fit or fit.isStructure:
return False
return True
def getText(self, itmContext, selection):

View File

@@ -0,0 +1,35 @@
from gui.contextMenu import ContextMenu
import gui.mainFrame
import service
import gui.globalEvents as GE
import wx
class CargoAmmo(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, srcContext, selection):
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False
for selected_item in selection:
if selected_item.category.ID in (
8, # Charge
):
return True
def getText(self, itmContext, selection):
return "Add {0} to Cargo (x1000)".format(itmContext)
def activate(self, fullContext, selection, i):
sFit = service.Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
typeID = int(selection[0].ID)
sFit.addCargo(fitID, typeID, 1000)
self.mainFrame.additionsPane.select("Cargo")
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
CargoAmmo.register()

View File

@@ -8,13 +8,18 @@ from eos.saveddata.character import Skill
import gui.globalEvents as GE
from service.fit import Fit
from service.character import Character
from service.settings import ContextMenuSettings
class ChangeAffectingSkills(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('changeAffectingSkills'):
return False
if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "fittingCharge", "fittingShip"):
return False

View File

@@ -6,6 +6,7 @@ import wx
from gui.bitmapLoader import BitmapLoader
from service.fit import Fit
from service.damagePattern import DamagePattern as import_DamagePattern
from service.settings import ContextMenuSettings
try:
from collections import OrderedDict
@@ -16,8 +17,12 @@ except ImportError:
class DamagePattern(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('damagePattern'):
return False
return srcContext == "resistancesViewFull" and self.mainFrame.getActiveFit() is not None
def getText(self, itmContext, selection):

View File

@@ -4,13 +4,18 @@ import gui.mainFrame
import wx
import gui.globalEvents as GE
from service.fit import Fit
from service.settings import ContextMenuSettings
class ItemRemove(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('droneRemoveStack'):
return False
return srcContext == "droneItem"
def getText(self, itmContext, selection):

View File

@@ -4,13 +4,18 @@ import gui.globalEvents as GE
from service.fit import Fit
# noinspection PyPackageRequirements
import wx
from service.settings import ContextMenuSettings
class DroneSplit(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('droneSplit'):
return False
return srcContext in ("droneItem", "projectedDrone") and selection[0].amount > 1
def getText(self, itmContext, selection):

View File

@@ -0,0 +1,37 @@
from gui.contextMenu import ContextMenu
import gui.mainFrame
import service
import gui.globalEvents as GE
import wx
class CargoAmmo(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def display(self, srcContext, selection):
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False
for selected_item in selection:
if selected_item.category.ID in (
18, # Drones
):
return True
return False
def getText(self, itmContext, selection):
return "Add {0} to Drone Bay (x5)".format(itmContext)
def activate(self, fullContext, selection, i):
sFit = service.Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
typeID = int(selection[0].ID)
sFit.addDrone(fitID, typeID, 5)
self.mainFrame.additionsPane.select("Drones")
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
CargoAmmo.register()

View File

@@ -5,13 +5,18 @@ import gui.globalEvents as GE
import wx
from gui.bitmapLoader import BitmapLoader
from service.fit import Fit
from service.settings import ContextMenuSettings
class FactorReload(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('factorReload'):
return False
return srcContext == "firepowerViewFull" and self.mainFrame.getActiveFit() is not None
def getText(self, itmContext, selection):

View File

@@ -4,13 +4,18 @@ from gui.contextMenu import ContextMenu
import gui.mainFrame
import gui.globalEvents as GE
from service.fit import Fit
from service.settings import ContextMenuSettings
class FighterAbility(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('fighterAbilities'):
return False
if self.mainFrame.getActiveFit() is None or srcContext not in ("fighterItem", "projectedFighter"):
return False

View File

@@ -6,13 +6,18 @@ import wx
from service.implantSet import ImplantSets as s_ImplantSets
from service.character import Character
from service.fit import Fit
from service.settings import ContextMenuSettings
class ImplantSets(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('implantSets'):
return False
return srcContext in ("implantView", "implantEditor")
def getText(self, itmContext, selection):

View File

@@ -4,13 +4,18 @@ import gui.mainFrame
import wx
import gui.globalEvents as GE
from service.fit import Fit
from service.settings import ContextMenuSettings
class ItemRemove(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('itemRemove'):
return False
return srcContext in ("fittingModule", "fittingCharge",
"droneItem", "implantItem",
"boosterItem", "projectedModule",

View File

@@ -4,13 +4,17 @@ import gui.mainFrame
# noinspection PyPackageRequirements
import wx
from service.fit import Fit
from service.settings import ContextMenuSettings
class ItemStats(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('itemStats'):
return False
return srcContext in ("marketItemGroup", "marketItemMisc",
"fittingModule", "fittingCharge",

View File

@@ -1,13 +1,18 @@
from gui.contextMenu import ContextMenu
import gui.mainFrame
from service.market import Market
from service.settings import ContextMenuSettings
class MarketJump(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('marketJump'):
return False
validContexts = ("marketItemMisc", "fittingModule",
"fittingCharge", "droneItem",
"implantItem", "boosterItem",

View File

@@ -8,15 +8,30 @@ from service.market import Market
import gui.mainFrame
import gui.globalEvents as GE
from gui.contextMenu import ContextMenu
from service.settings import ContextMenuSettings
from eos.saveddata.booster import Booster
from eos.saveddata.module import Module
from eos.saveddata.drone import Drone
from eos.saveddata.fighter import Fighter
from eos.saveddata.implant import Implant
class MetaSwap(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('metaSwap'):
return False
if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule",):
if self.mainFrame.getActiveFit() is None or srcContext not in (
"fittingModule",
"droneItem",
"fighterItem",
"boosterItem",
"implantItem",
):
return False
# Check if list of variations is same for all of selection
@@ -52,6 +67,17 @@ class MetaSwap(ContextMenu):
def get_metagroup(x):
return x.metaGroup.ID if x.metaGroup is not None else 0
def get_boosterrank(x):
# If we're returning a lot of items, sort my name
if len(self.variations) > 7:
return x.name
# Sort by booster chance to get some sort of pseudorank.
elif 'boosterEffectChance1' in x.attributes:
return x.attributes['boosterEffectChance1'].value
# the "first" rank (Synth) doesn't have boosterEffectChance1. If we're not pulling back all boosters, return 0 for proper sorting
else:
return 0
m = wx.Menu()
# If on Windows we need to bind out events into the root menu, on other
@@ -63,8 +89,17 @@ class MetaSwap(ContextMenu):
# Sort items by metalevel, and group within that metalevel
items = list(self.variations)
items.sort(key=get_metalevel)
items.sort(key=get_metagroup)
print context
if "implantItem" in context:
# sort implants based on name
items.sort(key=lambda x: x.name)
elif "boosterItem" in context:
# boosters don't have meta or anything concrete that we can rank by. Go by chance to inflict side effect
items.sort(key=get_boosterrank)
else:
# sort by group and meta level
items.sort(key=get_metalevel)
items.sort(key=get_metagroup)
group = None
for item in items:
@@ -74,7 +109,7 @@ class MetaSwap(ContextMenu):
else:
thisgroup = item.metaGroup.name
if thisgroup != group:
if thisgroup != group and context not in ("implantItem", "boosterItem"):
group = thisgroup
id = ContextMenu.nextID()
m.Append(id, u'%s' % group)
@@ -97,9 +132,56 @@ class MetaSwap(ContextMenu):
fitID = self.mainFrame.getActiveFit()
fit = sFit.getFit(fitID)
for mod in self.selection:
pos = fit.modules.index(mod)
sFit.changeModule(fitID, pos, item.ID)
for selected_item in self.selection:
if isinstance(selected_item, Module):
pos = fit.modules.index(selected_item)
sFit.changeModule(fitID, pos, item.ID)
elif isinstance(selected_item, Drone):
drone_count = None
for idx, drone_stack in enumerate(fit.drones):
if drone_stack is selected_item:
drone_count = drone_stack.amount
sFit.removeDrone(fitID, idx, drone_count)
break
if drone_count:
sFit.addDrone(fitID, item.ID, drone_count)
elif isinstance(selected_item, Fighter):
fighter_count = None
for idx, fighter_stack in enumerate(fit.fighters):
# Right now fighters always will have max stack size.
# Including this for future improvement, so if adjustable
# fighter stacks get added we're ready for it.
if fighter_stack is selected_item:
if fighter_stack.amount > 0:
fighter_count = fighter_stack.amount
elif fighter_stack.amount == -1:
fighter_count = fighter_stack.amountActive
else:
fighter_count.amount = 0
sFit.removeFighter(fitID, idx)
break
sFit.addFighter(fitID, item.ID)
elif isinstance(selected_item, Booster):
for idx, booster_stack in enumerate(fit.boosters):
if booster_stack is selected_item:
sFit.removeBooster(fitID, idx)
sFit.addBooster(fitID, item.ID)
break
elif isinstance(selected_item, Implant):
for idx, implant_stack in enumerate(fit.implants):
if implant_stack is selected_item:
sFit.removeImplant(fitID, idx)
sFit.addImplant(fitID, item.ID, False)
break
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))

View File

@@ -10,6 +10,7 @@ import gui.mainFrame
import gui.globalEvents as GE
from gui.contextMenu import ContextMenu
from gui.bitmapLoader import BitmapLoader
from service.settings import ContextMenuSettings
class ModuleAmmoPicker(ContextMenu):
@@ -18,8 +19,12 @@ class ModuleAmmoPicker(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('moduleAmmoPicker'):
return False
if self.mainFrame.getActiveFit() is None or srcContext not in ("fittingModule", "projectedModule"):
return False

View File

@@ -6,12 +6,14 @@ import gui.globalEvents as GE
from gui.builtinContextMenus.moduleAmmoPicker import ModuleAmmoPicker
from eos.db.saveddata.queries import getFit as db_getFit
from service.fit import Fit
from service.settings import ContextMenuSettings
class ModuleGlobalAmmoPicker(ModuleAmmoPicker):
def __init__(self):
super(ModuleGlobalAmmoPicker, self).__init__()
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def getText(self, itmContext, selection):
return "Charge (All)"
@@ -42,6 +44,9 @@ class ModuleGlobalAmmoPicker(ModuleAmmoPicker):
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
def display(self, srcContext, selection):
if not self.settings.get('moduleGlobalAmmoPicker'):
return False
try:
selectionLen = len(selection)
except:

View File

@@ -3,13 +3,18 @@ import gui.mainFrame
# noinspection PyPackageRequirements
import wx
from gui.shipBrowser import FitSelected
from service.settings import ContextMenuSettings
class OpenFit(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('openFit'):
return False
return srcContext == "projectedFit"
def getText(self, itmContext, selection):

View File

@@ -4,13 +4,18 @@ import gui.mainFrame
import wx
import gui.globalEvents as GE
from service.market import Market
from service.settings import ContextMenuSettings
class PriceClear(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('priceClear'):
return False
return srcContext == "priceViewFull"
def getText(self, itmContext, selection):

View File

@@ -4,13 +4,18 @@ import gui.globalEvents as GE
# noinspection PyPackageRequirements
import wx
from service.fit import Fit
from service.settings import ContextMenuSettings
class Project(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('project'):
return False
if srcContext not in ("marketItemGroup", "marketItemMisc") or self.mainFrame.getActiveFit() is None:
return False

View File

@@ -4,13 +4,18 @@ from gui.contextMenu import ContextMenu
import gui.mainFrame
from gui.shipBrowser import Stage3Selected
from service.fit import Fit
from service.settings import ContextMenuSettings
class ShipJump(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('shipJump'):
return False
return srcContext == "fittingShip"
def getText(self, itmContext, selection):

View File

@@ -5,13 +5,18 @@ import gui.mainFrame
import gui.globalEvents as GE
from service.fit import Fit
from service.settings import ContextMenuSettings
class TacticalMode(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('tacticalMode'):
return False
if self.mainFrame.getActiveFit() is None or srcContext != "fittingShip":
return False

View File

@@ -6,6 +6,7 @@ import wx
from gui.bitmapLoader 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
@@ -16,8 +17,12 @@ except ImportError:
class TargetResists(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('targetResists'):
return False
if self.mainFrame.getActiveFit() is None or srcContext != "firepowerViewFull":
return False

View File

@@ -5,13 +5,18 @@ import gui.globalEvents as GE
import wx
from service.market import Market
from service.fit import Fit
from service.settings import ContextMenuSettings
class WhProjector(ContextMenu):
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
def display(self, srcContext, selection):
if not self.settings.get('whProjector'):
return False
return srcContext == "projected"
def getText(self, itmContext, selection):

View File

@@ -1,8 +1,16 @@
# noinspection PyPackageRequirements
import wx
__all__ = ["pyfaGeneralPreferences", "pyfaHTMLExportPreferences", "pyfaUpdatePreferences",
"pyfaNetworkPreferences"] # noqa
__all__ = [
"pyfaGeneralPreferences",
"pyfaHTMLExportPreferences",
"pyfaUpdatePreferences",
"pyfaNetworkPreferences",
"pyfaDatabasePreferences",
"pyfaLoggingPreferences",
"pyfaEnginePreferences",
"pyfaStatViewPreferences",
]
if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)):
__all__.append("pyfaCrestPreferences")

View File

@@ -0,0 +1,114 @@
import wx
from gui.preferenceView import PreferenceView
from gui.bitmapLoader import BitmapLoader
import gui.mainFrame
from service.settings import ContextMenuSettings
class PFContextMenuPref(PreferenceView):
title = "Context Menu Panel"
def populatePanel(self, panel):
self.settings = ContextMenuSettings.getInstance()
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.dirtySettings = False
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
self.stTitle.Wrap(-1)
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
self.stSubTitle = wx.StaticText(panel, wx.ID_ANY,
u"Disabling context menus can improve responsiveness.",
wx.DefaultPosition, wx.DefaultSize, 0)
self.stSubTitle.Wrap(-1)
mainSizer.Add(self.stSubTitle, 0, wx.ALL, 5)
# Row 1
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.ALL, 5)
rbSizerRow1 = wx.BoxSizer(wx.HORIZONTAL)
self.rbBox1 = wx.RadioBox(panel, -1, "Set as Damage Pattern", wx.DefaultPosition, wx.DefaultSize, ['Disabled', 'Enabled'], 1, wx.RA_SPECIFY_COLS)
self.rbBox1.SetSelection(self.settings.get('ammoPattern'))
rbSizerRow1.Add(self.rbBox1, 1, wx.TOP | wx.RIGHT, 5)
self.rbBox1.Bind(wx.EVT_RADIOBOX, self.OnSetting1Change)
self.rbBox2 = wx.RadioBox(panel, -1, "Change Skills", wx.DefaultPosition, wx.DefaultSize, ['Disabled', 'Enabled'], 1, wx.RA_SPECIFY_COLS)
self.rbBox2.SetSelection(self.settings.get('changeAffectingSkills'))
rbSizerRow1.Add(self.rbBox2, 1, wx.ALL, 5)
self.rbBox2.Bind(wx.EVT_RADIOBOX, self.OnSetting2Change)
self.rbBox3 = wx.RadioBox(panel, -1, "Factor in Reload Time", wx.DefaultPosition, wx.DefaultSize, ['Disabled', 'Enabled'], 1, wx.RA_SPECIFY_COLS)
self.rbBox3.SetSelection(self.settings.get('factorReload'))
rbSizerRow1.Add(self.rbBox3, 1, wx.ALL, 5)
self.rbBox3.Bind(wx.EVT_RADIOBOX, self.OnSetting3Change)
mainSizer.Add(rbSizerRow1, 1, wx.ALL | wx.EXPAND, 0)
# Row 2
rbSizerRow2 = wx.BoxSizer(wx.HORIZONTAL)
self.rbBox4 = wx.RadioBox(panel, -1, "Variations", wx.DefaultPosition, wx.DefaultSize, ['Disabled', 'Enabled'], 1, wx.RA_SPECIFY_COLS)
self.rbBox4.SetSelection(self.settings.get('metaSwap'))
rbSizerRow2.Add(self.rbBox4, 1, wx.TOP | wx.RIGHT, 5)
self.rbBox4.Bind(wx.EVT_RADIOBOX, self.OnSetting4Change)
'''
self.rbBox5 = wx.RadioBox(panel, -1, "Charge", wx.DefaultPosition, wx.DefaultSize, ['Disabled', 'Enabled'], 1, wx.RA_SPECIFY_COLS)
self.rbBox5.SetSelection(self.settings.get('moduleAmmoPicker'))
rbSizerRow2.Add(self.rbBox5, 1, wx.ALL, 5)
self.rbBox5.Bind(wx.EVT_RADIOBOX, self.OnSetting5Change)
'''
self.rbBox6 = wx.RadioBox(panel, -1, "Charge (All)", wx.DefaultPosition, wx.DefaultSize, ['Disabled', 'Enabled'], 1, wx.RA_SPECIFY_COLS)
self.rbBox6.SetSelection(self.settings.get('moduleGlobalAmmoPicker'))
rbSizerRow2.Add(self.rbBox6, 1, wx.ALL, 5)
self.rbBox6.Bind(wx.EVT_RADIOBOX, self.OnSetting6Change)
mainSizer.Add(rbSizerRow2, 1, wx.ALL | wx.EXPAND, 0)
# Row 3
rbSizerRow3 = wx.BoxSizer(wx.HORIZONTAL)
self.rbBox7 = wx.RadioBox(panel, -1, "Project onto Fit", wx.DefaultPosition, wx.DefaultSize, ['Disabled', 'Enabled'], 1, wx.RA_SPECIFY_COLS)
self.rbBox7.SetSelection(self.settings.get('project'))
rbSizerRow3.Add(self.rbBox7, 1, wx.TOP | wx.RIGHT, 5)
self.rbBox7.Bind(wx.EVT_RADIOBOX, self.OnSetting7Change)
mainSizer.Add(rbSizerRow3, 1, wx.ALL | wx.EXPAND, 0)
panel.SetSizer(mainSizer)
panel.Layout()
def OnSetting1Change(self, event):
self.settings.set('ammoPattern', event.GetInt())
def OnSetting2Change(self, event):
self.settings.set('changeAffectingSkills', event.GetInt())
def OnSetting3Change(self, event):
self.settings.set('factorReload', event.GetInt())
def OnSetting4Change(self, event):
self.settings.set('metaSwap', event.GetInt())
def OnSetting5Change(self, event):
self.settings.set('moduleAmmoPicker', event.GetInt())
def OnSetting6Change(self, event):
self.settings.set('moduleGlobalAmmoPicker', event.GetInt())
def OnSetting7Change(self, event):
self.settings.set('project', event.GetInt())
def getImage(self):
return BitmapLoader.getBitmap("settings_menu", "gui")
PFContextMenuPref.register()

View File

@@ -0,0 +1,112 @@
import wx
from gui.preferenceView import PreferenceView
from gui.bitmapLoader import BitmapLoader
import config
import logging
logger = logging.getLogger(__name__)
class PFGeneralPref(PreferenceView):
title = "Database"
def populatePanel(self, panel):
self.dirtySettings = False
# self.openFitsSettings = service.SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []})
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
self.stTitle.Wrap(-1)
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, u"(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)
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)
# 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)
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.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)
self.inputUserPath.SetEditable(False)
self.inputUserPath.SetBackgroundColour((200, 200, 200))
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.Wrap(-1)
mainSizer.Add(self.stFitDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputFitDB = wx.TextCtrl(panel, wx.ID_ANY, config.saveDB, wx.DefaultPosition, wx.DefaultSize, 0)
self.inputFitDB.SetEditable(False)
self.inputFitDB.SetBackgroundColour((200, 200, 200))
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.Wrap(-1)
mainSizer.Add(self.stGameDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputGameDB = wx.TextCtrl(panel, wx.ID_ANY, config.gameDB, wx.DefaultPosition, wx.DefaultSize, 0)
self.inputGameDB.SetEditable(False)
self.inputGameDB.SetBackgroundColour((200, 200, 200))
mainSizer.Add(self.inputGameDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
self.cbsaveInRoot.SetValue(config.saveInRoot)
self.cbsaveInRoot.Bind(wx.EVT_CHECKBOX, self.onCBsaveInRoot)
self.inputUserPath.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave)
self.inputFitDB.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave)
self.inputGameDB.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave)
panel.SetSizer(mainSizer)
panel.Layout()
def onCBsaveInRoot(self, event):
# We don't want users to be able to actually change this,
# so if they try and change it, set it back to the current setting
self.cbsaveInRoot.SetValue(config.saveInRoot)
# If we ever enable it might need this.
'''
config.saveInRoot = self.cbsaveInRoot.GetValue()
'''
def getImage(self):
return BitmapLoader.getBitmap("settings_database", "gui")
def OnWindowLeave(self, event):
# We don't want to do anything when they leave,
# but in the future we'd want to make sure settings
# changed get saved.
pass
'''
#Set database path
config.defPaths(self.inputFitDBPath.GetValue())
logger.debug("Running database import")
if self.cbimportDefaults is True:
# Import default database values
# Import values that must exist otherwise Pyfa breaks
DefaultDatabaseValues.importRequiredDefaults()
# Import default values for damage profiles
DefaultDatabaseValues.importDamageProfileDefaults()
# Import default values for target resist profiles
DefaultDatabaseValues.importResistProfileDefaults()
'''
PFGeneralPref.register()

View File

@@ -0,0 +1,84 @@
import logging
import wx
from service.fit import Fit
from gui.bitmapLoader import BitmapLoader
from gui.preferenceView import PreferenceView
logger = logging.getLogger(__name__)
class PFFittingEnginePref(PreferenceView):
title = "Fitting Engine"
def __init__(self):
self.dirtySettings = False
def refreshPanel(self, fit):
pass
# noinspection PyAttributeOutsideInit
def populatePanel(self, panel):
# self.openFitsSettings = service.SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []})
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
self.stTitle.Wrap(-1)
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
self.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.",
wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(self.cbGlobalForceReload, 0, wx.ALL | wx.EXPAND, 5)
# Future code once new cap sim is implemented
'''
self.cbGlobalForceReactivationTimer = wx.CheckBox( panel, wx.ID_ANY, u"Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
mainSizer.Add( self.cbGlobalForceReactivationTimer, 0, wx.ALL|wx.EXPAND, 5 )
text = u" Ignores reactivation timer when calculating capacitor usage,\n damage, and tank."
self.cbGlobalForceReactivationTimerText = wx.StaticText( panel, wx.ID_ANY, text, wx.DefaultPosition, wx.DefaultSize, 0 )
self.cbGlobalForceReactivationTimerText.Wrap( -1 )
self.cbGlobalForceReactivationTimerText.SetFont( wx.Font( 10, 70, 90, 90, False, wx.EmptyString ) )
mainSizer.Add( self.cbGlobalForceReactivationTimerText, 0, wx.ALL, 5 )
'''
# Future code for mining laser crystal
'''
self.cbGlobalMiningSpecialtyCrystal = wx.CheckBox( panel, wx.ID_ANY, u"Factor in reactivation timer", wx.DefaultPosition, wx.DefaultSize, 0 )
mainSizer.Add( self.cbGlobalMiningSpecialtyCrystal, 0, wx.ALL|wx.EXPAND, 5 )
text = u" If enabled, displays the Specialty Crystal mining amount.\n This is the amount mined when using crystals and mining the matching asteroid."
self.cbGlobalMiningSpecialtyCrystalText = wx.StaticText( panel, wx.ID_ANY, text, wx.DefaultPosition, wx.DefaultSize, 0 )
self.cbGlobalMiningSpecialtyCrystalText.Wrap( -1 )
self.cbGlobalMiningSpecialtyCrystalText.SetFont( wx.Font( 10, 70, 90, 90, False, wx.EmptyString ) )
mainSizer.Add( self.cbGlobalMiningSpecialtyCrystalText, 0, wx.ALL, 5 )
'''
self.sFit = Fit.getInstance()
self.cbGlobalForceReload.SetValue(self.sFit.serviceFittingOptions["useGlobalForceReload"])
self.cbGlobalForceReload.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalForceReloadStateChange)
panel.SetSizer(mainSizer)
panel.Layout()
def OnCBGlobalForceReloadStateChange(self, event):
self.sFit.serviceFittingOptions["useGlobalForceReload"] = self.cbGlobalForceReload.GetValue()
def getImage(self):
return BitmapLoader.getBitmap("settings_fitting", "gui")
def OnWindowLeave(self, event):
# We don't want to do anything when they leave,
# but in the future we might.
pass
PFFittingEnginePref.register()

View File

@@ -40,10 +40,6 @@ class PFGeneralPref(PreferenceView):
wx.DefaultSize, 0)
mainSizer.Add(self.cbGlobalDmgPattern, 0, wx.ALL | wx.EXPAND, 5)
self.cbGlobalForceReload = wx.CheckBox(panel, wx.ID_ANY, u"Factor in reload time", wx.DefaultPosition,
wx.DefaultSize, 0)
mainSizer.Add(self.cbGlobalForceReload, 0, wx.ALL | wx.EXPAND, 5)
self.cbCompactSkills = wx.CheckBox(panel, wx.ID_ANY, u"Compact skills needed tooltip", wx.DefaultPosition,
wx.DefaultSize, 0)
mainSizer.Add(self.cbCompactSkills, 0, wx.ALL | wx.EXPAND, 5)
@@ -97,7 +93,6 @@ class PFGeneralPref(PreferenceView):
self.cbGlobalChar.SetValue(self.sFit.serviceFittingOptions["useGlobalCharacter"])
self.cbGlobalDmgPattern.SetValue(self.sFit.serviceFittingOptions["useGlobalDamagePattern"])
self.cbGlobalForceReload.SetValue(self.sFit.serviceFittingOptions["useGlobalForceReload"])
self.cbFitColorSlots.SetValue(self.sFit.serviceFittingOptions["colorFitBySlot"] or False)
self.cbRackSlots.SetValue(self.sFit.serviceFittingOptions["rackSlots"] or False)
self.cbRackLabels.SetValue(self.sFit.serviceFittingOptions["rackLabels"] or False)
@@ -112,7 +107,6 @@ class PFGeneralPref(PreferenceView):
self.cbGlobalChar.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalCharStateChange)
self.cbGlobalDmgPattern.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalDmgPatternStateChange)
self.cbGlobalForceReload.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalForceReloadStateChange)
self.cbFitColorSlots.Bind(wx.EVT_CHECKBOX, self.onCBGlobalColorBySlot)
self.cbRackSlots.Bind(wx.EVT_CHECKBOX, self.onCBGlobalRackSlots)
self.cbRackLabels.Bind(wx.EVT_CHECKBOX, self.onCBGlobalRackLabels)
@@ -152,13 +146,6 @@ class PFGeneralPref(PreferenceView):
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
event.Skip()
def OnCBGlobalForceReloadStateChange(self, event):
self.sFit.serviceFittingOptions["useGlobalForceReload"] = self.cbGlobalForceReload.GetValue()
fitID = self.mainFrame.getActiveFit()
self.sFit.refreshFit(fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
event.Skip()
def OnCBGlobalCharStateChange(self, event):
self.sFit.serviceFittingOptions["useGlobalCharacter"] = self.cbGlobalChar.GetValue()
event.Skip()

View File

@@ -0,0 +1,59 @@
import wx
from gui.preferenceView import PreferenceView
from gui.bitmapLoader import BitmapLoader
import config
class PFGeneralPref(PreferenceView):
title = "Logging"
def populatePanel(self, panel):
self.dirtySettings = False
# self.openFitsSettings = service.SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []})
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
self.stTitle.Wrap(-1)
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, u"(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)
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)
# Debug Logging
self.cbdebugLogging = wx.CheckBox(panel, wx.ID_ANY, u"Debug Logging Enabled", wx.DefaultPosition, wx.DefaultSize, 0)
mainSizer.Add(self.cbdebugLogging, 0, wx.ALL | wx.EXPAND, 5)
self.cbdebugLogging.SetValue(config.debug)
self.cbdebugLogging.Bind(wx.EVT_CHECKBOX, self.onCBdebugLogging)
panel.SetSizer(mainSizer)
panel.Layout()
def onCBdebugLogging(self, event):
# We don't want users to be able to actually change this,
# so if they try and change it, set it back to the current setting
self.cbdebugLogging.SetValue(config.debug)
# In case we do, down there road, here's a bit of a start.
'''
if self.cbdebugLogging.GetValue() is True:
self.cbdebugLogging.SetValue(False)
config.Debug = self.cbdebugLogging.GetValue()
else:
self.cbdebugLogging.SetValue(True)
config.Debug = self.cbdebugLogging.GetValue()
'''
def getImage(self):
return BitmapLoader.getBitmap("settings_log", "gui")
PFGeneralPref.register()

View File

@@ -0,0 +1,158 @@
# noinspection PyPackageRequirements
import wx
from gui.preferenceView import PreferenceView
from gui.bitmapLoader import BitmapLoader
from service.settings import StatViewSettings
class PFStatViewPref(PreferenceView):
title = "Statistics Panel"
def __init__(self):
self.dirtySettings = False
self.settings = StatViewSettings.getInstance()
def refreshPanel(self, fit):
pass
# noinspection PyAttributeOutsideInit
def populatePanel(self, panel):
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0)
self.stTitle.Wrap(-1)
self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))
mainSizer.Add(self.stTitle, 0, wx.ALL, 5)
self.stSubTitle = wx.StaticText(panel, wx.ID_ANY,
u"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)
# Row 1
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.ALL, 5)
rbSizerRow1 = wx.BoxSizer(wx.HORIZONTAL)
self.rbResources = wx.RadioBox(panel, -1, "Resources", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
# Disable minimal as we don't have a view for this yet
self.rbResources.EnableItem(1, False)
self.rbResources.SetSelection(self.settings.get('resources'))
rbSizerRow1.Add(self.rbResources, 1, wx.TOP | wx.RIGHT, 5)
self.rbResources.Bind(wx.EVT_RADIOBOX, self.OnResourcesChange)
self.rbResistances = wx.RadioBox(panel, -1, "Resistances", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
# Disable minimal as we don't have a view for this yet
self.rbResistances.EnableItem(1, False)
self.rbResistances.SetSelection(self.settings.get('resistances'))
rbSizerRow1.Add(self.rbResistances, 1, wx.ALL, 5)
self.rbResistances.Bind(wx.EVT_RADIOBOX, self.OnResistancesChange)
self.rbRecharge = wx.RadioBox(panel, -1, "Shield/Armor Tank", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
# Disable minimal as we don't have a view for this yet
self.rbRecharge.EnableItem(1, False)
self.rbRecharge.SetSelection(self.settings.get('recharge'))
rbSizerRow1.Add(self.rbRecharge, 1, wx.ALL, 5)
self.rbRecharge.Bind(wx.EVT_RADIOBOX, self.OnRechargeChange)
mainSizer.Add(rbSizerRow1, 1, wx.ALL | wx.EXPAND, 0)
# Row 2
rbSizerRow2 = wx.BoxSizer(wx.HORIZONTAL)
self.rbFirepower = wx.RadioBox(panel, -1, "Firepower", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
# Disable minimal as we don't have a view for this yet
self.rbFirepower.EnableItem(1, False)
self.rbFirepower.SetSelection(self.settings.get('firepower'))
rbSizerRow2.Add(self.rbFirepower, 1, wx.TOP | wx.RIGHT, 5)
self.rbFirepower.Bind(wx.EVT_RADIOBOX, self.OnFirepowerChange)
self.rbCapacitor = wx.RadioBox(panel, -1, "Capacitor", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
# Disable minimal as we don't have a view for this yet
self.rbCapacitor.EnableItem(1, False)
self.rbCapacitor.SetSelection(self.settings.get('capacitor'))
rbSizerRow2.Add(self.rbCapacitor, 1, wx.ALL, 5)
self.rbCapacitor.Bind(wx.EVT_RADIOBOX, self.OnCapacitorChange)
self.rbMisc = wx.RadioBox(panel, -1, "Misc", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
# Disable full as we don't have a view for this yet
self.rbMisc.EnableItem(2, False)
self.rbMisc.SetSelection(self.settings.get('targetingMisc'))
rbSizerRow2.Add(self.rbMisc, 1, wx.ALL, 5)
self.rbMisc.Bind(wx.EVT_RADIOBOX, self.OnTargetingMiscChange)
mainSizer.Add(rbSizerRow2, 1, wx.ALL | wx.EXPAND, 0)
# Row 3
rbSizerRow3 = wx.BoxSizer(wx.HORIZONTAL)
self.rbPrice = wx.RadioBox(panel, -1, "Price", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
# Disable minimal as we don't have a view for this yet
self.rbPrice.EnableItem(1, False)
self.rbPrice.SetSelection(self.settings.get('price'))
rbSizerRow3.Add(self.rbPrice, 1, wx.TOP | wx.RIGHT, 5)
self.rbPrice.Bind(wx.EVT_RADIOBOX, self.OnPriceChange)
self.rbOutgoing = wx.RadioBox(panel, -1, "Remote Reps", wx.DefaultPosition, wx.DefaultSize, ['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
self.rbOutgoing.SetSelection(self.settings.get('outgoing'))
rbSizerRow3.Add(self.rbOutgoing, 1, wx.TOP | wx.RIGHT, 5)
self.rbOutgoing.Bind(wx.EVT_RADIOBOX, self.OnOutgoingChange)
# We don't have views for these.....yet
'''
self.rbMining = wx.RadioBox(panel, -1, "Mining", wx.DefaultPosition, wx.DefaultSize,
['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
self.rbMining.SetSelection(self.settings.get('miningyield'))
rbSizerRow3.Add(self.rbMining, 1, wx.ALL, 5)
self.rbMining.Bind(wx.EVT_RADIOBOX, self.OnMiningYieldChange)
self.rbDrones = wx.RadioBox(panel, -1, "Drones", wx.DefaultPosition, wx.DefaultSize,
['None', 'Minimal', 'Full'], 1, wx.RA_SPECIFY_COLS)
self.rbDrones.SetSelection(self.settings.get('drones'))
rbSizerRow3.Add(self.rbDrones, 1, wx.ALL, 5)
self.rbDrones.Bind(wx.EVT_RADIOBOX, self.OnDroneChange)
'''
mainSizer.Add(rbSizerRow3, 1, wx.ALL | wx.EXPAND, 0)
panel.SetSizer(mainSizer)
panel.Layout()
def OnResourcesChange(self, event):
self.settings.set('resources', event.GetInt())
def OnResistancesChange(self, event):
self.settings.set('resistances', event.GetInt())
def OnRechargeChange(self, event):
self.settings.set('recharge', event.GetInt())
def OnFirepowerChange(self, event):
self.settings.set('firepower', event.GetInt())
def OnCapacitorChange(self, event):
self.settings.set('capacitor', event.GetInt())
def OnTargetingMiscChange(self, event):
self.settings.set('targetingMisc', event.GetInt())
def OnPriceChange(self, event):
self.settings.set('price', event.GetInt())
def OnOutgoingChange(self, event):
self.settings.set('outgoing', event.GetInt())
def OnMiningYieldChange(self, event):
self.settings.set('miningyield', event.GetInt())
def OnDroneChange(self, event):
self.settings.set('drones', event.GetInt())
def getImage(self):
return BitmapLoader.getBitmap("settings_stats", "gui")
PFStatViewPref.register()

View File

@@ -44,7 +44,7 @@ class PFUpdatePref(PreferenceView):
self.versionSizer = wx.BoxSizer(wx.VERTICAL)
self.versionTitle = wx.StaticText(panel, wx.ID_ANY, "Suppressing {0} Notifications".format(
self.UpdateSettings.get('version')), wx.DefaultPosition, wx.DefaultSize, 0)
self.UpdateSettings.get('version')), wx.DefaultPosition, wx.DefaultSize, 0)
self.versionTitle.Wrap(-1)
self.versionTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString))

View File

@@ -1,3 +1,11 @@
__all__ = ["resourcesViewFull", "resistancesViewFull",
"rechargeViewFull", "firepowerViewFull", "capacitorViewFull",
"targetingMiscViewFull", "priceViewFull", "miningyieldViewFull"]
__all__ = [
"resourcesViewFull",
"resistancesViewFull",
"rechargeViewFull",
"firepowerViewFull",
"capacitorViewFull",
"outgoingViewFull",
"outgoingViewMinimal",
"targetingMiscViewMinimal",
"priceViewFull",
]

View File

@@ -0,0 +1,106 @@
# ===============================================================================
# Copyright (C) 2014 Alexandros Kosiaris
#
# 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 <http://www.gnu.org/licenses/>.
# ===============================================================================
# noinspection PyPackageRequirements
import wx
from gui.statsView import StatsView
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
class OutgoingViewFull(StatsView):
name = "outgoingViewFull"
def __init__(self, parent):
StatsView.__init__(self)
self.parent = parent
self._cachedValues = []
def getHeaderText(self, fit):
return "Remote Reps"
def getTextExtentW(self, text):
width, height = self.parent.GetTextExtent(text)
return width
def populatePanel(self, contentPanel, headerPanel):
contentSizer = contentPanel.GetSizer()
parent = self.panel = contentPanel
self.headerPanel = headerPanel
sizerOutgoing = wx.GridSizer(1, 4)
contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0)
counter = 0
rr_list = [
("RemoteCapacitor", "Capacitor:", "capacitorInfo", "Capacitor GJ/s per second transferred remotely."),
("RemoteShield", "Shield:", "shieldActive", "Shield hitpoints per second repaired remotely."),
("RemoteArmor", "Armor:", "armorActive", "Armor hitpoints per second repaired remotely."),
("RemoteHull", "Hull:", "hullActive", "Hull hitpoints per second repaired remotely."),
]
for outgoingType, label, image, tooltip in rr_list:
baseBox = wx.BoxSizer(wx.VERTICAL)
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")
else:
lbl = wx.StaticText(parent, wx.ID_ANY, u"0 HP/s")
lbl.SetToolTip(wx.ToolTip(tooltip))
setattr(self, "label%s" % outgoingType, lbl)
baseBox.Add(lbl, 0, wx.ALIGN_CENTER)
self._cachedValues.append(0)
counter += 1
sizerOutgoing.Add(baseBox, 1, wx.ALIGN_LEFT)
def refreshPanel(self, fit):
# 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),
]
counter = 0
for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats:
label = getattr(self, labelName)
value = value() if fit is not None else 0
value = value if value is not None else 0
if self._cachedValues[counter] != value:
valueStr = formatAmount(value, prec, lowest, highest)
label.SetLabel(valueFormat % valueStr)
tipStr = valueFormat % valueStr if altFormat is None else altFormat % value
label.SetToolTip(wx.ToolTip(tipStr))
self._cachedValues[counter] = value
counter += 1
self.panel.Layout()
self.headerPanel.Layout()
OutgoingViewFull.register()

View File

@@ -0,0 +1,106 @@
# ===============================================================================
# Copyright (C) 2014 Alexandros Kosiaris
#
# 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 <http://www.gnu.org/licenses/>.
# ===============================================================================
# noinspection PyPackageRequirements
import wx
from gui.statsView import StatsView
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
class OutgoingViewMinimal(StatsView):
name = "outgoingViewMinimal"
def __init__(self, parent):
StatsView.__init__(self)
self.parent = parent
self._cachedValues = []
def getHeaderText(self, fit):
return "Remote Reps"
def getTextExtentW(self, text):
width, height = self.parent.GetTextExtent(text)
return width
def populatePanel(self, contentPanel, headerPanel):
contentSizer = contentPanel.GetSizer()
parent = self.panel = contentPanel
self.headerPanel = headerPanel
sizerOutgoing = wx.GridSizer(1, 4)
contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0)
counter = 0
rr_list = [
("RemoteCapacitor", "Capacitor:", "capacitorInfo", "Capacitor GJ/s per second transferred remotely."),
("RemoteShield", "Shield:", "shieldActive", "Shield hitpoints per second repaired remotely."),
("RemoteArmor", "Armor:", "armorActive", "Armor hitpoints per second repaired remotely."),
("RemoteHull", "Hull:", "hullActive", "Hull hitpoints per second repaired remotely."),
]
for outgoingType, label, image, tooltip in rr_list:
baseBox = wx.BoxSizer(wx.VERTICAL)
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")
else:
lbl = wx.StaticText(parent, wx.ID_ANY, u"0 HP/s")
lbl.SetToolTip(wx.ToolTip(tooltip))
setattr(self, "label%s" % outgoingType, lbl)
baseBox.Add(lbl, 0, wx.ALIGN_CENTER)
self._cachedValues.append(0)
counter += 1
sizerOutgoing.Add(baseBox, 1, wx.ALIGN_LEFT)
def refreshPanel(self, fit):
# 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),
]
counter = 0
for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats:
label = getattr(self, labelName)
value = value() if fit is not None else 0
value = value if value is not None else 0
if self._cachedValues[counter] != value:
valueStr = formatAmount(value, prec, lowest, highest)
label.SetLabel(valueFormat % valueStr)
tipStr = valueFormat % valueStr if altFormat is None else altFormat % value
label.SetToolTip(wx.ToolTip(tipStr))
self._cachedValues[counter] = value
counter += 1
self.panel.Layout()
self.headerPanel.Layout()
OutgoingViewMinimal.register()

View File

@@ -29,7 +29,7 @@ except ImportError:
class TargetingMiscViewFull(StatsView):
name = "targetingmiscViewFull"
name = "targetingMiscViewFull"
def __init__(self, parent):
StatsView.__init__(self)

View File

@@ -0,0 +1,256 @@
# =============================================================================
# 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 <http://www.gnu.org/licenses/>.
# =============================================================================
# noinspection PyPackageRequirements
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
class TargetingMiscViewMinimal(StatsView):
name = "targetingMiscViewMinimal"
def __init__(self, parent):
StatsView.__init__(self)
self.parent = parent
self._cachedValues = []
def getHeaderText(self, fit):
return "Targeting && Misc"
def getTextExtentW(self, text):
width, height = self.parent.GetTextExtent(text)
return width
def populatePanel(self, contentPanel, headerPanel):
contentSizer = contentPanel.GetSizer()
self.panel = contentPanel
self.headerPanel = headerPanel
gridTargetingMisc = wx.FlexGridSizer(1, 3)
contentSizer.Add(gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0)
gridTargetingMisc.AddGrowableCol(0)
gridTargetingMisc.AddGrowableCol(2)
# Targeting
gridTargeting = wx.FlexGridSizer(5, 2)
gridTargeting.AddGrowableCol(1)
gridTargetingMisc.Add(gridTargeting, 0, wx.ALIGN_LEFT | wx.ALL, 5)
labels = (("Targets", "Targets", ""),
("Range", "Range", "km"),
("Scan res.", "ScanRes", "mm"),
("Sensor str.", "SensorStr", ""),
("Drone range", "CtrlRange", "km"))
for header, labelShort, unit in labels:
gridTargeting.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT)
box = wx.BoxSizer(wx.HORIZONTAL)
gridTargeting.Add(box, 0, wx.ALIGN_LEFT)
lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" % unit)
setattr(self, "label%s" % labelShort, lbl)
box.Add(lbl, 0, wx.ALIGN_LEFT)
self._cachedValues.append({"main": 0})
# Misc
gridTargetingMisc.Add(wx.StaticLine(contentPanel, wx.ID_ANY, style=wx.VERTICAL), 0, wx.EXPAND, 3)
gridMisc = wx.FlexGridSizer(5, 2)
gridMisc.AddGrowableCol(1)
gridTargetingMisc.Add(gridMisc, 0, wx.ALIGN_LEFT | wx.ALL, 5)
labels = (("Speed", "Speed", "m/s"),
("Align time", "AlignTime", "s"),
("Signature", "SigRadius", "m"),
("Warp Speed", "WarpSpeed", "AU/s"),
("Cargo", "Cargo", u"m\u00B3"))
for header, labelShort, unit in labels:
gridMisc.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT)
box = wx.BoxSizer(wx.HORIZONTAL)
gridMisc.Add(box, 0, wx.ALIGN_LEFT)
lbl = wx.StaticText(contentPanel, wx.ID_ANY, "0 %s" % unit)
setattr(self, "labelFull%s" % labelShort, lbl)
box.Add(lbl, 0, wx.ALIGN_LEFT)
self._cachedValues.append({"main": 0})
def refreshPanel(self, fit):
# If we did anything interesting, we'd update our labels to reflect the new fit's stats here
cargoNamesOrder = OrderedDict((
("fleetHangarCapacity", "Fleet hangar"),
("shipMaintenanceBayCapacity", "Maintenance bay"),
("specialAmmoHoldCapacity", "Ammo hold"),
("specialFuelBayCapacity", "Fuel bay"),
("specialShipHoldCapacity", "Ship hold"),
("specialSmallShipHoldCapacity", "Small ship hold"),
("specialMediumShipHoldCapacity", "Medium ship hold"),
("specialLargeShipHoldCapacity", "Large ship hold"),
("specialIndustrialShipHoldCapacity", "Industrial ship hold"),
("specialOreHoldCapacity", "Ore hold"),
("specialMineralHoldCapacity", "Mineral hold"),
("specialMaterialBayCapacity", "Material bay"),
("specialGasHoldCapacity", "Gas hold"),
("specialSalvageHoldCapacity", "Salvage hold"),
("specialCommandCenterHoldCapacity", "Command center hold"),
("specialPlanetaryCommoditiesHoldCapacity", "Planetary goods hold"),
("specialQuafeHoldCapacity", "Quafe hold")
))
cargoValues = {
"main": lambda: fit.ship.getModifiedItemAttr("capacity"),
"fleetHangarCapacity": lambda: fit.ship.getModifiedItemAttr("fleetHangarCapacity"),
"shipMaintenanceBayCapacity": lambda: fit.ship.getModifiedItemAttr("shipMaintenanceBayCapacity"),
"specialAmmoHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialAmmoHoldCapacity"),
"specialFuelBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialFuelBayCapacity"),
"specialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialShipHoldCapacity"),
"specialSmallShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSmallShipHoldCapacity"),
"specialMediumShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMediumShipHoldCapacity"),
"specialLargeShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialLargeShipHoldCapacity"),
"specialIndustrialShipHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialIndustrialShipHoldCapacity"),
"specialOreHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialOreHoldCapacity"),
"specialMineralHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMineralHoldCapacity"),
"specialMaterialBayCapacity": lambda: fit.ship.getModifiedItemAttr("specialMaterialBayCapacity"),
"specialGasHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialGasHoldCapacity"),
"specialSalvageHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSalvageHoldCapacity"),
"specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialCommandCenterHoldCapacity"),
"specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialPlanetaryCommoditiesHoldCapacity"),
"specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity")
}
stats = (("labelTargets", {"main": lambda: fit.maxTargets}, 3, 0, 0, ""),
("labelRange", {"main": lambda: fit.maxTargetRange / 1000}, 3, 0, 0, "km"),
("labelScanRes", {"main": lambda: fit.ship.getModifiedItemAttr("scanResolution")}, 3, 0, 0, "mm"),
("labelSensorStr", {"main": lambda: fit.scanStrength}, 3, 0, 0, ""),
("labelCtrlRange", {"main": lambda: fit.extraAttributes["droneControlRange"] / 1000}, 3, 0, 0, "km"),
("labelFullSpeed", {"main": lambda: fit.maxSpeed}, 3, 0, 0, "m/s"),
("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"))
counter = 0
RADII = [("Pod", 25), ("Interceptor", 33), ("Frigate", 38),
("Destroyer", 83), ("Cruiser", 130),
("Battlecruiser", 265), ("Battleship", 420),
("Carrier", 3000)]
for labelName, valueDict, prec, lowest, highest, unit in stats:
label = getattr(self, labelName)
newValues = {}
for valueAlias, value in 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))
if labelName == "labelFullCargo":
# Get sum of all cargoholds except for maintenance bay
additionalCargo = sum(otherValues.values())
if additionalCargo > 0:
label.SetLabel("%s+%s %s" % (formatAmount(mainValue, prec, lowest, highest),
formatAmount(additionalCargo, prec, lowest, highest),
unit))
else:
label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit))
else:
label.SetLabel("%s %s" % (formatAmount(mainValue, prec, lowest, highest), unit))
# Tooltip stuff
if fit:
if labelName == "labelScanRes":
lockTime = "%s\n" % "Lock Times".center(30)
for size, radius in RADII:
left = "%.1fs" % fit.calculateLockTime(radius)
right = "%s [%d]" % (size, radius)
lockTime += "%5s\t%s\n" % (left, right)
label.SetToolTip(wx.ToolTip(lockTime))
elif labelName == "labelFullWarpSpeed":
label.SetToolTip(wx.ToolTip("Max Warp Distance: %.1f AU" % fit.maxWarpDistance))
elif labelName == "labelSensorStr":
if fit.jamChance > 0:
label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance)))
else:
label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType))
elif labelName == "labelFullAlignTime":
alignTime = "Align:\t%.3fs" % mainValue
mass = 'Mass:\t{:,.0f}kg'.format(fit.ship.getModifiedItemAttr("mass"))
agility = "Agility:\t%.3fx" % (fit.ship.getModifiedItemAttr("agility") or 0)
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():
if newValues[attrName] > 0:
tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName]))
label.SetToolTip(wx.ToolTip(u"\n".join(tipLines)))
else:
label.SetToolTip(wx.ToolTip("%.1f" % mainValue))
else:
label.SetToolTip(wx.ToolTip(""))
self._cachedValues[counter] = newValues
elif labelName == "labelFullWarpSpeed":
if fit:
label.SetToolTip(wx.ToolTip("Max Warp Distance: %.1f AU" % fit.maxWarpDistance))
else:
label.SetToolTip(wx.ToolTip(""))
elif labelName == "labelSensorStr":
if fit:
if fit.jamChance > 0:
label.SetToolTip(wx.ToolTip("Type: %s\n%.1f%% Chance of Jam" % (fit.scanType, fit.jamChance)))
else:
label.SetToolTip(wx.ToolTip("Type: %s" % fit.scanType))
else:
label.SetToolTip(wx.ToolTip(""))
elif labelName == "labelFullCargo":
if fit:
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():
if cachedCargo[attrName] > 0:
tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName]))
label.SetToolTip(wx.ToolTip(u"\n".join(tipLines)))
else:
label.SetToolTip(wx.ToolTip(""))
# forces update of probe size, since this stat is used by both sig radius and sensor str
if labelName == "labelFullSigRadius":
print "labelName"
if fit:
label.SetToolTip(wx.ToolTip("Probe Size: %.3f" % (fit.probeSize or 0)))
else:
label.SetToolTip(wx.ToolTip(""))
counter += 1
self.panel.Layout()
self.headerPanel.Layout()
TargetingMiscViewMinimal.register()

View File

@@ -33,7 +33,7 @@ from gui.builtinViewColumns.state import State
from gui.bitmapLoader import BitmapLoader
import gui.builtinViews.emptyView
from gui.utils.exportHtml import exportHtml
from logging import getLogger
from logbook import Logger
from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED
from service.fit import Fit
@@ -41,7 +41,7 @@ from service.market import Market
import gui.globalEvents as GE
logger = getLogger(__name__)
pyfalog = Logger(__name__)
# Tab spawning handler
@@ -55,14 +55,16 @@ class FitSpawner(gui.multiSwitch.TabSpawner):
def fitSelected(self, event):
count = -1
for index, page in enumerate(self.multiSwitch.pages):
try:
if page.activeFitID == event.fitID:
count += 1
self.multiSwitch.SetSelection(index)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=event.fitID))
break
except:
pass
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:
count += 1
self.multiSwitch.SetSelection(index)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=event.fitID))
break
except Exception as e:
pyfalog.critical("Caught exception in fitSelected")
pyfalog.critical(e)
if count < 0:
startup = getattr(event, "startup", False) # see OpenFitsThread in gui.mainFrame
sFit = Fit.getInstance()
@@ -278,6 +280,7 @@ class FittingView(d.Display):
sFit.refreshFit(self.getActiveFit())
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID))
except wx._core.PyDeadObjectError:
pyfalog.warning("Caught dead object")
pass
event.Skip()
@@ -406,7 +409,7 @@ class FittingView(d.Display):
if mod1.slot != mod2.slot:
return
if getattr(mod2, "modPosition"):
if getattr(mod2, "modPosition") is not None:
if clone and mod2.isEmpty:
sFit.cloneModule(self.mainFrame.getActiveFit(), srcIdx, mod2.modPosition)
else:
@@ -414,7 +417,7 @@ class FittingView(d.Display):
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
else:
logger.error("Missing module position for: %s", str(getattr(mod2, "ID", "Unknown")))
pyfalog.error("Missing module position for: {0}", str(getattr(mod2, "ID", "Unknown")))
def generateMods(self):
"""
@@ -483,7 +486,7 @@ class FittingView(d.Display):
self.Show(self.activeFitID is not None and self.activeFitID == event.fitID)
except wx._core.PyDeadObjectError:
pass
pyfalog.warning("Caught dead object")
finally:
event.Skip()
@@ -636,15 +639,17 @@ class FittingView(d.Display):
if 'wxMac' in wx.PlatformInfo:
try:
self.MakeSnapshot()
except:
pass
except Exception as e:
pyfalog.critical("Failed to make snapshot")
pyfalog.critical(e)
def OnShow(self, event):
if event.GetShow():
try:
self.MakeSnapshot()
except:
pass
except Exception as e:
pyfalog.critical("Failed to make snapshot")
pyfalog.critical(e)
event.Skip()
def Snapshot(self):
@@ -669,8 +674,9 @@ class FittingView(d.Display):
sFit = Fit.getInstance()
try:
fit = sFit.getFit(self.activeFitID)
except:
return
except Exception as e:
pyfalog.critical("Failed to get fit")
pyfalog.critical(e)
if fit is None:
return

View File

@@ -33,6 +33,8 @@ from service.fit import Fit
from service.character import Character
from service.network import AuthenticationError, TimeoutError
from service.market import Market
from logbook import Logger
pyfalog = Logger(__name__)
class CharacterTextValidor(BaseValidator):
@@ -55,6 +57,7 @@ class CharacterTextValidor(BaseValidator):
return True
except ValueError, e:
pyfalog.error(e)
wx.MessageBox(u"{}".format(e), "Error")
textCtrl.SetFocus()
return False
@@ -628,11 +631,16 @@ 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:
self.stStatus.SetLabel("Authentication failure. Please check keyID and vCode combination.")
except TimeoutError:
self.stStatus.SetLabel("Request timed out. Please check network connectivity and/or proxy settings.")
except AuthenticationError, e:
msg = "Authentication failure. Please check keyID and vCode combination."
pyfalog.info(msg)
self.stStatus.SetLabel(msg)
except TimeoutError, e:
msg = "Request timed out. Please check network connectivity and/or proxy settings."
pyfalog.info(msg)
self.stStatus.SetLabel(msg)
except Exception, e:
pyfalog.error(e)
self.stStatus.SetLabel("Error:\n%s" % e.message)
else:
self.charChoice.Clear()
@@ -655,6 +663,7 @@ class APIView(wx.Panel):
sChar.apiFetch(activeChar.ID, charName)
self.stStatus.SetLabel("Successfully fetched %s\'s skills from EVE API." % charName)
except Exception, e:
pyfalog.error("Unable to retrieve {0}\'s skills. Error message:\n{1}", charName, e)
self.stStatus.SetLabel("Unable to retrieve %s\'s skills. Error message:\n%s" % (charName, e))

View File

@@ -24,6 +24,8 @@ import gui.globalEvents as GE
import gui.mainFrame
from service.character import Character
from service.fit import Fit
from logbook import Logger
pyfalog = Logger(__name__)
class CharacterSelection(wx.Panel):
@@ -112,9 +114,10 @@ class CharacterSelection(wx.Panel):
if charName:
try:
sChar.apiFetch(self.getActiveCharacter(), charName)
except:
except Exception as e:
# can we do a popup, notifying user of API error?
pass
pyfalog.error("API fetch error")
pyfalog.error(e)
self.refreshCharacterList()
def charChanged(self, event):

View File

@@ -25,9 +25,11 @@ import gui.utils.colorUtils as colorUtils
import gui.utils.drawUtils as drawUtils
import gui.utils.fonts as fonts
from gui.bitmapLoader import BitmapLoader
from logbook import Logger
from service.fit import Fit
pyfalog = Logger(__name__)
_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent()
_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent()
_PageAdding, EVT_NOTEBOOK_PAGE_ADDING = wx.lib.newevent.NewEvent()
@@ -1093,8 +1095,9 @@ class PFTabsContainer(wx.Panel):
self.previewTab = tab
self.previewTimer.Start(500, True)
break
except:
pass
except Exception as e:
pyfalog.critical("Exception caught in CheckTabPreview.")
pyfalog.critical(e)
def CheckAddHighlighted(self, x, y):
"""

View File

@@ -19,9 +19,9 @@
# noinspection PyPackageRequirements
import wx
import logging
from logbook import Logger
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class ContextMenu(object):
@@ -121,7 +121,7 @@ class ContextMenu(object):
debug_end = len(cls._ids)
if debug_end - debug_start:
logger.debug("%d new IDs created for this menu" % (debug_end - debug_start))
pyfalog.debug("{0} new IDs created for this menu", (debug_end - debug_start))
return rootMenu if empty is False else None

View File

@@ -14,6 +14,9 @@ from eos.db import getItem
from gui.display import Display
import gui.globalEvents as GE
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, CrestModes
@@ -147,7 +150,9 @@ class CrestFittings(wx.Frame):
self.fitTree.populateSkillTree(fittings)
del waitDialog
except requests.exceptions.ConnectionError:
self.statusbar.SetStatusText("Connection error, please check your internet connection")
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
def importFitting(self, event):
selection = self.fitView.fitSelection
@@ -173,7 +178,9 @@ class CrestFittings(wx.Frame):
try:
sCrest.delFitting(self.getActiveCharacter(), data['fittingID'])
except requests.exceptions.ConnectionError:
self.statusbar.SetStatusText("Connection error, please check your internet connection")
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
class ExportToEve(wx.Frame):
@@ -281,9 +288,12 @@ class ExportToEve(wx.Frame):
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:
self.statusbar.SetStatusText("Connection error, please check your internet connection", 1)
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
class CrestMgmt(wx.Dialog):
@@ -405,8 +415,9 @@ class FittingsTreeView(wx.Panel):
cargo = Cargo(getItem(item['type']['id']))
cargo.amount = item['quantity']
list.append(cargo)
except:
pass
except Exception as e:
pyfalog.critical("Exception caught in displayFit")
pyfalog.critical(e)
self.parent.fitView.fitSelection = selection
self.parent.fitView.update(list)

View File

@@ -18,7 +18,7 @@
# =============================================================================
import os
import logging
from logbook import Logger
# noinspection PyPackageRequirements
import wx
@@ -30,6 +30,8 @@ import gui.globalEvents as GE
from gui.graph import Graph
from gui.bitmapLoader import BitmapLoader
pyfalog = Logger(__name__)
try:
import matplotlib as mpl
@@ -51,8 +53,7 @@ except ImportError:
graphFrame_enabled = False
mplImported = False
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class GraphFrame(wx.Frame):
@@ -61,11 +62,29 @@ class GraphFrame(wx.Frame):
global graphFrame_enabled
global mplImported
self.mpl_version = int(mpl.__version__[0])
self.Patch = None
self.mpl_version = -1
try:
import matplotlib as mpl
self.mpl_version = int(mpl.__version__[0])
if self.mpl_version >= 2:
mpl.use('wxagg')
mplImported = True
else:
mplImported = False
from matplotlib.patches import Patch
self.Patch = Patch
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
from matplotlib.figure import Figure
graphFrame_enabled = True
except ImportError:
Patch = mpl = Canvas = Figure = None
graphFrame_enabled = False
self.legendFix = False
if not graphFrame_enabled:
logger.info("Problems importing matplotlib; continuing without graphs")
pyfalog.info("Problems importing matplotlib; continuing without graphs")
return
try:
@@ -236,6 +255,7 @@ class GraphFrame(wx.Frame):
self.subplot.plot(x, y)
legend.append(fit.name)
except:
pyfalog.warning("Invalid values in '{0}'", fit.name)
self.SetStatusText("Invalid values in '%s'" % fit.name)
self.canvas.draw()
return

View File

@@ -62,13 +62,13 @@ class ItemStatsDialog(wx.Dialog):
):
wx.Dialog.__init__(
self,
gui.mainFrame.MainFrame.getInstance(),
wx.ID_ANY,
title="Item stats",
pos=pos,
size=size,
style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU
self,
gui.mainFrame.MainFrame.getInstance(),
wx.ID_ANY,
title="Item stats",
pos=pos,
size=size,
style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU
)
empty = getattr(victim, "isEmpty", False)
@@ -197,6 +197,10 @@ class ItemStatsContainer(wx.Panel):
self.affectedby = ItemAffectedBy(self.nbContainer, stuff, item)
self.nbContainer.AddPage(self.affectedby, "Affected by")
if config.debug:
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()
@@ -256,7 +260,7 @@ class ItemDescription(wx.Panel):
# Strip URLs
desc = re.sub("<( *)a(.*?)>(?P<inside>.*?)<( *)/( *)a( *)>", "\g<inside>", desc)
desc = "<body bgcolor='" + bgcolor.GetAsString(wx.C2S_HTML_SYNTAX) + "' text='" + fgcolor.GetAsString(
wx.C2S_HTML_SYNTAX) + "' >" + desc + "</body>"
wx.C2S_HTML_SYNTAX) + "' >" + desc + "</body>"
self.description.SetPage(desc)
@@ -358,13 +362,13 @@ class ItemParams(wx.Panel):
writer = csv.writer(exportFile, delimiter=',')
writer.writerow(
[
"ID",
"Internal Name",
"Friendly Name",
"Modified Value",
"Base Value",
]
[
"ID",
"Internal Name",
"Friendly Name",
"Modified Value",
"Base Value",
]
)
for attribute in self.attrValues:
@@ -395,13 +399,13 @@ class ItemParams(wx.Panel):
attribute_modified_value = self.attrValues[attribute]
writer.writerow(
[
attribute_id,
attribute_name,
attribute_displayname,
attribute_modified_value,
attribute_value,
]
[
attribute_id,
attribute_name,
attribute_displayname,
attribute_modified_value,
attribute_value,
]
)
def PopulateList(self):
@@ -496,17 +500,19 @@ class ItemParams(wx.Panel):
attribute = Attribute.getInstance().getAttributeInfo(value)
return "%s (%d)" % (attribute.name.capitalize(), value)
trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName),
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
"Modifier Percent": (
lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
"Volume": (lambda: value, u"m\u00B3"),
"Sizeclass": (lambda: value, ""),
"Absolute Percent": (lambda: (value * 100), unitName),
"Milliseconds": (lambda: value / 1000.0, unitName),
"typeID": (itemIDCallback, ""),
"groupID": (groupIDCallback, ""),
"attributeID": (attributeIDCallback, "")}
trans = {
"Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName),
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
"Modifier Percent" : (
lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
"Volume" : (lambda: value, u"m\u00B3"),
"Sizeclass" : (lambda: value, ""),
"Absolute Percent" : (lambda: (value * 100), unitName),
"Milliseconds" : (lambda: value / 1000.0, unitName),
"typeID" : (itemIDCallback, ""),
"groupID" : (groupIDCallback, ""),
"attributeID" : (attributeIDCallback, "")
}
override = trans.get(unitDisplayName)
if override is not None:
@@ -689,17 +695,18 @@ class ItemCompare(wx.Panel):
attribute = Attribute.getInstance().getAttributeInfo(value)
return "%s (%d)" % (attribute.name.capitalize(), value)
trans = {"Inverse Absolute Percent": (lambda: (1 - value) * 100, unitName),
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
"Modifier Percent": (
lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
"Volume": (lambda: value, u"m\u00B3"),
"Sizeclass": (lambda: value, ""),
"Absolute Percent": (lambda: (value * 100), unitName),
"Milliseconds": (lambda: value / 1000.0, unitName),
"typeID": (itemIDCallback, ""),
"groupID": (groupIDCallback, ""),
"attributeID": (attributeIDCallback, "")}
trans = {
"Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName),
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
"Modifier Percent" : (lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
"Volume" : (lambda: value, u"m\u00B3"),
"Sizeclass" : (lambda: value, ""),
"Absolute Percent" : (lambda: (value * 100), unitName),
"Milliseconds" : (lambda: value / 1000.0, unitName),
"typeID" : (itemIDCallback, ""),
"groupID" : (groupIDCallback, ""),
"attributeID" : (attributeIDCallback, "")
}
override = trans.get(unitDisplayName)
if override is not None:
@@ -1067,7 +1074,7 @@ class ItemAffectedBy(wx.Panel):
item = afflictor.item
items[attrName].append(
(type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False)))
(type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False)))
# Make sure projected fits are on top
rootOrder = container.keys()
@@ -1295,3 +1302,97 @@ class ItemAffectedBy(wx.Panel):
treeitem = self.affectedBy.AppendItem(child, display, attrIcon)
self.affectedBy.SetPyData(treeitem, saved)
self.treeItems.append(treeitem)
class ItemProperties(wx.Panel):
def __init__(self, parent, stuff, item, context=None):
wx.Panel.__init__(self, parent)
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.paramList = AutoListCtrl(self, wx.ID_ANY,
style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER)
mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0)
self.SetSizer(mainSizer)
self.toggleView = 1
self.stuff = stuff
self.item = item
self.attrInfo = {}
self.attrValues = {}
self._fetchValues()
self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
bSizer = wx.BoxSizer(wx.HORIZONTAL)
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0)
bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT)
self.PopulateList()
def _fetchValues(self):
if self.stuff is None:
self.attrInfo.clear()
self.attrValues.clear()
self.attrInfo.update(self.item.attributes)
self.attrValues.update(self.item.attributes)
elif self.stuff.item == self.item:
self.attrInfo.clear()
self.attrValues.clear()
self.attrInfo.update(self.stuff.item.attributes)
self.attrValues.update(self.stuff.itemModifiedAttributes)
elif self.stuff.charge == self.item:
self.attrInfo.clear()
self.attrValues.clear()
self.attrInfo.update(self.stuff.charge.attributes)
self.attrValues.update(self.stuff.chargeModifiedAttributes)
# When item for stats window no longer exists, don't change anything
else:
return
def PopulateList(self):
self.paramList.InsertColumn(0, "Attribute")
self.paramList.InsertColumn(1, "Current Value")
self.paramList.SetColumnWidth(0, 110)
self.paramList.SetColumnWidth(1, 1500)
self.paramList.setResizeColumn(0)
if self.stuff:
names = dir(self.stuff)
else:
names = dir(self.item)
names = [a for a in names if not (a.startswith('__') and a.endswith('__'))]
idNameMap = {}
idCount = 0
for name in names:
try:
if self.stuff:
attrName = name.title()
value = getattr(self.stuff, name)
else:
attrName = name.title()
value = getattr(self.item, name)
except Exception as e:
# TODO: Add logging to this.
# We couldn't get a property for some reason. Skip it for now.
print(e)
continue
index = self.paramList.InsertStringItem(sys.maxint, attrName)
# index = self.paramList.InsertImageStringItem(sys.maxint, attrName)
idNameMap[idCount] = attrName
self.paramList.SetItemData(index, idCount)
idCount += 1
valueUnit = str(value)
self.paramList.SetStringItem(index, 1, valueUnit)
self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2]))
self.paramList.RefreshRows()
self.totalAttrsLabel.SetLabel("%d attributes. " % idCount)
self.Layout()

View File

@@ -19,7 +19,7 @@
import sys
import os.path
import logging
from logbook import Logger
import sqlalchemy
# noinspection PyPackageRequirements
@@ -94,7 +94,7 @@ except ImportError as e:
print("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message)
disableOverrideEditor = True
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
# dummy panel(no paint no erasebk)
@@ -145,6 +145,7 @@ class MainFrame(wx.Frame):
return cls.__instance if cls.__instance is not None else MainFrame()
def __init__(self, title="pyfa"):
pyfalog.debug("Initialize MainFrame")
self.title = title
wx.Frame.__init__(self, None, wx.ID_ANY, self.title)
@@ -399,7 +400,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <showDamagePatternEditor>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <showDamagePatternEditor>.")
def showImplantSetEditor(self, event):
ImplantSetEditorDlg(self)
@@ -427,7 +428,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <showExportDialog>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <showExportDialog>.")
return
with open(path, "w", encoding="utf-8") as openfile:
@@ -437,7 +438,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <showExportDialog>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <showExportDialog>.")
def showPreferenceDialog(self, event):
dlg = PreferenceDialog(self)
@@ -734,7 +735,7 @@ class MainFrame(wx.Frame):
try:
fits = Port().importFitFromBuffer(clipboard, self.getActiveFit())
except:
logger.error("Attempt to import failed:\n%s", clipboard)
pyfalog.error("Attempt to import failed:\n{0}", clipboard)
else:
self._openAfterImport(fits)
@@ -754,7 +755,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <exportToClipboard>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <exportToClipboard>.")
def exportSkillsNeeded(self, event):
""" Exports skills needed for active fit and active character """
@@ -811,7 +812,7 @@ class MainFrame(wx.Frame):
try:
dlg.Destroy()
except PyDeadObjectError:
logger.error("Tried to destroy an object that doesn't exist in <fileImportDialog>.")
pyfalog.error("Tried to destroy an object that doesn't exist in <fileImportDialog>.")
def backupToXml(self, event):
""" Back up all fits to EVE XML file """

View File

@@ -26,6 +26,9 @@ import gui.graphFrame
import gui.globalEvents as GE
from gui.bitmapLoader 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
@@ -33,6 +36,7 @@ if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION
class MainMenuBar(wx.MenuBar):
def __init__(self, mainFrame):
pyfalog.debug("Initialize MainMenuBar")
self.characterEditorId = wx.NewId()
self.damagePatternEditorId = wx.NewId()
self.targetResistsEditorId = wx.NewId()
@@ -166,6 +170,7 @@ class MainMenuBar(wx.MenuBar):
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
def fitChanged(self, event):
pyfalog.debug("fitChanged triggered")
enable = event.fitID is not None
self.Enable(wx.ID_SAVEAS, enable)
self.Enable(wx.ID_COPY, enable)

View File

@@ -26,6 +26,9 @@ import gui.PFSearchBox as SBox
from gui.cachingImageList import CachingImageList
from gui.contextMenu import ContextMenu
from gui.bitmapLoader import BitmapLoader
from logbook import Logger
pyfalog = Logger(__name__)
ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent()
@@ -56,6 +59,7 @@ class MetaButton(wx.ToggleButton):
class MarketBrowser(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
pyfalog.debug("Initialize marketBrowser")
vbox = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(vbox)
@@ -134,6 +138,7 @@ class SearchBox(SBox.PFSearchBox):
class MarketTree(wx.TreeCtrl):
def __init__(self, parent, marketBrowser):
wx.TreeCtrl.__init__(self, parent, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT)
pyfalog.debug("Initialize marketTree")
self.root = self.AddRoot("root")
self.imageList = CachingImageList(16, 16)
@@ -182,7 +187,9 @@ class MarketTree(wx.TreeCtrl):
iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp))
try:
childId = self.AppendItem(root, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID))
except:
except Exception as e:
pyfalog.debug("Error appending item.")
pyfalog.debug(e)
continue
if sMkt.marketGroupHasTypesCheck(childMktGrp) is False:
self.AppendItem(childId, "dummy")
@@ -226,6 +233,7 @@ class ItemView(Display):
def __init__(self, parent, marketBrowser):
Display.__init__(self, parent)
pyfalog.debug("Initialize ItemView")
marketBrowser.Bind(wx.EVT_TREE_SEL_CHANGED, self.selectionMade)
self.unfilteredStore = set()
@@ -252,6 +260,7 @@ class ItemView(Display):
self.metaMap = self.makeReverseMetaMap()
# Fill up recently used modules set
pyfalog.debug("Fill up recently used modules set")
for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]:
self.recentlyUsedModules.add(self.sMkt.getItem(itemID))

View File

@@ -25,6 +25,9 @@ from wx.lib.intctrl import IntCtrl
from gui.utils.clipboard import toClipboard, fromClipboard
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
from service.damagePattern import DamagePattern, ImportError
from logbook import Logger
pyfalog = Logger(__name__)
class DmgPatternTextValidor(BaseValidator):
@@ -47,6 +50,7 @@ class DmgPatternTextValidor(BaseValidator):
return True
except ValueError as e:
pyfalog.error(e)
wx.MessageBox(u"{}".format(e), "Error")
textCtrl.SetFocus()
return False
@@ -256,9 +260,13 @@ class DmgPatternEditorDlg(wx.Dialog):
sDP.importPatterns(text)
self.stNotice.SetLabel("Patterns successfully imported from clipboard")
except ImportError as e:
pyfalog.error(e)
self.stNotice.SetLabel(str(e))
except Exception:
self.stNotice.SetLabel("Could not import from clipboard: unknown errors")
except Exception as e:
msg = "Could not import from clipboard: unknown errors"
pyfalog.warning(msg)
pyfalog.error(e)
self.stNotice.SetLabel(msg)
finally:
self.entityEditor.refreshEntityList()
else:

View File

@@ -66,12 +66,12 @@ class PreferenceDialog(wx.Dialog):
# Set the height based on a condition. Can all the panels fit in the current height?
# If not, use the .GetBestVirtualSize() to ensure that all content is available.
minHeight = 360
minHeight = 550
bestFit = self.GetBestVirtualSize()
if minHeight > bestFit[1]:
self.SetSizeWH(450, minHeight)
self.SetSizeWH(650, minHeight)
else:
self.SetSizeWH(450, bestFit[1])
self.SetSizeWH(650, bestFit[1])
self.Layout()

View File

@@ -44,5 +44,10 @@ from gui.builtinPreferenceViews import ( # noqa: E402, F401
pyfaNetworkPreferences,
pyfaHTMLExportPreferences,
pyfaCrestPreferences,
pyfaUpdatePreferences
pyfaContextMenuPreferences,
pyfaStatViewPreferences,
pyfaUpdatePreferences,
pyfaEnginePreferences,
pyfaDatabasePreferences,
pyfaLoggingPreferences
)

View File

@@ -1,5 +1,5 @@
import csv
import logging
from logbook import Logger
# noinspection PyPackageRequirements
import wx
@@ -21,7 +21,7 @@ import gui.PFSearchBox as SBox
from gui.marketBrowser import SearchBox
from gui.bitmapLoader import BitmapLoader
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class AttributeEditor(wx.Frame):
@@ -270,7 +270,7 @@ class AttributeGrid(wxpg.PropertyGrid):
self.itemView.updateItems()
logger.debug('%s changed to "%s"' % (p.GetName(), p.GetValueAsString()))
pyfalog.debug('{0} changed to "{1}"', p.GetName(), p.GetValueAsString())
def OnPropGridSelect(self, event):
pass

View File

@@ -23,6 +23,9 @@ from service.targetResists import TargetResists
from gui.bitmapLoader import BitmapLoader
from gui.utils.clipboard import toClipboard, fromClipboard
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
from logbook import Logger
pyfalog = Logger(__name__)
class TargetResistsTextValidor(BaseValidator):
@@ -45,6 +48,7 @@ class TargetResistsTextValidor(BaseValidator):
return True
except ValueError as e:
pyfalog.error(e)
wx.MessageBox(u"{}".format(e), "Error")
textCtrl.SetFocus()
return False
@@ -230,10 +234,14 @@ class ResistsEditorDlg(wx.Dialog):
except ValueError:
editObj.SetForegroundColour(wx.RED)
self.stNotice.SetLabel("Incorrect Formatting (decimals only)")
msg = "Incorrect Formatting (decimals only)"
pyfalog.warning(msg)
self.stNotice.SetLabel(msg)
except AssertionError:
editObj.SetForegroundColour(wx.RED)
self.stNotice.SetLabel("Incorrect Range (must be 0-100)")
msg = "Incorrect Range (must be 0-100)"
pyfalog.warning(msg)
self.stNotice.SetLabel(msg)
finally: # Refresh for color changes to take effect immediately
self.Refresh()
@@ -271,9 +279,13 @@ class ResistsEditorDlg(wx.Dialog):
sTR.importPatterns(text)
self.stNotice.SetLabel("Patterns successfully imported from clipboard")
except ImportError as e:
pyfalog.error(e)
self.stNotice.SetLabel(str(e))
except Exception:
self.stNotice.SetLabel("Could not import from clipboard: unknown errors")
except Exception as e:
msg = "Could not import from clipboard:"
pyfalog.warning(msg)
pyfalog.error(e)
self.stNotice.SetLabel(msg)
finally:
self.entityEditor.refreshEntityList()
else:

View File

@@ -17,7 +17,7 @@
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
# =============================================================================
import logging
from logbook import Logger
# noinspection PyPackageRequirements
import wx
@@ -26,7 +26,7 @@ from gui.builtinViews.implantEditor import BaseImplantEditorView
from gui.utils.clipboard import toClipboard, fromClipboard
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
logger = logging.getLogger(__name__)
pyfalog = Logger(__name__)
class ImplantTextValidor(BaseValidator):
@@ -49,6 +49,7 @@ class ImplantTextValidor(BaseValidator):
return True
except ValueError as e:
pyfalog.error(e)
wx.MessageBox(u"{}".format(e), "Error")
textCtrl.SetFocus()
return False
@@ -198,9 +199,10 @@ class ImplantSetEditorDlg(wx.Dialog):
self.stNotice.SetLabel("Patterns successfully imported from clipboard")
self.showInput(False)
except ImportError as e:
pyfalog.error(e)
self.stNotice.SetLabel(str(e))
except Exception as e:
logging.exception("Unhandled Exception")
pyfalog.error(e)
self.stNotice.SetLabel("Could not import from clipboard: unknown errors")
finally:
self.updateChoices()

View File

@@ -21,6 +21,8 @@ import gui.utils.animEffects as animEffects
from gui.PFListPane import PFListPane
from gui.contextMenu import ContextMenu
from gui.bitmapLoader import BitmapLoader
from logbook import Logger
pyfalog = Logger(__name__)
FitRenamed, EVT_FIT_RENAMED = wx.lib.newevent.NewEvent()
FitSelected, EVT_FIT_SELECTED = wx.lib.newevent.NewEvent()
@@ -684,6 +686,7 @@ class ShipBrowser(wx.Panel):
self.lpane.Freeze()
self.lpane.RemoveAllChildren()
pyfalog.debug("Populate ship category list.")
if len(self.categoryList) == 0:
# set cache of category list
self.categoryList = list(sMkt.getShipRoot())
@@ -1552,6 +1555,10 @@ class FitItem(SFItem.SFBrowserItem):
self.selTimer.Start(100)
self.Bind(wx.EVT_RIGHT_UP, self.OnContextMenu)
self.Bind(wx.EVT_MIDDLE_UP, self.OpenNewTab)
def OpenNewTab(self, evt):
self.selectFit(newTab=True)
def OnToggleBooster(self, event):
sFit = Fit.getInstance()
@@ -1616,6 +1623,9 @@ class FitItem(SFItem.SFBrowserItem):
# menu.AppendSubMenu(boosterMenu, 'Set Booster')
if fit:
newTabItem = menu.Append(wx.ID_ANY, "Open in new tab")
self.Bind(wx.EVT_MENU, self.OpenNewTab, newTabItem)
projectedItem = menu.Append(wx.ID_ANY, "Project onto Active Fit")
self.Bind(wx.EVT_MENU, self.OnProjectToFit, projectedItem)
@@ -1809,8 +1819,11 @@ class FitItem(SFItem.SFBrowserItem):
self.dragWindow.SetPosition(pos)
return
def selectFit(self, event=None):
wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fitID))
def selectFit(self, event=None, newTab=False):
if newTab:
wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fitID, startup=2))
else:
wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fitID))
def RestoreEditButton(self):
self.tcFitName.Show(False)

View File

@@ -21,6 +21,7 @@
import wx
from service.fit import Fit
from service.settings import StatViewSettings
import gui.mainFrame
import gui.builtinStatsViews
import gui.globalEvents as GE
@@ -28,12 +29,44 @@ import gui.globalEvents as GE
from gui.statsView import StatsView
from gui.contextMenu import ContextMenu
from gui.pyfatogglepanel import TogglePanel
from logbook import Logger
pyfalog = Logger(__name__)
class StatsPane(wx.Panel):
DEFAULT_VIEWS = ["resourcesViewFull", "resistancesViewFull", "rechargeViewFull", "firepowerViewFull",
"capacitorViewFull", "targetingmiscViewFull",
"priceViewFull"]
AVAILIBLE_VIEWS = [
"resources",
"resistances",
"recharge",
"firepower",
"outgoing",
"capacitor",
"targetingMisc",
"price",
]
# Don't have these....yet....
'''
"miningyield", "drones"
]
'''
DEFAULT_VIEWS = []
settings = StatViewSettings.getInstance()
for aView in AVAILIBLE_VIEWS:
if settings.get(aView) == 2:
DEFAULT_VIEWS.extend(["%sViewFull" % aView])
pyfalog.debug("Setting full view for: {0}", aView)
elif settings.get(aView) == 1:
DEFAULT_VIEWS.extend(["%sViewMinimal" % aView])
pyfalog.debug("Setting minimal view for: {0}", aView)
elif settings.get(aView) == 0:
pyfalog.debug("Setting disabled view for: {0}", aView)
else:
pyfalog.error("Unknown setting for view: {0}", aView)
def fitChanged(self, event):
sFit = Fit.getInstance()
@@ -66,7 +99,12 @@ class StatsPane(wx.Panel):
contentPanel = tp.GetContentPane()
contentPanel.viewName = viewName
view = StatsView.getView(viewName)(self)
try:
view = StatsView.getView(viewName)(self)
pyfalog.debug("Load view: {0}", viewName)
except KeyError:
pyfalog.error("Attempted to load an invalid view: {0}", viewName)
self.nameViewMap[viewName] = view
self.views.append(view)

View File

@@ -50,6 +50,8 @@ from gui.builtinStatsViews import ( # noqa: E402, F401
miningyieldViewFull,
capacitorViewFull,
rechargeViewFull,
targetingMiscViewFull,
targetingMiscViewMinimal,
priceViewFull,
outgoingViewFull,
outgoingViewMinimal,
)

View File

@@ -6,8 +6,11 @@ from service.settings import HTMLExportSettings
from service.fit import Fit
from service.port import Port
from service.market import Market
from logbook import Logger
from eos.db import getFit
pyfalog = Logger(__name__)
class exportHtml(object):
_instance = None
@@ -175,7 +178,6 @@ class exportHtmlThread(threading.Thread):
count = 0
# todo: logging for export exceptions
for group in categoryList:
# init market group string to give ships something to attach to
HTMLgroup = ''
@@ -200,7 +202,8 @@ class exportHtmlThread(threading.Thread):
HTMLgroup += ' <li><a data-dna="' + dnaFit + '" target="_blank">' + ship.name + ": " + \
fit[1] + '</a></li>\n'
except:
continue
pyfalog.warning("Failed to export line")
pass
finally:
if self.callback:
wx.CallAfter(self.callback, count)
@@ -223,6 +226,7 @@ class exportHtmlThread(threading.Thread):
HTMLship += ' <li><a data-dna="' + dnaFit + '" target="_blank">' + fit[
1] + '</a></li>\n'
except:
pyfalog.warning("Failed to export line")
continue
finally:
if self.callback:
@@ -275,6 +279,7 @@ class exportHtmlThread(threading.Thread):
HTML += '<a class="outOfGameBrowserLink" target="_blank" href="' + dnaUrl + dnaFit + '">' + ship.name + ': ' + \
fit[1] + '</a><br> \n'
except:
pyfalog.error("Failed to export line")
continue
finally:
if self.callback:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
imgs/gui/settings_log.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
imgs/gui/settings_menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
imgs/gui/settings_stats.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

142
pyfa.py
View File

@@ -19,11 +19,17 @@
# ==============================================================================
import sys
import os
import os.path
import re
import config
from optparse import OptionParser, BadOptionError, AmbiguousOptionError
from logbook import TimedRotatingFileHandler, Logger, StreamHandler, NestedSetup, FingersCrossedHandler, NullHandler, \
CRITICAL, ERROR, WARNING, DEBUG, INFO
pyfalog = Logger(__name__)
class PassThroughOptionParser(OptionParser):
"""
@@ -36,9 +42,30 @@ class PassThroughOptionParser(OptionParser):
try:
OptionParser._process_args(self, largs, rargs, values)
except (BadOptionError, AmbiguousOptionError) as e:
pyfalog.error("Bad startup option passed.")
largs.append(e.opt_str)
class LoggerWriter:
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)
# Parse command line options
usage = "usage: %prog [--root]"
parser = PassThroughOptionParser(usage=usage)
@@ -47,9 +74,23 @@ parser.add_option("-w", "--wx28", action="store_true", dest="force28", help="For
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)
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 not hasattr(sys, 'frozen'):
if sys.version_info < (2, 6) or sys.version_info > (3, 0):
@@ -115,7 +156,6 @@ if __name__ == "__main__":
config.debug = options.debug
import logging
# Import everything
# noinspection PyPackageRequirements
import wx
@@ -129,7 +169,75 @@ if __name__ == "__main__":
config.defPaths(options.savepath)
# Basic logging initialization
logging.basicConfig()
# 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"
savePath_Destination = 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(
savePath_Destination,
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(
savePath_Destination,
level=0,
backup_count=3,
bubble=False,
date_format='%Y-%m-%d',
),
action_level=ERROR,
buffer_size=1000,
# pull_information=True,
# reset=False,
)
])
except:
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
)
])
import eos.db
# noinspection PyUnresolvedReferences
@@ -152,8 +260,30 @@ if __name__ == "__main__":
pyfa.MainLoop()
sys.exit()
from gui.mainFrame import MainFrame
with logging_setup.threadbound():
# Don't redirect if frozen
if not hasattr(sys, 'frozen'):
# Output all stdout (print) messages as warnings
try:
sys.stdout = LoggerWriter(pyfalog.warning)
except ValueError, Exception:
pyfalog.critical("Cannot access log file. Continuing without writing stdout to log.")
pyfa = wx.App(False)
MainFrame(options.title)
pyfa.MainLoop()
if not options.debug:
# Output all stderr (stacktrace) messages as critical
try:
sys.stderr = LoggerWriter(pyfalog.critical)
except ValueError, Exception:
pyfalog.critical("Cannot access log file. Continuing without writing stderr to log.")
pyfalog.info("Starting Pyfa")
pyfalog.info("Running in logging mode: {0}", logging_mode)
if hasattr(sys, 'frozen') and options.debug:
pyfalog.critical("Running in frozen mode with debug turned on. Forcing all output to be written to log.")
from gui.mainFrame import MainFrame
pyfa = wx.App(False)
MainFrame(options.title)
pyfa.MainLoop()

83
pyfa.spec Normal file
View File

@@ -0,0 +1,83 @@
# -*- 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
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 eos.effects and add all effects so we can import them properly
for root, folders, files in os.walk("eos/effects"):
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=['C:\\Users\\Ebag333\\Documents\\GitHub\\Ebag333\\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=False,
strip=False,
upx=True,
name='pyfa',
icon='dist_assets/win/pyfa.ico',
onefile=False,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
onefile=False,
name='pyfa',
icon='dist_assets/win/pyfa.ico',
)

View File

@@ -1,3 +1,4 @@
logbook
matplotlib
PyYAML
python-dateutil

View File

@@ -0,0 +1,9 @@
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

View File

@@ -0,0 +1,8 @@
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

View File

@@ -0,0 +1,10 @@
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
tornado

Some files were not shown because too many files have changed in this diff Show More