Merge branch 'charImplants' into singularity
# Conflicts: # eos/db/saveddata/fit.py # gui/builtinContextMenus/itemStats.py
This commit is contained in:
@@ -4,6 +4,9 @@ import time
|
||||
import re
|
||||
import os
|
||||
import migrations
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def getVersion(db):
|
||||
cursor = db.execute('PRAGMA user_version')
|
||||
@@ -30,10 +33,9 @@ def update(saveddata_engine):
|
||||
shutil.copyfile(config.saveDB, toFile)
|
||||
|
||||
for version in xrange(dbVersion, appVersion):
|
||||
|
||||
func = migrations.updates[version+1]
|
||||
if func:
|
||||
print "applying update",version+1
|
||||
logger.info("Applying database update: %d", version+1)
|
||||
func(saveddata_engine)
|
||||
|
||||
# when all is said and done, set version to current
|
||||
|
||||
15
eos/db/migrations/upgrade13.py
Normal file
15
eos/db/migrations/upgrade13.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
Migration 13
|
||||
|
||||
- Alters fits table to introduce implant location attribute
|
||||
"""
|
||||
|
||||
import sqlalchemy
|
||||
|
||||
def upgrade(saveddata_engine):
|
||||
# Update fits schema to include implant location attribute
|
||||
try:
|
||||
saveddata_engine.execute("SELECT implantLocation FROM fits LIMIT 1")
|
||||
except sqlalchemy.exc.DatabaseError:
|
||||
saveddata_engine.execute("ALTER TABLE fits ADD COLUMN implantLocation INTEGER;")
|
||||
saveddata_engine.execute("UPDATE fits SET implantLocation = 0")
|
||||
@@ -13,6 +13,7 @@ __all__ = [
|
||||
"miscData",
|
||||
"targetResists",
|
||||
"override",
|
||||
"crest"
|
||||
"crest",
|
||||
"implantSet"
|
||||
]
|
||||
|
||||
|
||||
@@ -36,9 +36,23 @@ characters_table = Table("characters", saveddata_meta,
|
||||
Column("ownerID", ForeignKey("users.ID"), nullable = True))
|
||||
|
||||
mapper(Character, characters_table,
|
||||
properties = {"_Character__owner" : relation(User, backref = "characters"),
|
||||
"_Character__skills" : relation(Skill, backref="character", cascade = "all,delete-orphan"),
|
||||
"_Character__implants" : relation(Implant, collection_class = HandledImplantBoosterList, cascade='all,delete-orphan', single_parent=True,
|
||||
primaryjoin = charImplants_table.c.charID == characters_table.c.ID,
|
||||
secondaryjoin = charImplants_table.c.implantID == Implant.ID,
|
||||
secondary = charImplants_table),})
|
||||
properties = {
|
||||
"savedName": characters_table.c.name,
|
||||
"_Character__owner": relation(
|
||||
User,
|
||||
backref = "characters"),
|
||||
"_Character__skills": relation(
|
||||
Skill,
|
||||
backref="character",
|
||||
cascade = "all,delete-orphan"),
|
||||
"_Character__implants": relation(
|
||||
Implant,
|
||||
collection_class = HandledImplantBoosterList,
|
||||
cascade='all,delete-orphan',
|
||||
backref='character',
|
||||
single_parent=True,
|
||||
primaryjoin = charImplants_table.c.charID == characters_table.c.ID,
|
||||
secondaryjoin = charImplants_table.c.implantID == Implant.ID,
|
||||
secondary = charImplants_table),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ from eos.db.saveddata.drone import drones_table
|
||||
from eos.db.saveddata.fighter import fighters_table
|
||||
from eos.db.saveddata.cargo import cargo_table
|
||||
from eos.db.saveddata.implant import fitImplants_table
|
||||
from eos.types import Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, TargetResists
|
||||
from eos.types import Fit, Module, User, Booster, Drone, Fighter, Cargo, Implant, Character, DamagePattern, TargetResists, ImplantLocation
|
||||
from eos.effectHandlerHelpers import *
|
||||
|
||||
fits_table = Table("fits", saveddata_meta,
|
||||
@@ -43,6 +43,7 @@ fits_table = Table("fits", saveddata_meta,
|
||||
Column("booster", Boolean, nullable = False, index = True, default = 0),
|
||||
Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True),
|
||||
Column("modeID", Integer, nullable=True),
|
||||
Column("implantLocation", Integer, nullable=False, default=ImplantLocation.FIT),
|
||||
)
|
||||
|
||||
projectedFits_table = Table("projectedFits", saveddata_meta,
|
||||
|
||||
@@ -36,4 +36,8 @@ charImplants_table = Table("charImplants", saveddata_meta,
|
||||
Column("charID", ForeignKey("characters.ID"), index = True),
|
||||
Column("implantID", ForeignKey("implants.ID"), primary_key = True))
|
||||
|
||||
implantsSetMap_table = Table("implantSetMap", saveddata_meta,
|
||||
Column("setID", ForeignKey("implantSets.ID"), index = True),
|
||||
Column("implantID", ForeignKey("implants.ID"), primary_key = True))
|
||||
|
||||
mapper(Implant, implants_table)
|
||||
|
||||
45
eos/db/saveddata/implantSet.py
Normal file
45
eos/db/saveddata/implantSet.py
Normal file
@@ -0,0 +1,45 @@
|
||||
#===============================================================================
|
||||
# Copyright (C) 2016 Ryan Holmes
|
||||
#
|
||||
# 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/>.
|
||||
#===============================================================================
|
||||
|
||||
from sqlalchemy import Table, Column, Integer, ForeignKey, String
|
||||
from sqlalchemy.orm import relation, mapper
|
||||
|
||||
from eos.db import saveddata_meta
|
||||
from eos.db.saveddata.implant import implantsSetMap_table
|
||||
from eos.types import Implant, ImplantSet
|
||||
from eos.effectHandlerHelpers import HandledImplantBoosterList
|
||||
|
||||
implant_set_table = Table("implantSets", saveddata_meta,
|
||||
Column("ID", Integer, primary_key = True),
|
||||
Column("name", String, nullable = False),
|
||||
)
|
||||
|
||||
mapper(ImplantSet, implant_set_table,
|
||||
properties = {
|
||||
"_ImplantSet__implants": relation(
|
||||
Implant,
|
||||
collection_class = HandledImplantBoosterList,
|
||||
cascade='all, delete, delete-orphan',
|
||||
backref='set',
|
||||
single_parent=True,
|
||||
primaryjoin = implantsSetMap_table.c.setID == implant_set_table.c.ID,
|
||||
secondaryjoin = implantsSetMap_table.c.implantID == Implant.ID,
|
||||
secondary = implantsSetMap_table),
|
||||
}
|
||||
)
|
||||
@@ -20,7 +20,7 @@
|
||||
from eos.db.util import processEager, processWhere
|
||||
from eos.db import saveddata_session, sd_lock
|
||||
|
||||
from eos.types import User, Character, Fit, Price, DamagePattern, Fleet, MiscData, Wing, Squad, TargetResists, Override, CrestChar
|
||||
from eos.types import *
|
||||
from eos.db.saveddata.fleet import squadmembers_table
|
||||
from eos.db.saveddata.fit import projectedFits_table
|
||||
from sqlalchemy.sql import and_
|
||||
@@ -154,7 +154,7 @@ def getCharacter(lookfor, eager=None):
|
||||
elif isinstance(lookfor, basestring):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
character = saveddata_session.query(Character).options(*eager).filter(Character.name == lookfor).first()
|
||||
character = saveddata_session.query(Character).options(*eager).filter(Character.savedName == lookfor).first()
|
||||
else:
|
||||
raise TypeError("Need integer or string as argument")
|
||||
return character
|
||||
@@ -349,6 +349,12 @@ def getTargetResistsList(eager=None):
|
||||
patterns = saveddata_session.query(TargetResists).options(*eager).all()
|
||||
return patterns
|
||||
|
||||
def getImplantSetList(eager=None):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
sets = saveddata_session.query(ImplantSet).options(*eager).all()
|
||||
return sets
|
||||
|
||||
@cachedQuery(DamagePattern, 1, "lookfor")
|
||||
def getDamagePattern(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
@@ -385,6 +391,24 @@ def getTargetResists(lookfor, eager=None):
|
||||
raise TypeError("Need integer or string as argument")
|
||||
return pattern
|
||||
|
||||
@cachedQuery(ImplantSet, 1, "lookfor")
|
||||
def getImplantSet(lookfor, eager=None):
|
||||
if isinstance(lookfor, int):
|
||||
if eager is None:
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(ImplantSet).get(lookfor)
|
||||
else:
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.ID == lookfor).first()
|
||||
elif isinstance(lookfor, basestring):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.name == lookfor).first()
|
||||
else:
|
||||
raise TypeError("Improper argument")
|
||||
return pattern
|
||||
|
||||
def searchFits(nameLike, where=None, eager=None):
|
||||
if not isinstance(nameLike, basestring):
|
||||
raise TypeError("Need string as argument")
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(
|
||||
fit.appliedImplants.filteredItemMultiply(
|
||||
lambda implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and "implantSetAngel" in implant.itemModifiedAttributes,
|
||||
"signatureRadiusBonus",
|
||||
implant.getModifiedItemAttr("implantSetAngel"))
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
"scanGravimetricStrengthPercent", implant.getModifiedItemAttr("implantSetCaldariNavy"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
"scanGravimetricStrengthModifier", implant.getModifiedItemAttr("implantSetLGCaldariNavy"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
"scanMagnetometricStrengthPercent", implant.getModifiedItemAttr("implantSetFederationNavy"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
"scanMagnetometricStrengthModifier", implant.getModifiedItemAttr("implantSetLGFederationNavy"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
"scanRadarStrengthPercent", implant.getModifiedItemAttr("implantSetImperialNavy"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
type = "passive"
|
||||
runTime = "early"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
"scanRadarStrengthModifier", implant.getModifiedItemAttr("implantSetLGImperialNavy"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"WarpSBonus", implant.getModifiedItemAttr("implantSetWarpSpeed"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
"scanLadarStrengthPercent", implant.getModifiedItemAttr("implantSetRepublicFleet"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
"scanLadarStrengthModifier", implant.getModifiedItemAttr("implantSetLGRepublicFleet"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"durationBonus", implant.getModifiedItemAttr("implantSetBloodraider"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"agilityBonus", implant.getModifiedItemAttr("implantSetChristmas"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"armorHpBonus2", implant.getModifiedItemAttr("implantSetChristmas"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"implantBonusVelocity", implant.getModifiedItemAttr("implantSetChristmas"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"capacitorCapacityBonus", implant.getModifiedItemAttr("implantSetChristmas"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"capRechargeBonus", implant.getModifiedItemAttr("implantSetChristmas"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"cpuOutputBonus2", implant.getModifiedItemAttr("implantSetChristmas"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"powerEngineeringOutputBonus", implant.getModifiedItemAttr("implantSetChristmas"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"shieldCapacityBonus", implant.getModifiedItemAttr("implantSetChristmas"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"shieldBoostMultiplier", implant.getModifiedItemAttr("implantSetGuristas"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"rangeSkillBonus", implant.getModifiedItemAttr("implantSetMordus"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"maxRangeBonus", implant.getModifiedItemAttr("implantSetORE"))
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"),
|
||||
"armorHpBonus", implant.getModifiedItemAttr("implantSetSansha") or 1)
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"velocityBonus", implant.getModifiedItemAttr("implantSetSerpentis"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"scanStrengthBonus", implant.getModifiedItemAttr("implantSetSisters"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"boosterAttributeModifier", implant.getModifiedItemAttr("implantSetSyndicate"))
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
runTime = "early"
|
||||
type = "passive"
|
||||
def handler(fit, implant, context):
|
||||
fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant",
|
||||
"agilityBonus", implant.getModifiedItemAttr("implantSetThukker"))
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
|
||||
|
||||
from sqlalchemy.orm import validates, reconstructor
|
||||
from itertools import chain
|
||||
|
||||
from eos.effectHandlerHelpers import HandledItem
|
||||
from eos.effectHandlerHelpers import HandledItem, HandledImplantBoosterList
|
||||
import eos.db
|
||||
import eos
|
||||
import logging
|
||||
@@ -89,7 +90,7 @@ class Character(object):
|
||||
return all0
|
||||
|
||||
def __init__(self, name, defaultLevel=None, initSkills=True):
|
||||
self.name = name
|
||||
self.savedName = name
|
||||
self.__owner = None
|
||||
self.defaultLevel = defaultLevel
|
||||
self.__skills = []
|
||||
@@ -100,7 +101,7 @@ class Character(object):
|
||||
for item in self.getSkillList():
|
||||
self.addSkill(Skill(item.ID, self.defaultLevel))
|
||||
|
||||
self.__implants = eos.saveddata.fit.HandledImplantBoosterList()
|
||||
self.__implants = HandledImplantBoosterList()
|
||||
self.apiKey = None
|
||||
|
||||
@reconstructor
|
||||
@@ -128,6 +129,14 @@ class Character(object):
|
||||
def owner(self, owner):
|
||||
self.__owner = owner
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.savedName if not self.isDirty else "{} *".format(self.savedName)
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
self.savedName = name
|
||||
|
||||
@property
|
||||
def skills(self):
|
||||
return self.__skills
|
||||
@@ -200,8 +209,13 @@ class Character(object):
|
||||
skill.calculateModifiedAttributes(fit, runTime)
|
||||
|
||||
def clear(self):
|
||||
for skill in self.skills:
|
||||
skill.clear()
|
||||
c = chain(
|
||||
self.skills,
|
||||
self.implants
|
||||
)
|
||||
for stuff in c:
|
||||
if stuff is not None and stuff != self:
|
||||
stuff.clear()
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copy = Character("%s copy" % self.name, initSkills=False)
|
||||
|
||||
@@ -31,6 +31,8 @@ import eos.db
|
||||
import time
|
||||
import copy
|
||||
from utils.timer import Timer
|
||||
from eos.enum import Enum
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
@@ -41,6 +43,10 @@ try:
|
||||
except ImportError:
|
||||
from utils.compat import OrderedDict
|
||||
|
||||
class ImplantLocation(Enum):
|
||||
FIT = 0
|
||||
CHARACTER = 1
|
||||
|
||||
class Fit(object):
|
||||
"""Represents a fitting, with modules, ship, implants, etc."""
|
||||
|
||||
@@ -323,17 +329,20 @@ class Fit(object):
|
||||
|
||||
return -log(0.25) * agility * mass / 1000000
|
||||
|
||||
@property
|
||||
def implantSource(self):
|
||||
return self.implantLocation
|
||||
|
||||
@implantSource.setter
|
||||
def implantSource(self, source):
|
||||
self.implantLocation = source
|
||||
|
||||
@property
|
||||
def appliedImplants(self):
|
||||
implantsBySlot = {}
|
||||
if self.character:
|
||||
for implant in self.character.implants:
|
||||
implantsBySlot[implant.slot] = implant
|
||||
|
||||
for implant in self.implants:
|
||||
implantsBySlot[implant.slot] = implant
|
||||
|
||||
return implantsBySlot.values()
|
||||
if self.implantLocation == ImplantLocation.CHARACTER:
|
||||
return self.character.implants
|
||||
else:
|
||||
return self.implants
|
||||
|
||||
@validates("ID", "ownerID", "shipID")
|
||||
def validator(self, key, val):
|
||||
|
||||
58
eos/saveddata/implantSet.py
Normal file
58
eos/saveddata/implantSet.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#===============================================================================
|
||||
# Copyright (C) 2016 Ryan Holmes
|
||||
#
|
||||
# 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/>.
|
||||
#===============================================================================
|
||||
|
||||
from eos.effectHandlerHelpers import HandledImplantBoosterList
|
||||
from copy import deepcopy
|
||||
|
||||
class ImplantSet(object):
|
||||
def __init__(self, name=None):
|
||||
self.name = name
|
||||
self.__implants = HandledImplantBoosterList()
|
||||
|
||||
@property
|
||||
def implants(self):
|
||||
return self.__implants
|
||||
|
||||
@classmethod
|
||||
def exportSets(cls, *sets):
|
||||
out = "# Exported from pyfa\n#\n" \
|
||||
"# Values are in following format:\n" \
|
||||
"# [Implant Set name]\n" \
|
||||
"# [Implant name]\n" \
|
||||
"# [Implant name]\n" \
|
||||
"# ...\n\n"
|
||||
|
||||
for set in sets:
|
||||
out += "[{}]\n".format(set.name)
|
||||
for implant in set.implants:
|
||||
out += "{}\n".format(implant.item.name)
|
||||
out += "\n"
|
||||
|
||||
return out.strip()
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
copy = ImplantSet(self.name)
|
||||
copy.name = "%s copy" % self.name
|
||||
|
||||
orig = getattr(self, 'implants')
|
||||
c = getattr(copy, 'implants')
|
||||
for i in orig:
|
||||
c.append(deepcopy(i, memo))
|
||||
|
||||
return copy
|
||||
@@ -30,10 +30,11 @@ from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fighter import Fighter
|
||||
from eos.saveddata.cargo import Cargo
|
||||
from eos.saveddata.implant import Implant
|
||||
from eos.saveddata.implantSet import ImplantSet
|
||||
from eos.saveddata.booster import SideEffect
|
||||
from eos.saveddata.booster import Booster
|
||||
from eos.saveddata.ship import Ship
|
||||
from eos.saveddata.fit import Fit
|
||||
from eos.saveddata.fit import Fit, ImplantLocation
|
||||
from eos.saveddata.mode import Mode
|
||||
from eos.saveddata.fleet import Fleet, Wing, Squad
|
||||
from eos.saveddata.miscData import MiscData
|
||||
|
||||
@@ -20,4 +20,5 @@ __all__ = [
|
||||
"priceClear",
|
||||
"amount",
|
||||
"metaSwap",
|
||||
"implantSets",
|
||||
]
|
||||
|
||||
78
gui/builtinContextMenus/implantSets.py
Normal file
78
gui/builtinContextMenus/implantSets.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from gui.contextMenu import ContextMenu
|
||||
import gui.mainFrame
|
||||
import service
|
||||
import gui.globalEvents as GE
|
||||
import wx
|
||||
|
||||
class ImplantSets(ContextMenu):
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
return srcContext in ("implantView", "implantEditor")
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "Add Implant Set"
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
"""
|
||||
A note on the selection here: Most context menus act on a fit, so it's easy enough to get the active fit from
|
||||
the MainFrame instance. There's never been a reason to get info from another window, so there's not common
|
||||
way of doing this. However, we use this context menu within the Character Editor to apply implant sets to a
|
||||
character, so we need to access the character editor.
|
||||
|
||||
It is for these reasons that I hijack the selection parameter when calling the menu and pass a pointer to the
|
||||
Character Editor. This way we can use it to get current editing character ID and apply the implants.
|
||||
|
||||
It would probably be better to have a function on the MainFrame to get the currently open Character Editor (as
|
||||
we do with the item stats window). Eventually... Until then, this long ass note will remain to remind me why
|
||||
stupid shit like this is even happening.
|
||||
"""
|
||||
|
||||
m = wx.Menu()
|
||||
bindmenu = rootMenu if "wxMSW" in wx.PlatformInfo else m
|
||||
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
implantSets = sIS.getImplantSetList()
|
||||
|
||||
self.context = context
|
||||
if len(selection) == 1:
|
||||
self.selection = selection[0] # dirty hack here
|
||||
|
||||
self.idmap = {}
|
||||
|
||||
for set in implantSets:
|
||||
id = wx.NewId()
|
||||
mitem = wx.MenuItem(rootMenu, id, set.name)
|
||||
bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem)
|
||||
self.idmap[id] = set
|
||||
m.AppendItem(mitem)
|
||||
|
||||
return m
|
||||
|
||||
def handleSelection(self, event):
|
||||
set = self.idmap.get(event.Id, None)
|
||||
|
||||
if set is None:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
if self.context == "implantEditor":
|
||||
# we are calling from character editor, the implant source is different
|
||||
sChar = service.Character.getInstance()
|
||||
charID = self.selection.getActiveCharacter()
|
||||
|
||||
for implant in set.implants:
|
||||
sChar.addImplant(charID, implant.item.ID)
|
||||
|
||||
wx.PostEvent(self.selection, GE.CharChanged())
|
||||
else:
|
||||
sFit = service.Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
for implant in set.implants:
|
||||
sFit.addImplant(fitID, implant.item.ID, recalc=implant == set.implants[-1])
|
||||
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
|
||||
|
||||
|
||||
ImplantSets.register()
|
||||
@@ -9,6 +9,7 @@ class ItemStats(ContextMenu):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
|
||||
return srcContext in ("marketItemGroup", "marketItemMisc",
|
||||
"fittingModule", "fittingCharge",
|
||||
"fittingShip", "baseShip",
|
||||
@@ -16,7 +17,7 @@ class ItemStats(ContextMenu):
|
||||
"implantItem", "boosterItem",
|
||||
"skillItem", "projectedModule",
|
||||
"projectedDrone", "projectedCharge",
|
||||
"itemStats", "fighterItem")
|
||||
"itemStats", "fighterItem", "implantItemChar")
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "{0} Stats".format(itmContext if itmContext is not None else "Item")
|
||||
|
||||
@@ -12,7 +12,8 @@ class MarketJump(ContextMenu):
|
||||
"fittingCharge", "droneItem",
|
||||
"implantItem", "boosterItem",
|
||||
"projectedModule", "projectedDrone",
|
||||
"projectedCharge", "cargoItem")
|
||||
"projectedCharge", "cargoItem",
|
||||
"implantItemChar")
|
||||
|
||||
if not srcContext in validContexts or selection is None or len(selection) < 1:
|
||||
return False
|
||||
@@ -33,12 +34,10 @@ class MarketJump(ContextMenu):
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
srcContext = fullContext[0]
|
||||
if srcContext in ("fittingModule", "droneItem", "implantItem",
|
||||
"boosterItem", "projectedModule", "projectedDrone",
|
||||
"cargoItem"):
|
||||
item = selection[0].item
|
||||
elif srcContext in ("fittingCharge", "projectedCharge"):
|
||||
if srcContext in ("fittingCharge", "projectedCharge"):
|
||||
item = selection[0].charge
|
||||
elif hasattr(selection[0], "item"):
|
||||
item = selection[0].item
|
||||
else:
|
||||
item = selection[0]
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from gui import builtinViewColumns
|
||||
from gui.viewColumn import ViewColumn
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
import wx
|
||||
from eos.types import Drone, Fit, Module, Slot, Rack
|
||||
from eos.types import Drone, Fit, Module, Slot, Rack, Implant
|
||||
|
||||
class BaseIcon(ViewColumn):
|
||||
name = "Base Icon"
|
||||
@@ -21,6 +21,11 @@ class BaseIcon(ViewColumn):
|
||||
return self.shipImage
|
||||
if isinstance(stuff, Rack):
|
||||
return -1
|
||||
if isinstance(stuff, Implant):
|
||||
if stuff.character: # if it has a character as it's parent
|
||||
return self.fittingView.imageList.GetImageIndex("character_small", "gui")
|
||||
else:
|
||||
return self.shipImage
|
||||
if isinstance(stuff, Module):
|
||||
if stuff.isEmpty:
|
||||
return self.fittingView.imageList.GetImageIndex("slot_%s_small" % Slot.getName(stuff.slot).lower(), "gui")
|
||||
|
||||
@@ -22,7 +22,7 @@ from gui.viewColumn import ViewColumn
|
||||
import gui.mainFrame
|
||||
|
||||
import wx
|
||||
from eos.types import Drone, Cargo, Fit, Module, Slot, Rack
|
||||
from eos.types import Drone, Cargo, Fit, Module, Slot, Rack, Implant
|
||||
import service
|
||||
|
||||
class BaseName(ViewColumn):
|
||||
@@ -61,6 +61,8 @@ class BaseName(ViewColumn):
|
||||
return "%s Slot" % Slot.getName(stuff.slot).capitalize()
|
||||
else:
|
||||
return stuff.item.name
|
||||
elif isinstance(stuff, Implant):
|
||||
return stuff.item.name
|
||||
else:
|
||||
item = getattr(stuff, "item", stuff)
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ from gui.bitmapLoader import BitmapLoader
|
||||
import gui.mainFrame
|
||||
|
||||
import wx
|
||||
from eos.types import Drone, Module, Rack, Fit
|
||||
from eos.types import Drone, Module, Rack, Fit, Implant
|
||||
from eos.types import State as State_
|
||||
|
||||
class State(ViewColumn):
|
||||
@@ -67,6 +67,9 @@ class State(ViewColumn):
|
||||
if projectionInfo.active:
|
||||
return generic_active
|
||||
return generic_inactive
|
||||
elif isinstance(stuff, Implant) and stuff.character:
|
||||
# if we're showing character implants, show an "online" state, which should not be changed
|
||||
return self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(0).lower(), "gui")
|
||||
else:
|
||||
active = getattr(stuff, "active", None)
|
||||
if active is None:
|
||||
|
||||
@@ -1 +1 @@
|
||||
__all__ = ["fittingView", "fleetView"]
|
||||
__all__ = ["fittingView", "fleetView", "implantEditor"]
|
||||
|
||||
173
gui/builtinViews/entityEditor.py
Normal file
173
gui/builtinViews/entityEditor.py
Normal file
@@ -0,0 +1,173 @@
|
||||
import wx
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
import service
|
||||
|
||||
class BaseValidator(wx.PyValidator):
|
||||
def __init__(self):
|
||||
wx.PyValidator.__init__(self)
|
||||
|
||||
def Validate(self, win):
|
||||
raise NotImplementedError()
|
||||
|
||||
def TransferToWindow(self):
|
||||
return True
|
||||
|
||||
def TransferFromWindow(self):
|
||||
return True
|
||||
|
||||
class TextEntryValidatedDialog(wx.TextEntryDialog):
|
||||
def __init__(self, parent, validator=None, *args, **kargs):
|
||||
wx.TextEntryDialog.__init__(self, parent, *args, **kargs)
|
||||
self.parent = parent
|
||||
|
||||
self.txtctrl = self.FindWindowById(3000)
|
||||
if validator:
|
||||
self.txtctrl.SetValidator(validator())
|
||||
|
||||
class EntityEditor (wx.Panel):
|
||||
"""
|
||||
Entity Editor is a panel that takes some sort of list as a source and populates a drop down with options to add/
|
||||
rename/clone/delete an entity. Comes with dialogs that take user input. Classes that derive this class must override
|
||||
functions that get the list from the source, what to do when user does an action, and how to validate the input.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, entityName):
|
||||
wx.Panel.__init__(self, parent, id=wx.ID_ANY, style=wx.TAB_TRAVERSAL)
|
||||
self.entityName = entityName
|
||||
self.validator = None
|
||||
self.navSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.choices = []
|
||||
self.choices.sort(key=lambda p: p.name)
|
||||
self.entityChoices = wx.Choice(self, choices=map(lambda p: p.name, self.choices))
|
||||
self.navSizer.Add(self.entityChoices, 1, wx.ALL, 5)
|
||||
|
||||
buttons = (("new", wx.ART_NEW, self.OnNew),
|
||||
("rename", BitmapLoader.getBitmap("rename", "gui"), self.OnRename),
|
||||
("copy", wx.ART_COPY, self.OnCopy),
|
||||
("delete", wx.ART_DELETE, self.OnDelete))
|
||||
|
||||
size = None
|
||||
for name, art, func in buttons:
|
||||
bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art
|
||||
btn = wx.BitmapButton(self, wx.ID_ANY, bitmap)
|
||||
if size is None:
|
||||
size = btn.GetSize()
|
||||
|
||||
btn.SetMinSize(size)
|
||||
btn.SetMaxSize(size)
|
||||
|
||||
btn.SetToolTipString("{} {}".format(name.capitalize(), self.entityName))
|
||||
btn.Bind(wx.EVT_BUTTON, func)
|
||||
setattr(self, "btn%s" % name.capitalize(), btn)
|
||||
self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2)
|
||||
|
||||
self.SetSizer(self.navSizer)
|
||||
self.Layout()
|
||||
|
||||
self.refreshEntityList()
|
||||
|
||||
def SetEditorValidator(self, validator=None):
|
||||
""" Sets validator class (not an instance of the class) """
|
||||
self.validator = validator
|
||||
|
||||
def getEntitiesFromContext(self):
|
||||
""" Gets list of entities from current context """
|
||||
raise NotImplementedError()
|
||||
|
||||
def DoNew(self, name):
|
||||
"""Override method to do new entity logic. Must return the new entity"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def DoCopy(self, entity, name):
|
||||
"""Override method to copy entity. Must return the copy"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def DoRename(self, entity, name):
|
||||
"""Override method to rename an entity"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def DoDelete(self, entity):
|
||||
"""Override method to delete entity"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def OnNew(self, event):
|
||||
dlg = TextEntryValidatedDialog(self, self.validator,
|
||||
"Enter a name for your new {}:".format(self.entityName),
|
||||
"New {}".format(self.entityName))
|
||||
dlg.CenterOnParent()
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
new = self.DoNew(dlg.GetValue().strip())
|
||||
self.refreshEntityList(new)
|
||||
wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED))
|
||||
else:
|
||||
return False
|
||||
|
||||
def OnCopy(self, event):
|
||||
dlg = TextEntryValidatedDialog(self, self.validator,
|
||||
"Enter a name for your {} copy:".format(self.entityName),
|
||||
"Copy {}".format(self.entityName))
|
||||
active = self.getActiveEntity()
|
||||
dlg.SetValue("{} Copy".format(active.name))
|
||||
dlg.txtctrl.SetInsertionPointEnd()
|
||||
dlg.CenterOnParent()
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
copy = self.DoCopy(active, dlg.GetValue().strip())
|
||||
self.refreshEntityList(copy)
|
||||
wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED))
|
||||
|
||||
def OnRename(self, event):
|
||||
dlg = TextEntryValidatedDialog(self, self.validator,
|
||||
"Enter a new name for your {}:".format(self.entityName),
|
||||
"Rename {}".format(self.entityName))
|
||||
active = self.getActiveEntity()
|
||||
dlg.SetValue(active.name)
|
||||
dlg.txtctrl.SetInsertionPointEnd()
|
||||
dlg.CenterOnParent()
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
self.DoRename(active, dlg.GetValue().strip())
|
||||
self.refreshEntityList(active)
|
||||
wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED))
|
||||
|
||||
def OnDelete(self, event):
|
||||
dlg = wx.MessageDialog(self,
|
||||
"Do you really want to delete the {} {}?".format(self.getActiveEntity().name, self.entityName),
|
||||
"Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION)
|
||||
dlg.CenterOnParent()
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
self.DoDelete(self.getActiveEntity())
|
||||
self.refreshEntityList()
|
||||
wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED))
|
||||
|
||||
def refreshEntityList(self, selected=None):
|
||||
self.choices = self.getEntitiesFromContext()
|
||||
self.entityChoices.Clear()
|
||||
|
||||
self.entityChoices.AppendItems(map(lambda p: p.name, self.choices))
|
||||
if selected:
|
||||
idx = self.choices.index(selected)
|
||||
self.entityChoices.SetSelection(idx)
|
||||
else:
|
||||
self.entityChoices.SetSelection(0)
|
||||
|
||||
def getActiveEntity(self):
|
||||
if len(self.choices) == 0:
|
||||
return None
|
||||
|
||||
return self.choices[self.entityChoices.GetSelection()]
|
||||
|
||||
def setActiveEntity(self, entity):
|
||||
self.entityChoices.SetSelection(self.choices.index(entity))
|
||||
|
||||
def checkEntitiesExist(self):
|
||||
if len(self.choices) == 0:
|
||||
self.Parent.Hide()
|
||||
if self.OnNew(None) is False:
|
||||
return False
|
||||
self.Parent.Show()
|
||||
|
||||
return True
|
||||
258
gui/builtinViews/implantEditor.py
Normal file
258
gui/builtinViews/implantEditor.py
Normal file
@@ -0,0 +1,258 @@
|
||||
import wx
|
||||
import service
|
||||
import gui.display as d
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
import gui.PFSearchBox as SBox
|
||||
from gui.marketBrowser import SearchBox
|
||||
from wx.lib.buttons import GenBitmapButton
|
||||
|
||||
class BaseImplantEditorView (wx.Panel):
|
||||
def addMarketViewImage(self, iconFile):
|
||||
if iconFile is None:
|
||||
return -1
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
if bitmap is None:
|
||||
return -1
|
||||
else:
|
||||
return self.availableImplantsImageList.Add(bitmap)
|
||||
|
||||
def __init__(self, parent):
|
||||
wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL)
|
||||
self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
|
||||
|
||||
pmainSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
availableSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.searchBox = SearchBox(self)
|
||||
self.itemView = ItemView(self)
|
||||
|
||||
self.itemView.Hide()
|
||||
|
||||
availableSizer.Add(self.searchBox, 0, wx.EXPAND)
|
||||
availableSizer.Add(self.itemView, 1, wx.EXPAND)
|
||||
|
||||
'''
|
||||
self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
self.availableImplantsSearch.ShowCancelButton(True)
|
||||
|
||||
availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2)
|
||||
'''
|
||||
|
||||
self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT)
|
||||
root = self.availableRoot = self.availableImplantsTree.AddRoot("Available")
|
||||
self.availableImplantsImageList = wx.ImageList(16, 16)
|
||||
self.availableImplantsTree.SetImageList(self.availableImplantsImageList)
|
||||
|
||||
availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND)
|
||||
|
||||
|
||||
pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
|
||||
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
buttonSizer.AddSpacer(( 0, 0), 1)
|
||||
|
||||
self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style = wx.BORDER_NONE)
|
||||
buttonSizer.Add(self.btnAdd, 0)
|
||||
|
||||
self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style = wx.BORDER_NONE)
|
||||
buttonSizer.Add(self.btnRemove, 0)
|
||||
|
||||
buttonSizer.AddSpacer(( 0, 0), 1)
|
||||
pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 0)
|
||||
|
||||
characterImplantSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.pluggedImplantsTree = AvailableImplantsView(self)
|
||||
characterImplantSizer.Add(self.pluggedImplantsTree, 1, wx.ALL|wx.EXPAND, 5)
|
||||
pmainSizer.Add(characterImplantSizer, 1, wx.EXPAND, 5)
|
||||
|
||||
self.SetSizer(pmainSizer)
|
||||
|
||||
# Populate the market tree
|
||||
|
||||
sMkt = service.Market.getInstance()
|
||||
for mktGrp in sMkt.getImplantTree():
|
||||
iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp))
|
||||
childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID))
|
||||
if sMkt.marketGroupHasTypesCheck(mktGrp) is False:
|
||||
self.availableImplantsTree.AppendItem(childId, "dummy")
|
||||
|
||||
self.availableImplantsTree.SortChildren(self.availableRoot)
|
||||
|
||||
#Bind the event to replace dummies by real data
|
||||
self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
|
||||
self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.itemSelected)
|
||||
|
||||
self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemSelected)
|
||||
|
||||
#Bind add & remove buttons
|
||||
self.btnAdd.Bind(wx.EVT_BUTTON, self.itemSelected)
|
||||
self.btnRemove.Bind(wx.EVT_BUTTON, self.removeItem)
|
||||
|
||||
# We update with an empty list first to set the initial size for Layout(), then update later with actual
|
||||
# implants for character. This helps with sizing issues.
|
||||
self.pluggedImplantsTree.update([])
|
||||
self.bindContext()
|
||||
self.Layout()
|
||||
|
||||
self.update()
|
||||
|
||||
def bindContext(self):
|
||||
# Binds self.contextChanged to whatever changes the context
|
||||
raise NotImplementedError()
|
||||
|
||||
def getImplantsFromContext(self):
|
||||
""" Gets list of implants from current context """
|
||||
raise NotImplementedError()
|
||||
|
||||
def addImplantToContext(self, item):
|
||||
""" Adds implant to the current context"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def removeImplantFromContext(self, implant):
|
||||
""" Removes implant from the current context"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def update(self):
|
||||
"""Updates implant list based off the current context"""
|
||||
self.implants = self.getImplantsFromContext()
|
||||
self.implants.sort(key=lambda i: int(i.getModifiedItemAttr("implantness")))
|
||||
self.pluggedImplantsTree.update(self.implants)
|
||||
|
||||
def contextChanged(self, event):
|
||||
self.update()
|
||||
event.Skip()
|
||||
|
||||
def expandLookup(self, event):
|
||||
tree = self.availableImplantsTree
|
||||
sMkt = service.Market.getInstance()
|
||||
parent = event.Item
|
||||
child, _ = tree.GetFirstChild(parent)
|
||||
text = tree.GetItemText(child)
|
||||
|
||||
if text == "dummy" or text == "itemdummy":
|
||||
tree.Delete(child)
|
||||
|
||||
# if the dummy item is a market group, replace with actual market groups
|
||||
if text == "dummy":
|
||||
#Add 'real stoof!' instead
|
||||
currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent), eager="children")
|
||||
for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp):
|
||||
iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(childMktGrp))
|
||||
childId = tree.AppendItem(parent, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID))
|
||||
if sMkt.marketGroupHasTypesCheck(childMktGrp) is False:
|
||||
tree.AppendItem(childId, "dummy")
|
||||
else:
|
||||
tree.AppendItem(childId, "itemdummy")
|
||||
|
||||
# replace dummy with actual items
|
||||
if text == "itemdummy":
|
||||
currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent))
|
||||
items = sMkt.getItemsByMarketGroup(currentMktGrp)
|
||||
for item in items:
|
||||
iconId = self.addMarketViewImage(item.icon.iconFile)
|
||||
tree.AppendItem(parent, item.name, iconId, data=wx.TreeItemData(item))
|
||||
|
||||
tree.SortChildren(parent)
|
||||
|
||||
def itemSelected(self, event):
|
||||
if event.EventObject is self.btnAdd:
|
||||
# janky fix that sets EventObject so that we don't have similar code elsewhere.
|
||||
if self.itemView.IsShown():
|
||||
event.EventObject = self.itemView
|
||||
else:
|
||||
event.EventObject = self.availableImplantsTree
|
||||
|
||||
if event.EventObject is self.itemView:
|
||||
curr = event.EventObject.GetFirstSelected()
|
||||
|
||||
while curr != -1:
|
||||
item = self.itemView.items[curr]
|
||||
self.addImplantToContext(item)
|
||||
|
||||
curr = event.EventObject.GetNextSelected(curr)
|
||||
else:
|
||||
root = self.availableImplantsTree.GetSelection()
|
||||
|
||||
if not root.IsOk():
|
||||
return
|
||||
|
||||
nchilds = self.availableImplantsTree.GetChildrenCount(root)
|
||||
if nchilds == 0:
|
||||
item = self.availableImplantsTree.GetPyData(root)
|
||||
self.addImplantToContext(item)
|
||||
else:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
self.update()
|
||||
|
||||
def removeItem(self, event):
|
||||
pos = self.pluggedImplantsTree.GetFirstSelected()
|
||||
if pos != -1:
|
||||
self.removeImplantFromContext(self.implants[pos])
|
||||
self.update()
|
||||
|
||||
class AvailableImplantsView(d.Display):
|
||||
DEFAULT_COLS = ["attr:implantness",
|
||||
"Base Icon",
|
||||
"Base Name"]
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, parent.removeItem)
|
||||
|
||||
class ItemView(d.Display):
|
||||
DEFAULT_COLS = ["Base Icon",
|
||||
"Base Name",
|
||||
"attr:power,,,True",
|
||||
"attr:cpu,,,True"]
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.searchBox = parent.searchBox
|
||||
|
||||
self.items = []
|
||||
|
||||
# Bind search actions
|
||||
self.searchBox.Bind(SBox.EVT_TEXT_ENTER, self.scheduleSearch)
|
||||
self.searchBox.Bind(SBox.EVT_SEARCH_BTN, self.scheduleSearch)
|
||||
self.searchBox.Bind(SBox.EVT_CANCEL_BTN, self.clearSearch)
|
||||
self.searchBox.Bind(SBox.EVT_TEXT, self.scheduleSearch)
|
||||
|
||||
def clearSearch(self, event=None):
|
||||
if self.IsShown():
|
||||
self.parent.availableImplantsTree.Show()
|
||||
self.Hide()
|
||||
self.parent.Layout()
|
||||
|
||||
if event:
|
||||
self.searchBox.Clear()
|
||||
|
||||
self.items = []
|
||||
self.update(self.items)
|
||||
|
||||
def scheduleSearch(self, event=None):
|
||||
sMkt = service.Market.getInstance()
|
||||
|
||||
search = self.searchBox.GetLineText(0)
|
||||
# Make sure we do not count wildcard as search symbol
|
||||
realsearch = search.replace("*", "")
|
||||
# Show nothing if query is too short
|
||||
if len(realsearch) < 3:
|
||||
self.clearSearch()
|
||||
return
|
||||
|
||||
sMkt.searchItems(search, self.populateSearch, ["Implant"])
|
||||
|
||||
def populateSearch(self, items):
|
||||
if not self.IsShown():
|
||||
self.parent.availableImplantsTree.Hide()
|
||||
self.Show()
|
||||
self.parent.Layout()
|
||||
|
||||
self.items = sorted(list(items), key=lambda i: i.name)
|
||||
|
||||
self.update(self.items)
|
||||
@@ -19,15 +19,80 @@
|
||||
|
||||
import wx
|
||||
|
||||
import gui.mainFrame
|
||||
import wx.lib.newevent
|
||||
import wx.gizmos
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
import service
|
||||
import gui.display as d
|
||||
from gui.contextMenu import ContextMenu
|
||||
from wx.lib.buttons import GenBitmapButton
|
||||
import gui.globalEvents as GE
|
||||
from gui.builtinViews.implantEditor import BaseImplantEditorView
|
||||
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
|
||||
|
||||
|
||||
class CharacterTextValidor(BaseValidator):
|
||||
def __init__(self):
|
||||
BaseValidator.__init__(self)
|
||||
|
||||
def Clone(self):
|
||||
return CharacterTextValidor()
|
||||
|
||||
def Validate(self, win):
|
||||
profileEditor = win.Parent
|
||||
textCtrl = self.GetWindow()
|
||||
text = textCtrl.GetValue().strip()
|
||||
|
||||
try:
|
||||
if len(text) == 0:
|
||||
raise ValueError("You must supply a name for the Character!")
|
||||
elif text in [x.name for x in profileEditor.entityEditor.choices]:
|
||||
raise ValueError("Character name already in use, please choose another.")
|
||||
|
||||
return True
|
||||
except ValueError, e:
|
||||
wx.MessageBox(u"{}".format(e), "Error")
|
||||
textCtrl.SetFocus()
|
||||
return False
|
||||
|
||||
|
||||
class CharacterEntityEditor(EntityEditor):
|
||||
def __init__(self, parent):
|
||||
EntityEditor.__init__(self, parent, "Character")
|
||||
self.SetEditorValidator(CharacterTextValidor)
|
||||
|
||||
def getEntitiesFromContext(self):
|
||||
sChar = service.Character.getInstance()
|
||||
charList = sorted(sChar.getCharacterList(), key=lambda c: c.name)
|
||||
|
||||
# Do some processing to ensure that we have All 0 and All 5 at the top
|
||||
all5 = sChar.all5()
|
||||
all0 = sChar.all0()
|
||||
|
||||
charList.remove(all5)
|
||||
charList.remove(all0)
|
||||
|
||||
charList.insert(0, all5)
|
||||
charList.insert(0, all0)
|
||||
|
||||
return charList
|
||||
|
||||
def DoNew(self, name):
|
||||
sChar = service.Character.getInstance()
|
||||
return sChar.new(name)
|
||||
|
||||
def DoRename(self, entity, name):
|
||||
sChar = service.Character.getInstance()
|
||||
sChar.rename(entity, name)
|
||||
|
||||
def DoCopy(self, entity, name):
|
||||
sChar = service.Character.getInstance()
|
||||
copy = sChar.copy(entity)
|
||||
sChar.rename(copy, name)
|
||||
return copy
|
||||
|
||||
def DoDelete(self, entity):
|
||||
sChar = service.Character.getInstance()
|
||||
sChar.delete(entity)
|
||||
|
||||
|
||||
class CharacterEditor(wx.Frame):
|
||||
def __init__(self, parent):
|
||||
@@ -38,75 +103,26 @@ class CharacterEditor(wx.Frame):
|
||||
self.SetIcon(i)
|
||||
|
||||
self.mainFrame = parent
|
||||
|
||||
#self.disableWin = wx.WindowDisabler(self)
|
||||
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
||||
sFit = service.Fit.getInstance()
|
||||
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.navSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
sChar = service.Character.getInstance()
|
||||
|
||||
self.btnSave = wx.Button(self, wx.ID_SAVE)
|
||||
self.btnSave.Hide()
|
||||
self.btnSave.Bind(wx.EVT_BUTTON, self.processRename)
|
||||
|
||||
self.characterRename = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
self.characterRename.Hide()
|
||||
self.characterRename.Bind(wx.EVT_TEXT_ENTER, self.processRename)
|
||||
|
||||
self.charChoice = wx.Choice(self, wx.ID_ANY, style=0)
|
||||
self.navSizer.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
charList = sChar.getCharacterList()
|
||||
|
||||
for id, name, active in charList:
|
||||
i = self.charChoice.Append(name, id)
|
||||
if active:
|
||||
self.charChoice.SetSelection(i)
|
||||
|
||||
self.navSizer.Add(self.btnSave, 0, wx.ALL , 5)
|
||||
|
||||
|
||||
buttons = (("new", wx.ART_NEW),
|
||||
("rename", BitmapLoader.getBitmap("rename", "gui")),
|
||||
("copy", wx.ART_COPY),
|
||||
("delete", wx.ART_DELETE))
|
||||
|
||||
size = None
|
||||
for name, art in buttons:
|
||||
bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art
|
||||
btn = wx.BitmapButton(self, wx.ID_ANY, bitmap)
|
||||
if size is None:
|
||||
size = btn.GetSize()
|
||||
|
||||
btn.SetMinSize(size)
|
||||
btn.SetMaxSize(size)
|
||||
|
||||
btn.SetToolTipString("%s character" % name.capitalize())
|
||||
btn.Bind(wx.EVT_BUTTON, getattr(self, name))
|
||||
setattr(self, "btn%s" % name.capitalize(), btn)
|
||||
self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2)
|
||||
|
||||
|
||||
mainSizer.Add(self.navSizer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
self.entityEditor = CharacterEntityEditor(self)
|
||||
mainSizer.Add(self.entityEditor, 0, wx.ALL | wx.EXPAND, 2)
|
||||
# Default drop down to current fit's character
|
||||
self.entityEditor.setActiveEntity(sFit.character)
|
||||
|
||||
self.viewsNBContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
|
||||
self.sview = SkillTreeView(self.viewsNBContainer)
|
||||
#self.iview = ImplantsTreeView(self.viewsNBContainer)
|
||||
#=======================================================================
|
||||
# RC2
|
||||
#self.iview.Show(False)
|
||||
#=======================================================================
|
||||
self.iview = ImplantEditorView(self.viewsNBContainer)
|
||||
self.aview = APIView(self.viewsNBContainer)
|
||||
|
||||
self.viewsNBContainer.AddPage(self.sview, "Skills")
|
||||
|
||||
#=======================================================================
|
||||
# Disabled for RC2
|
||||
# self.viewsNBContainer.AddPage(self.iview, "Implants")
|
||||
#=======================================================================
|
||||
self.viewsNBContainer.AddPage(self.iview, "Implants")
|
||||
self.viewsNBContainer.AddPage(self.aview, "API")
|
||||
|
||||
mainSizer.Add(self.viewsNBContainer, 1, wx.EXPAND | wx.ALL, 5)
|
||||
@@ -124,7 +140,6 @@ class CharacterEditor(wx.Frame):
|
||||
bSizerButtons.AddStretchSpacer()
|
||||
bSizerButtons.Add(self.btnOK, 0, wx.ALL, 5)
|
||||
|
||||
|
||||
self.btnSaveChar.Bind(wx.EVT_BUTTON, self.saveChar)
|
||||
self.btnSaveAs.Bind(wx.EVT_BUTTON, self.saveCharAs)
|
||||
self.btnRevert.Bind(wx.EVT_BUTTON, self.revertChar)
|
||||
@@ -139,16 +154,12 @@ class CharacterEditor(wx.Frame):
|
||||
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
charID = self.getActiveCharacter()
|
||||
if sChar.getCharName(charID) in ("All 0", "All 5"):
|
||||
self.restrict()
|
||||
|
||||
self.registerEvents()
|
||||
self.Bind(wx.EVT_CLOSE, self.closeEvent)
|
||||
self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList)
|
||||
self.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged)
|
||||
|
||||
def btnRestrict(self):
|
||||
sChar = service.Character.getInstance()
|
||||
charID = self.getActiveCharacter()
|
||||
char = sChar.getCharacter(charID)
|
||||
char = self.entityEditor.getActiveEntity()
|
||||
|
||||
# enable/disable character saving stuff
|
||||
self.btnSaveChar.Enable(not char.ro and char.isDirty)
|
||||
@@ -156,46 +167,34 @@ class CharacterEditor(wx.Frame):
|
||||
self.btnRevert.Enable(char.isDirty)
|
||||
|
||||
def refreshCharacterList(self, event=None):
|
||||
sChar = service.Character.getInstance()
|
||||
charList = sChar.getCharacterList()
|
||||
active = self.getActiveCharacter()
|
||||
self.charChoice.Clear()
|
||||
|
||||
for id, name, _ in charList:
|
||||
i = self.charChoice.Append(name, id)
|
||||
if active == id:
|
||||
self.charChoice.SetSelection(i)
|
||||
|
||||
"""This is only called when we save a modified character"""
|
||||
active = self.entityEditor.getActiveEntity()
|
||||
self.entityEditor.refreshEntityList(active)
|
||||
self.btnRestrict()
|
||||
|
||||
if event:
|
||||
event.Skip()
|
||||
|
||||
def editingFinished(self, event):
|
||||
#del self.disableWin
|
||||
wx.PostEvent(self.mainFrame, GE.CharListUpdated())
|
||||
self.Destroy()
|
||||
|
||||
def registerEvents(self):
|
||||
self.Bind(wx.EVT_CLOSE, self.closeEvent)
|
||||
self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList)
|
||||
self.charChoice.Bind(wx.EVT_CHOICE, self.charChanged)
|
||||
|
||||
def saveChar(self, event):
|
||||
sChr = service.Character.getInstance()
|
||||
charID = self.getActiveCharacter()
|
||||
sChr.saveCharacter(charID)
|
||||
self.sview.populateSkillTree()
|
||||
char = self.entityEditor.getActiveEntity()
|
||||
sChr.saveCharacter(char.ID)
|
||||
wx.PostEvent(self, GE.CharListUpdated())
|
||||
|
||||
def saveCharAs(self, event):
|
||||
charID = self.getActiveCharacter()
|
||||
dlg = SaveCharacterAs(self, charID)
|
||||
char = self.entityEditor.getActiveEntity()
|
||||
dlg = SaveCharacterAs(self, char.ID)
|
||||
dlg.ShowModal()
|
||||
self.sview.populateSkillTree()
|
||||
|
||||
def revertChar(self, event):
|
||||
sChr = service.Character.getInstance()
|
||||
charID = self.getActiveCharacter()
|
||||
sChr.revertCharacter(charID)
|
||||
self.sview.populateSkillTree()
|
||||
char = self.entityEditor.getActiveEntity()
|
||||
sChr.revertCharacter(char.ID)
|
||||
wx.PostEvent(self, GE.CharListUpdated())
|
||||
|
||||
def closeEvent(self, event):
|
||||
@@ -204,120 +203,25 @@ class CharacterEditor(wx.Frame):
|
||||
self.Destroy()
|
||||
|
||||
def restrict(self):
|
||||
self.btnRename.Enable(False)
|
||||
self.btnDelete.Enable(False)
|
||||
self.aview.stDisabledTip.Show()
|
||||
self.aview.inputID.Enable(False)
|
||||
self.aview.inputKey.Enable(False)
|
||||
self.aview.charChoice.Enable(False)
|
||||
self.aview.btnFetchCharList.Enable(False)
|
||||
self.aview.btnFetchSkills.Enable(False)
|
||||
self.aview.stStatus.SetLabel("")
|
||||
self.aview.Layout()
|
||||
self.entityEditor.btnRename.Enable(False)
|
||||
self.entityEditor.btnDelete.Enable(False)
|
||||
|
||||
def unrestrict(self):
|
||||
self.btnRename.Enable(True)
|
||||
self.btnDelete.Enable(True)
|
||||
self.aview.stDisabledTip.Hide()
|
||||
self.aview.inputID.Enable(True)
|
||||
self.aview.inputKey.Enable(True)
|
||||
self.aview.btnFetchCharList.Enable(True)
|
||||
self.aview.btnFetchSkills.Enable(True)
|
||||
self.aview.stStatus.SetLabel("")
|
||||
self.aview.Layout()
|
||||
self.entityEditor.btnRename.Enable()
|
||||
self.entityEditor.btnDelete.Enable()
|
||||
|
||||
def charChanged(self, event):
|
||||
self.sview.populateSkillTree()
|
||||
sChar = service.Character.getInstance()
|
||||
charID = self.getActiveCharacter()
|
||||
if sChar.getCharName(charID) in ("All 0", "All 5"):
|
||||
char = self.entityEditor.getActiveEntity()
|
||||
if char.name in ("All 0", "All 5"):
|
||||
self.restrict()
|
||||
else:
|
||||
self.unrestrict()
|
||||
|
||||
wx.PostEvent(self, GE.CharChanged())
|
||||
self.btnRestrict()
|
||||
|
||||
if event is not None:
|
||||
event.Skip()
|
||||
|
||||
def getActiveCharacter(self):
|
||||
selection = self.charChoice.GetCurrentSelection()
|
||||
return self.charChoice.GetClientData(selection) if selection is not None else None
|
||||
|
||||
def new(self, event):
|
||||
sChar = service.Character.getInstance()
|
||||
charID = sChar.new()
|
||||
id = self.charChoice.Append(sChar.getCharName(charID), charID)
|
||||
self.charChoice.SetSelection(id)
|
||||
self.unrestrict()
|
||||
self.btnSave.SetLabel("Create")
|
||||
self.rename(None)
|
||||
self.charChanged(None)
|
||||
|
||||
def rename(self, event):
|
||||
if event is not None:
|
||||
self.btnSave.SetLabel("Rename")
|
||||
self.charChoice.Hide()
|
||||
self.characterRename.Show()
|
||||
self.navSizer.Replace(self.charChoice, self.characterRename)
|
||||
self.characterRename.SetFocus()
|
||||
for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete):
|
||||
btn.Hide()
|
||||
|
||||
self.btnSave.Show()
|
||||
self.navSizer.Layout()
|
||||
|
||||
sChar = service.Character.getInstance()
|
||||
currName = sChar.getCharName(self.getActiveCharacter())
|
||||
self.characterRename.SetValue(currName)
|
||||
self.characterRename.SetSelection(0, len(currName))
|
||||
|
||||
def processRename(self, event):
|
||||
sChar = service.Character.getInstance()
|
||||
newName = self.characterRename.GetLineText(0)
|
||||
|
||||
if newName == "All 0" or newName == "All 5":
|
||||
newName = newName + " bases are belong to us"
|
||||
|
||||
charID = self.getActiveCharacter()
|
||||
sChar.rename(charID, newName)
|
||||
|
||||
self.charChoice.Show()
|
||||
self.characterRename.Hide()
|
||||
self.navSizer.Replace(self.characterRename, self.charChoice)
|
||||
for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete):
|
||||
btn.Show()
|
||||
|
||||
self.btnSave.Hide()
|
||||
self.navSizer.Layout()
|
||||
self.refreshCharacterList()
|
||||
|
||||
def copy(self, event):
|
||||
sChar = service.Character.getInstance()
|
||||
charID = sChar.copy(self.getActiveCharacter())
|
||||
id = self.charChoice.Append(sChar.getCharName(charID), charID)
|
||||
self.charChoice.SetSelection(id)
|
||||
self.unrestrict()
|
||||
self.btnSave.SetLabel("Copy")
|
||||
self.rename(None)
|
||||
wx.PostEvent(self, GE.CharChanged())
|
||||
|
||||
def delete(self, event):
|
||||
dlg = wx.MessageDialog(self,
|
||||
"Do you really want to delete this character?",
|
||||
"Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION)
|
||||
|
||||
if dlg.ShowModal() == wx.ID_YES:
|
||||
sChar = service.Character.getInstance()
|
||||
sChar.delete(self.getActiveCharacter())
|
||||
sel = self.charChoice.GetSelection()
|
||||
self.charChoice.Delete(sel)
|
||||
self.charChoice.SetSelection(sel - 1)
|
||||
newSelection = self.getActiveCharacter()
|
||||
if sChar.getCharName(newSelection) in ("All 0", "All 5"):
|
||||
self.restrict()
|
||||
|
||||
wx.PostEvent(self, GE.CharChanged())
|
||||
|
||||
def Destroy(self):
|
||||
sFit = service.Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
@@ -329,7 +233,8 @@ class CharacterEditor(wx.Frame):
|
||||
|
||||
class SkillTreeView (wx.Panel):
|
||||
def __init__(self, parent):
|
||||
wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL)
|
||||
wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL)
|
||||
self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor
|
||||
self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
|
||||
|
||||
pmainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
@@ -356,6 +261,10 @@ class SkillTreeView (wx.Panel):
|
||||
tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
|
||||
tree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.scheduleMenu)
|
||||
|
||||
# bind the Character selection event
|
||||
self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.populateSkillTree)
|
||||
self.charEditor.Bind(GE.CHAR_LIST_UPDATED, self.populateSkillTree)
|
||||
|
||||
srcContext = "skillItem"
|
||||
itemContext = "Skill"
|
||||
context = (srcContext, itemContext)
|
||||
@@ -386,11 +295,10 @@ class SkillTreeView (wx.Panel):
|
||||
|
||||
self.Layout()
|
||||
|
||||
def populateSkillTree(self):
|
||||
def populateSkillTree(self, event=None):
|
||||
sChar = service.Character.getInstance()
|
||||
charID = self.Parent.Parent.getActiveCharacter()
|
||||
dirtySkills = sChar.getDirtySkills(charID)
|
||||
dirtyGroups = set([skill.item.group.ID for skill in dirtySkills])
|
||||
char = self.charEditor.entityEditor.getActiveEntity()
|
||||
dirtyGroups = set([skill.item.group.ID for skill in char.dirtySkills])
|
||||
|
||||
groups = sChar.getSkillGroups()
|
||||
imageId = self.skillBookImageId
|
||||
@@ -407,6 +315,9 @@ class SkillTreeView (wx.Panel):
|
||||
|
||||
tree.SortChildren(root)
|
||||
|
||||
if event:
|
||||
event.Skip()
|
||||
|
||||
def expandLookup(self, event):
|
||||
root = event.Item
|
||||
tree = self.skillTreeListCtrl
|
||||
@@ -416,11 +327,11 @@ class SkillTreeView (wx.Panel):
|
||||
|
||||
#Get the real intrestin' stuff
|
||||
sChar = service.Character.getInstance()
|
||||
char = self.Parent.Parent.getActiveCharacter()
|
||||
char = self.charEditor.entityEditor.getActiveEntity()
|
||||
for id, name in sChar.getSkills(tree.GetPyData(root)):
|
||||
iconId = self.skillBookImageId
|
||||
childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id))
|
||||
level, dirty = sChar.getSkillLevel(char, id)
|
||||
level, dirty = sChar.getSkillLevel(char.ID, id)
|
||||
tree.SetItemText(childId, "Level %d" % level if isinstance(level, int) else level, 1)
|
||||
if dirty:
|
||||
tree.SetItemTextColour(childId, wx.BLUE)
|
||||
@@ -436,10 +347,9 @@ class SkillTreeView (wx.Panel):
|
||||
if self.skillTreeListCtrl.GetChildrenCount(item) > 0:
|
||||
return
|
||||
|
||||
sChar = service.Character.getInstance()
|
||||
charID = self.Parent.Parent.getActiveCharacter()
|
||||
char = self.charEditor.entityEditor.getActiveEntity()
|
||||
sMkt = service.Market.getInstance()
|
||||
if sChar.getCharName(charID) not in ("All 0", "All 5"):
|
||||
if char.name not in ("All 0", "All 5"):
|
||||
self.levelChangeMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item))
|
||||
self.PopupMenu(self.levelChangeMenu)
|
||||
else:
|
||||
@@ -450,21 +360,21 @@ class SkillTreeView (wx.Panel):
|
||||
level = self.levelIds.get(event.Id)
|
||||
|
||||
sChar = service.Character.getInstance()
|
||||
charID = self.Parent.Parent.getActiveCharacter()
|
||||
char = self.charEditor.entityEditor.getActiveEntity()
|
||||
selection = self.skillTreeListCtrl.GetSelection()
|
||||
skillID = self.skillTreeListCtrl.GetPyData(selection)
|
||||
|
||||
if level is not None:
|
||||
self.skillTreeListCtrl.SetItemText(selection, "Level %d" % level if isinstance(level, int) else level, 1)
|
||||
sChar.changeLevel(charID, skillID, level, persist=True)
|
||||
sChar.changeLevel(char.ID, skillID, level, persist=True)
|
||||
elif event.Id == self.revertID:
|
||||
sChar.revertLevel(charID, skillID)
|
||||
sChar.revertLevel(char.ID, skillID)
|
||||
elif event.Id == self.saveID:
|
||||
sChar.saveSkill(charID, skillID)
|
||||
sChar.saveSkill(char.ID, skillID)
|
||||
|
||||
self.skillTreeListCtrl.SetItemTextColour(selection, None)
|
||||
|
||||
dirtySkills = sChar.getDirtySkills(charID)
|
||||
dirtySkills = sChar.getDirtySkills(char.ID)
|
||||
dirtyGroups = set([skill.item.group.ID for skill in dirtySkills])
|
||||
|
||||
parentID = self.skillTreeListCtrl.GetItemParent(selection)
|
||||
@@ -473,156 +383,69 @@ class SkillTreeView (wx.Panel):
|
||||
if groupID not in dirtyGroups:
|
||||
self.skillTreeListCtrl.SetItemTextColour(parentID, None)
|
||||
|
||||
wx.PostEvent(self.Parent.Parent, GE.CharListUpdated())
|
||||
event.Skip()
|
||||
|
||||
class ImplantsTreeView (wx.Panel):
|
||||
def addMarketViewImage(self, iconFile):
|
||||
if iconFile is None:
|
||||
return -1
|
||||
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
if bitmap is None:
|
||||
return -1
|
||||
else:
|
||||
return self.availableImplantsImageList.Add(bitmap)
|
||||
|
||||
class ImplantEditorView(BaseImplantEditorView):
|
||||
def __init__(self, parent):
|
||||
wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL)
|
||||
BaseImplantEditorView.__init__ (self, parent)
|
||||
|
||||
pmainSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.determineEnabled()
|
||||
|
||||
availableSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5)
|
||||
if "__WXGTK__" in wx.PlatformInfo:
|
||||
self.pluggedImplantsTree.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu)
|
||||
else:
|
||||
self.pluggedImplantsTree.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu)
|
||||
|
||||
self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
|
||||
self.availableImplantsSearch.ShowCancelButton(True)
|
||||
availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2)
|
||||
def bindContext(self):
|
||||
self.Parent.Parent.entityEditor.Bind(wx.EVT_CHOICE, self.contextChanged)
|
||||
|
||||
self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT)
|
||||
root = self.availableRoot = self.availableImplantsTree.AddRoot("Available")
|
||||
self.availableImplantsImageList = wx.ImageList(16, 16)
|
||||
self.availableImplantsTree.SetImageList(self.availableImplantsImageList)
|
||||
def contextChanged(self, event):
|
||||
BaseImplantEditorView.contextChanged(self, event)
|
||||
self.determineEnabled()
|
||||
|
||||
availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND)
|
||||
|
||||
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
pmainSizer.Add(buttonSizer, 0, wx.TOP, 5)
|
||||
|
||||
self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style = wx.BORDER_NONE)
|
||||
buttonSizer.Add(self.btnAdd, 0)
|
||||
self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style = wx.BORDER_NONE)
|
||||
buttonSizer.Add(self.btnRemove, 0)
|
||||
|
||||
self.pluggedImplantsTree = AvailableImplantsView(self, style=wx.LC_SINGLE_SEL)
|
||||
|
||||
pmainSizer.Add(self.pluggedImplantsTree, 1, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.SetSizer(pmainSizer)
|
||||
|
||||
# Populate the market tree
|
||||
sMkt = service.Market.getInstance()
|
||||
for mktGrp in sMkt.getImplantTree():
|
||||
iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp))
|
||||
childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID))
|
||||
if sMkt.marketGroupHasTypesCheck(mktGrp) is False:
|
||||
self.availableImplantsTree.AppendItem(childId, "dummy")
|
||||
|
||||
self.availableImplantsTree.SortChildren(self.availableRoot)
|
||||
|
||||
#Bind the event to replace dummies by real data
|
||||
self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
|
||||
|
||||
#Bind add & remove buttons
|
||||
self.btnAdd.Bind(wx.EVT_BUTTON, self.addImplant)
|
||||
self.btnRemove.Bind(wx.EVT_BUTTON, self.removeImplant)
|
||||
|
||||
#Bind the change of a character*
|
||||
self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged)
|
||||
self.Enable(False)
|
||||
self.Layout()
|
||||
|
||||
def update(self, implants):
|
||||
self.implants = implants[:]
|
||||
self.implants.sort(key=lambda i: int(i.getModifiedItemAttr("implantness")))
|
||||
self.pluggedImplantsTree.update(self.implants)
|
||||
|
||||
def charChanged(self, event):
|
||||
def getImplantsFromContext(self):
|
||||
sChar = service.Character.getInstance()
|
||||
charID = self.Parent.Parent.getActiveCharacter()
|
||||
name = sChar.getCharName(charID)
|
||||
if name == "All 0" or name == "All 5":
|
||||
char = self.Parent.Parent.entityEditor.getActiveEntity()
|
||||
|
||||
return sChar.getImplants(char.ID)
|
||||
|
||||
def addImplantToContext(self, item):
|
||||
sChar = service.Character.getInstance()
|
||||
char = self.Parent.Parent.entityEditor.getActiveEntity()
|
||||
|
||||
sChar.addImplant(char.ID, item.ID)
|
||||
|
||||
def removeImplantFromContext(self, implant):
|
||||
sChar = service.Character.getInstance()
|
||||
char = self.Parent.Parent.entityEditor.getActiveEntity()
|
||||
|
||||
sChar.removeImplant(char.ID, implant)
|
||||
|
||||
def scheduleMenu(self, event):
|
||||
event.Skip()
|
||||
wx.CallAfter(self.spawnMenu)
|
||||
|
||||
def spawnMenu(self):
|
||||
context = (("implantEditor",),)
|
||||
# fuck good coding practices, passing a pointer to the character editor here for [reasons] =D
|
||||
# (see implantSets context class for info)
|
||||
menu = ContextMenu.getMenu((self.Parent.Parent,), *context)
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def determineEnabled(self):
|
||||
char = self.Parent.Parent.entityEditor.getActiveEntity()
|
||||
|
||||
if char.name in ("All 0", "All 5"):
|
||||
self.Enable(False)
|
||||
else:
|
||||
self.Enable(True)
|
||||
self.Enable()
|
||||
|
||||
self.update(sChar.getImplants(charID))
|
||||
event.Skip()
|
||||
|
||||
def expandLookup(self, event):
|
||||
tree = self.availableImplantsTree
|
||||
root = event.Item
|
||||
child, cookie = tree.GetFirstChild(root)
|
||||
text = tree.GetItemText(child)
|
||||
if text == "dummy" or text == "itemdummy":
|
||||
sMkt = service.Market.getInstance()
|
||||
#A DUMMY! Keeeel!!! EBUL DUMMY MUST DIAF!
|
||||
tree.Delete(child)
|
||||
|
||||
if text == "dummy":
|
||||
#Add 'real stoof!' instead
|
||||
for id, name, iconFile, more in sMkt.getChildren(tree.GetPyData(root)):
|
||||
iconId = self.addMarketViewImage(iconFile)
|
||||
childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id))
|
||||
if more:
|
||||
tree.AppendItem(childId, "dummy")
|
||||
else:
|
||||
tree.AppendItem(childId, "itemdummy")
|
||||
|
||||
if text == "itemdummy":
|
||||
sMkt = service.Market.getInstance()
|
||||
data, usedMetas = sMkt.getVariations(tree.GetPyData(root))
|
||||
for item in data:
|
||||
id = item.ID
|
||||
name = item.name
|
||||
iconFile = item.icon.iconFile
|
||||
iconId = self.addMarketViewImage(iconFile)
|
||||
tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id))
|
||||
|
||||
tree.SortChildren(root)
|
||||
|
||||
def addImplant(self, event):
|
||||
root = self.availableImplantsTree.GetSelection()
|
||||
|
||||
if not root.IsOk():
|
||||
return
|
||||
|
||||
nchilds = self.availableImplantsTree.GetChildrenCount(root)
|
||||
sChar = service.Character.getInstance()
|
||||
charID = self.Parent.Parent.getActiveCharacter()
|
||||
if nchilds == 0:
|
||||
itemID = self.availableImplantsTree.GetPyData(root)
|
||||
sChar.addImplant(charID, itemID)
|
||||
self.update(sChar.getImplants(charID))
|
||||
|
||||
def removeImplant(self, event):
|
||||
pos = self.pluggedImplantsTree.GetFirstSelected()
|
||||
if pos != -1:
|
||||
sChar = service.Character.getInstance()
|
||||
charID = self.Parent.Parent.getActiveCharacter()
|
||||
sChar.removeImplant(charID, self.implants[pos].slot)
|
||||
self.update(sChar.getImplants(charID))
|
||||
|
||||
class AvailableImplantsView(d.Display):
|
||||
DEFAULT_COLS = ["Base Name",
|
||||
"attr:implantness"]
|
||||
|
||||
def __init__(self, parent, style):
|
||||
d.Display.__init__(self, parent, style=style)
|
||||
|
||||
class APIView (wx.Panel):
|
||||
def __init__(self, parent):
|
||||
wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL)
|
||||
self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged)
|
||||
self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor
|
||||
self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
|
||||
|
||||
self.apiUrlCreatePredefined = u"https://community.eveonline.com/support/api-key/CreatePredefined?accessMask=8"
|
||||
@@ -706,13 +529,17 @@ class APIView (wx.Panel):
|
||||
self.hlEveAPI2 = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlKeyList, self.apiUrlKeyList, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE )
|
||||
pmainSizer.Add( self.hlEveAPI2, 0, wx.ALL, 2 )
|
||||
|
||||
self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged)
|
||||
|
||||
self.SetSizer(pmainSizer)
|
||||
self.Layout()
|
||||
self.charChanged(None)
|
||||
|
||||
def charChanged(self, event):
|
||||
sChar = service.Character.getInstance()
|
||||
ID, key, char, chars = sChar.getApiDetails(self.Parent.Parent.getActiveCharacter())
|
||||
activeChar = self.charEditor.entityEditor.getActiveEntity()
|
||||
|
||||
ID, key, char, chars = sChar.getApiDetails(activeChar.ID)
|
||||
self.inputID.SetValue(str(ID))
|
||||
self.inputKey.SetValue(key)
|
||||
|
||||
@@ -730,6 +557,14 @@ class APIView (wx.Panel):
|
||||
self.charChoice.Enable(False)
|
||||
self.btnFetchSkills.Enable(False)
|
||||
|
||||
if activeChar.name in ("All 0", "All 5"):
|
||||
self.Enable(False)
|
||||
self.stDisabledTip.Show()
|
||||
self.Layout()
|
||||
else:
|
||||
self.Enable()
|
||||
self.stDisabledTip.Hide()
|
||||
self.Layout()
|
||||
|
||||
if event is not None:
|
||||
event.Skip()
|
||||
|
||||
@@ -83,9 +83,9 @@ class CharacterSelection(wx.Panel):
|
||||
charList = sChar.getCharacterList()
|
||||
picked = False
|
||||
|
||||
for id, name, active in charList:
|
||||
currId = choice.Append(name, id)
|
||||
if id == activeChar:
|
||||
for char in charList:
|
||||
currId = choice.Append(char.name, char.ID)
|
||||
if char.ID == activeChar:
|
||||
choice.SetSelection(currId)
|
||||
self.charChanged(None)
|
||||
picked = True
|
||||
|
||||
@@ -346,9 +346,8 @@ class GangView ( ScrolledPanel ):
|
||||
choice.Clear()
|
||||
currSelFound = False
|
||||
for char in charList:
|
||||
id,name,_ = char
|
||||
choice.Append(name, id)
|
||||
if chCurrData == id:
|
||||
choice.Append(char.name, char.ID)
|
||||
if chCurrData == char.ID:
|
||||
currSelFound = True
|
||||
|
||||
if chCurrSelection == -1:
|
||||
|
||||
@@ -21,12 +21,62 @@ import wx
|
||||
import service
|
||||
import gui.display as d
|
||||
import gui.marketBrowser as mb
|
||||
import gui.mainFrame
|
||||
from gui.builtinViewColumns.state import State
|
||||
from gui.contextMenu import ContextMenu
|
||||
import globalEvents as GE
|
||||
class ImplantView(d.Display):
|
||||
from eos.types import ImplantLocation
|
||||
|
||||
|
||||
class ImplantView(wx.Panel):
|
||||
def __init__(self, parent):
|
||||
wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL )
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.implantDisplay = ImplantDisplay(self)
|
||||
mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0 )
|
||||
|
||||
radioSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
radioSizer.AddSpacer(( 0, 0), 1, wx.EXPAND, 5)
|
||||
self.rbFit = wx.RadioButton(self, id=wx.ID_ANY, label="Use Fit-specific Implants", style=wx.RB_GROUP)
|
||||
self.rbChar = wx.RadioButton(self, id=wx.ID_ANY, label="Use Character Implants")
|
||||
radioSizer.Add(self.rbFit, 0, wx.ALL, 5)
|
||||
radioSizer.Add(self.rbChar, 0, wx.ALL, 5)
|
||||
radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5)
|
||||
|
||||
mainSizer.Add(radioSizer, 0, wx.EXPAND, 5)
|
||||
|
||||
self.SetSizer( mainSizer )
|
||||
self.SetAutoLayout(True)
|
||||
|
||||
self.Bind(wx.EVT_RADIOBUTTON, self.OnRadioSelect, self.rbFit)
|
||||
self.Bind(wx.EVT_RADIOBUTTON, self.OnRadioSelect, self.rbChar)
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
|
||||
def fitChanged(self, event):
|
||||
sFit = service.Fit.getInstance()
|
||||
activeFitID = self.mainFrame.getActiveFit()
|
||||
fit = sFit.getFit(activeFitID)
|
||||
if fit:
|
||||
if fit.implantSource == ImplantLocation.FIT:
|
||||
self.rbFit.SetValue(True)
|
||||
else:
|
||||
self.rbChar.SetValue(True)
|
||||
|
||||
def OnRadioSelect(self, event):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit = service.Fit.getInstance()
|
||||
sFit.toggleImplantSource(fitID, ImplantLocation.FIT if self.rbFit.GetValue() else ImplantLocation.CHARACTER)
|
||||
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
|
||||
|
||||
|
||||
class ImplantDisplay(d.Display):
|
||||
DEFAULT_COLS = ["State",
|
||||
"attr:implantness",
|
||||
"Base Icon",
|
||||
"Base Name"]
|
||||
|
||||
def __init__(self, parent):
|
||||
@@ -65,7 +115,7 @@ class ImplantView(d.Display):
|
||||
fit = sFit.getFit(event.fitID)
|
||||
|
||||
self.original = fit.implants if fit is not None else None
|
||||
self.implants = stuff = fit.implants if fit is not None else None
|
||||
self.implants = stuff = fit.appliedImplants if fit is not None else None
|
||||
if stuff is not None: stuff.sort(key=lambda implant: implant.slot)
|
||||
|
||||
if event.fitID != self.lastFitId:
|
||||
@@ -78,8 +128,7 @@ class ImplantView(d.Display):
|
||||
|
||||
self.deselectItems()
|
||||
|
||||
self.populate(stuff)
|
||||
self.refresh(stuff)
|
||||
self.update(stuff)
|
||||
event.Skip()
|
||||
|
||||
def addItem(self, event):
|
||||
@@ -123,14 +172,27 @@ class ImplantView(d.Display):
|
||||
|
||||
def spawnMenu(self):
|
||||
sel = self.GetFirstSelected()
|
||||
menu = None
|
||||
|
||||
sFit = service.Fit.getInstance()
|
||||
fit = sFit.getFit(self.mainFrame.getActiveFit())
|
||||
|
||||
if not fit:
|
||||
return
|
||||
|
||||
if sel != -1:
|
||||
sFit = service.Fit.getInstance()
|
||||
fit = sFit.getFit(self.mainFrame.getActiveFit())
|
||||
implant = fit.implants[sel]
|
||||
implant = fit.appliedImplants[sel]
|
||||
|
||||
sMkt = service.Market.getInstance()
|
||||
sourceContext = "implantItem"
|
||||
sourceContext = "implantItem" if fit.implantSource == ImplantLocation.FIT else "implantItemChar"
|
||||
itemContext = sMkt.getCategoryByItem(implant.item).name
|
||||
|
||||
menu = ContextMenu.getMenu((implant,), (sourceContext, itemContext))
|
||||
elif sel == -1 and fit.implantSource == ImplantLocation.FIT:
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return
|
||||
context = (("implantView",),)
|
||||
menu = ContextMenu.getMenu([], *context)
|
||||
if menu is not None:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
@@ -50,6 +50,7 @@ from gui.characterEditor import CharacterEditor, SaveCharacterAs
|
||||
from gui.characterSelection import CharacterSelection
|
||||
from gui.patternEditor import DmgPatternEditorDlg
|
||||
from gui.resistsEditor import ResistsEditorDlg
|
||||
from gui.setEditor import ImplantSetEditorDlg
|
||||
from gui.preferenceDialog import PreferenceDialog
|
||||
from gui.graphFrame import GraphFrame
|
||||
from gui.copySelectDialog import CopySelectDialog
|
||||
@@ -358,15 +359,16 @@ class MainFrame(wx.Frame):
|
||||
dlg.Show()
|
||||
|
||||
def showTargetResistsEditor(self, event):
|
||||
dlg=ResistsEditorDlg(self)
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
ResistsEditorDlg(self)
|
||||
|
||||
def showDamagePatternEditor(self, event):
|
||||
dlg=DmgPatternEditorDlg(self)
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
|
||||
def showImplantSetEditor(self, event):
|
||||
ImplantSetEditorDlg(self)
|
||||
|
||||
def showExportDialog(self, event):
|
||||
""" Export active fit """
|
||||
sFit = service.Fit.getInstance()
|
||||
@@ -418,6 +420,8 @@ class MainFrame(wx.Frame):
|
||||
self.Bind(wx.EVT_MENU, self.showDamagePatternEditor, id=menuBar.damagePatternEditorId)
|
||||
# Target Resists editor
|
||||
self.Bind(wx.EVT_MENU, self.showTargetResistsEditor, id=menuBar.targetResistsEditorId)
|
||||
# Implant Set editor
|
||||
self.Bind(wx.EVT_MENU, self.showImplantSetEditor, id=menuBar.implantSetEditorId)
|
||||
# Import dialog
|
||||
self.Bind(wx.EVT_MENU, self.fileImportDialog, id=wx.ID_OPEN)
|
||||
# Export dialog
|
||||
|
||||
@@ -33,6 +33,7 @@ class MainMenuBar(wx.MenuBar):
|
||||
self.characterEditorId = wx.NewId()
|
||||
self.damagePatternEditorId = wx.NewId()
|
||||
self.targetResistsEditorId = wx.NewId()
|
||||
self.implantSetEditorId = wx.NewId()
|
||||
self.graphFrameId = wx.NewId()
|
||||
self.backupFitsId = wx.NewId()
|
||||
self.exportSkillsNeededId = wx.NewId()
|
||||
@@ -101,9 +102,13 @@ class MainMenuBar(wx.MenuBar):
|
||||
windowMenu.AppendItem(damagePatternEditItem)
|
||||
|
||||
targetResistsEditItem = wx.MenuItem(windowMenu, self.targetResistsEditorId, "Target Resists Editor\tCTRL+R")
|
||||
targetResistsEditItem.SetBitmap(BitmapLoader.getBitmap("explosive_big", "gui"))
|
||||
targetResistsEditItem.SetBitmap(BitmapLoader.getBitmap("explosive_small", "gui"))
|
||||
windowMenu.AppendItem(targetResistsEditItem)
|
||||
|
||||
implantSetEditItem = wx.MenuItem(windowMenu, self.implantSetEditorId, "Implant Set Editor\tCTRL+I")
|
||||
implantSetEditItem.SetBitmap(BitmapLoader.getBitmap("hardwire_small", "gui"))
|
||||
windowMenu.AppendItem(implantSetEditItem)
|
||||
|
||||
graphFrameItem = wx.MenuItem(windowMenu, self.graphFrameId, "Graphs\tCTRL+G")
|
||||
graphFrameItem.SetBitmap(BitmapLoader.getBitmap("graphs_small", "gui"))
|
||||
windowMenu.AppendItem(graphFrameItem)
|
||||
|
||||
@@ -23,11 +23,64 @@ import service
|
||||
from wx.lib.intctrl import IntCtrl
|
||||
from gui.utils.clipboard import toClipboard, fromClipboard
|
||||
from service.damagePattern import ImportError
|
||||
|
||||
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
|
||||
###########################################################################
|
||||
## Class DmgPatternEditorDlg
|
||||
###########################################################################
|
||||
|
||||
class DmgPatternTextValidor(BaseValidator):
|
||||
def __init__(self):
|
||||
BaseValidator.__init__(self)
|
||||
|
||||
def Clone(self):
|
||||
return DmgPatternTextValidor()
|
||||
|
||||
def Validate(self, win):
|
||||
profileEditor = win.Parent
|
||||
textCtrl = self.GetWindow()
|
||||
text = textCtrl.GetValue().strip()
|
||||
|
||||
try:
|
||||
if len(text) == 0:
|
||||
raise ValueError("You must supply a name for your Damage Profile!")
|
||||
elif text in [x.name for x in profileEditor.entityEditor.choices]:
|
||||
raise ValueError("Damage Profile name already in use, please choose another.")
|
||||
|
||||
return True
|
||||
except ValueError, e:
|
||||
wx.MessageBox(u"{}".format(e), "Error")
|
||||
textCtrl.SetFocus()
|
||||
return False
|
||||
|
||||
|
||||
class DmgPatternEntityEditor(EntityEditor):
|
||||
def __init__(self, parent):
|
||||
EntityEditor.__init__(self, parent, "Damage Profile")
|
||||
self.SetEditorValidator(DmgPatternTextValidor)
|
||||
|
||||
def getEntitiesFromContext(self):
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
choices = sorted(sDP.getDamagePatternList(), key=lambda p: p.name)
|
||||
return [c for c in choices if c.name != "Selected Ammo"]
|
||||
|
||||
def DoNew(self, name):
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
return sDP.newPattern(name)
|
||||
|
||||
def DoRename(self, entity, name):
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
sDP.renamePattern(entity, name)
|
||||
|
||||
def DoCopy(self, entity, name):
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
copy = sDP.copyPattern(entity)
|
||||
sDP.renamePattern(copy, name)
|
||||
return copy
|
||||
|
||||
def DoDelete(self, entity):
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
sDP.deletePattern(entity)
|
||||
|
||||
class DmgPatternEditorDlg(wx.Dialog):
|
||||
DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive")
|
||||
|
||||
@@ -39,52 +92,8 @@ class DmgPatternEditorDlg(wx.Dialog):
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.headerSizer = headerSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
|
||||
self.choices = sDP.getDamagePatternList()
|
||||
# Remove "Selected Ammo" Damage Pattern
|
||||
for dp in self.choices:
|
||||
if dp.name == "Selected Ammo":
|
||||
self.choices.remove(dp)
|
||||
# Sort the remaining list and continue on
|
||||
self.choices.sort(key=lambda p: p.name)
|
||||
self.ccDmgPattern = wx.Choice(self, choices=map(lambda p: p.name, self.choices))
|
||||
self.ccDmgPattern.Bind(wx.EVT_CHOICE, self.patternChanged)
|
||||
self.ccDmgPattern.SetSelection(0)
|
||||
|
||||
self.namePicker = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
|
||||
self.namePicker.Bind(wx.EVT_TEXT_ENTER, self.processRename)
|
||||
self.namePicker.Hide()
|
||||
|
||||
size = None
|
||||
headerSizer.Add(self.ccDmgPattern, 1, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT|wx.LEFT, 3)
|
||||
buttons = (("new", wx.ART_NEW),
|
||||
("rename", BitmapLoader.getBitmap("rename", "gui")),
|
||||
("copy", wx.ART_COPY),
|
||||
("delete", wx.ART_DELETE))
|
||||
for name, art in buttons:
|
||||
bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art
|
||||
btn = wx.BitmapButton(self, wx.ID_ANY, bitmap)
|
||||
if size is None:
|
||||
size = btn.GetSize()
|
||||
|
||||
btn.SetMinSize(size)
|
||||
btn.SetMaxSize(size)
|
||||
|
||||
btn.Layout()
|
||||
setattr(self, name, btn)
|
||||
btn.Enable(True)
|
||||
btn.SetToolTipString("%s pattern" % name.capitalize())
|
||||
headerSizer.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.btnSave = wx.Button(self, wx.ID_SAVE)
|
||||
self.btnSave.Hide()
|
||||
self.btnSave.Bind(wx.EVT_BUTTON, self.processRename)
|
||||
self.headerSizer.Add(self.btnSave, 0, wx.ALIGN_CENTER)
|
||||
|
||||
mainSizer.Add(headerSizer, 0, wx.EXPAND | wx.ALL, 2)
|
||||
self.entityEditor = DmgPatternEntityEditor(self)
|
||||
mainSizer.Add(self.entityEditor, 0, wx.ALL | wx.EXPAND, 2)
|
||||
|
||||
self.sl = wx.StaticLine(self)
|
||||
mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
@@ -108,7 +117,7 @@ class DmgPatternEditorDlg(wx.Dialog):
|
||||
bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big"%type, "gui"))
|
||||
if i%2:
|
||||
style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT
|
||||
border = 10
|
||||
border = 20
|
||||
else:
|
||||
style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT
|
||||
border = 5
|
||||
@@ -155,6 +164,7 @@ class DmgPatternEditorDlg(wx.Dialog):
|
||||
|
||||
importExport = (("Import", wx.ART_FILE_OPEN, "from"),
|
||||
("Export", wx.ART_FILE_SAVE_AS, "to"))
|
||||
|
||||
for name, art, direction in importExport:
|
||||
bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON)
|
||||
btn = wx.BitmapButton(self, wx.ID_ANY, bitmap)
|
||||
@@ -170,14 +180,10 @@ class DmgPatternEditorDlg(wx.Dialog):
|
||||
|
||||
self.Layout()
|
||||
bsize = self.GetBestSize()
|
||||
self.SetSize((-1,bsize.height))
|
||||
self.SetSize((-1, bsize.height))
|
||||
self.CenterOnParent()
|
||||
|
||||
self.new.Bind(wx.EVT_BUTTON, self.newPattern)
|
||||
self.rename.Bind(wx.EVT_BUTTON, self.renamePattern)
|
||||
self.copy.Bind(wx.EVT_BUTTON, self.copyPattern)
|
||||
self.delete.Bind(wx.EVT_BUTTON, self.deletePattern)
|
||||
self.Import.Bind(wx.EVT_BUTTON, self.importPatterns)
|
||||
self.Export.Bind(wx.EVT_BUTTON, self.exportPatterns)
|
||||
self.Bind(wx.EVT_CHOICE, self.patternChanged)
|
||||
|
||||
self.patternChanged()
|
||||
|
||||
@@ -188,7 +194,7 @@ class DmgPatternEditorDlg(wx.Dialog):
|
||||
if self.block:
|
||||
return
|
||||
|
||||
p = self.getActivePattern()
|
||||
p = self.entityEditor.getActiveEntity()
|
||||
total = sum(map(lambda attr: getattr(self, "%sEdit"%attr).GetValue(), self.DAMAGE_TYPES))
|
||||
for type in self.DAMAGE_TYPES:
|
||||
editObj = getattr(self, "%sEdit"%type)
|
||||
@@ -207,24 +213,18 @@ class DmgPatternEditorDlg(wx.Dialog):
|
||||
for type in self.DAMAGE_TYPES:
|
||||
editObj = getattr(self, "%sEdit"%type)
|
||||
editObj.Enable(False)
|
||||
self.rename.Enable(False)
|
||||
self.delete.Enable(False)
|
||||
self.entityEditor.btnRename.Enable(False)
|
||||
self.entityEditor.btnDelete.Enable(False)
|
||||
|
||||
def unrestrict(self):
|
||||
for type in self.DAMAGE_TYPES:
|
||||
editObj = getattr(self, "%sEdit"%type)
|
||||
editObj.Enable()
|
||||
self.rename.Enable()
|
||||
self.delete.Enable()
|
||||
|
||||
def getActivePattern(self):
|
||||
if len(self.choices) == 0:
|
||||
return None
|
||||
|
||||
return self.choices[self.ccDmgPattern.GetSelection()]
|
||||
self.entityEditor.btnRename.Enable()
|
||||
self.entityEditor.btnDelete.Enable()
|
||||
|
||||
def patternChanged(self, event=None):
|
||||
p = self.getActivePattern()
|
||||
p = self.entityEditor.getActiveEntity()
|
||||
|
||||
if p is None:
|
||||
return
|
||||
@@ -244,126 +244,9 @@ class DmgPatternEditorDlg(wx.Dialog):
|
||||
self.block = False
|
||||
self.ValuesUpdated()
|
||||
|
||||
def newPattern(self, event):
|
||||
self.restrict()
|
||||
|
||||
self.block = True
|
||||
# reset values
|
||||
for type in self.DAMAGE_TYPES:
|
||||
editObj = getattr(self, "%sEdit"%type)
|
||||
editObj.SetValue(0)
|
||||
|
||||
self.block = False
|
||||
|
||||
self.btnSave.SetLabel("Create")
|
||||
self.Refresh()
|
||||
self.renamePattern()
|
||||
|
||||
def renamePattern(self, event=None):
|
||||
if event is not None:
|
||||
self.btnSave.SetLabel("Rename")
|
||||
|
||||
self.ccDmgPattern.Hide()
|
||||
self.namePicker.Show()
|
||||
self.headerSizer.Replace(self.ccDmgPattern, self.namePicker)
|
||||
self.namePicker.SetFocus()
|
||||
|
||||
if event is not None: # Rename mode
|
||||
self.btnSave.SetLabel("Rename")
|
||||
self.namePicker.SetValue(self.getActivePattern().name)
|
||||
else: # Create mode
|
||||
self.namePicker.SetValue("")
|
||||
|
||||
for btn in (self.new, self.rename, self.delete, self.copy):
|
||||
btn.Hide()
|
||||
|
||||
self.btnSave.Show()
|
||||
self.headerSizer.Layout()
|
||||
if event is not None:
|
||||
event.Skip()
|
||||
|
||||
def processRename(self, event):
|
||||
newName = self.namePicker.GetLineText(0)
|
||||
self.stNotice.SetLabel("")
|
||||
|
||||
if newName == "":
|
||||
self.stNotice.SetLabel("Invalid name.")
|
||||
return
|
||||
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
if self.btnSave.Label == "Create":
|
||||
p = sDP.newPattern()
|
||||
else:
|
||||
# we are renaming, so get the current selection
|
||||
p = self.getActivePattern()
|
||||
|
||||
for pattern in self.choices:
|
||||
if pattern.name == newName and p != pattern:
|
||||
self.stNotice.SetLabel("Name already used, please choose another")
|
||||
return
|
||||
|
||||
sDP.renamePattern(p, newName)
|
||||
|
||||
self.updateChoices(newName)
|
||||
self.headerSizer.Replace(self.namePicker, self.ccDmgPattern)
|
||||
self.ccDmgPattern.Show()
|
||||
self.namePicker.Hide()
|
||||
self.btnSave.Hide()
|
||||
for btn in (self.new, self.rename, self.delete, self.copy):
|
||||
btn.Show()
|
||||
|
||||
sel = self.ccDmgPattern.GetSelection()
|
||||
self.ccDmgPattern.Delete(sel)
|
||||
self.ccDmgPattern.Insert(newName, sel)
|
||||
self.ccDmgPattern.SetSelection(sel)
|
||||
self.ValuesUpdated()
|
||||
self.unrestrict()
|
||||
|
||||
def copyPattern(self,event):
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
p = sDP.copyPattern(self.getActivePattern())
|
||||
self.choices.append(p)
|
||||
id = self.ccDmgPattern.Append(p.name)
|
||||
self.ccDmgPattern.SetSelection(id)
|
||||
self.btnSave.SetLabel("Copy")
|
||||
self.renamePattern()
|
||||
self.patternChanged()
|
||||
|
||||
def deletePattern(self,event):
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
sel = self.ccDmgPattern.GetSelection()
|
||||
sDP.deletePattern(self.getActivePattern())
|
||||
self.ccDmgPattern.Delete(sel)
|
||||
self.ccDmgPattern.SetSelection(max(0, sel - 1))
|
||||
del self.choices[sel]
|
||||
self.patternChanged()
|
||||
|
||||
def __del__( self ):
|
||||
def __del__(self):
|
||||
pass
|
||||
|
||||
def updateChoices(self, select=None):
|
||||
"Gathers list of patterns and updates choice selections"
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
self.choices = sDP.getDamagePatternList()
|
||||
|
||||
for dp in self.choices:
|
||||
if dp.name == "Selected Ammo": # don't include this special butterfly
|
||||
self.choices.remove(dp)
|
||||
|
||||
# Sort the remaining list and continue on
|
||||
self.choices.sort(key=lambda p: p.name)
|
||||
self.ccDmgPattern.Clear()
|
||||
|
||||
for i, choice in enumerate(map(lambda p: p.name, self.choices)):
|
||||
self.ccDmgPattern.Append(choice)
|
||||
|
||||
if select is not None and choice == select:
|
||||
self.ccDmgPattern.SetSelection(i)
|
||||
|
||||
if select is None:
|
||||
self.ccDmgPattern.SetSelection(0)
|
||||
self.patternChanged()
|
||||
|
||||
def importPatterns(self, event):
|
||||
text = fromClipboard()
|
||||
if text:
|
||||
@@ -384,3 +267,6 @@ class DmgPatternEditorDlg(wx.Dialog):
|
||||
sDP = service.DamagePattern.getInstance()
|
||||
toClipboard( sDP.exportPatterns() )
|
||||
self.stNotice.SetLabel("Patterns exported to clipboard")
|
||||
|
||||
def contextChanged(self, event):
|
||||
print "lol"
|
||||
@@ -22,6 +22,61 @@ from gui.bitmapLoader import BitmapLoader
|
||||
import service
|
||||
from gui.utils.clipboard import toClipboard, fromClipboard
|
||||
from service.targetResists import ImportError
|
||||
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
|
||||
|
||||
|
||||
class TargetResistsTextValidor(BaseValidator):
|
||||
def __init__(self):
|
||||
BaseValidator.__init__(self)
|
||||
|
||||
def Clone(self):
|
||||
return TargetResistsTextValidor()
|
||||
|
||||
def Validate(self, win):
|
||||
profileEditor = win.parent.Parent
|
||||
textCtrl = self.GetWindow()
|
||||
text = textCtrl.GetValue().strip()
|
||||
|
||||
try:
|
||||
if len(text) == 0:
|
||||
raise ValueError("You must supply a name for your Target Resist Profile!")
|
||||
elif text in [x.name for x in profileEditor.entityEditor.choices]:
|
||||
raise ValueError("Target Resist Profile name already in use, please choose another.")
|
||||
|
||||
return True
|
||||
except ValueError, e:
|
||||
wx.MessageBox(u"{}".format(e), "Error")
|
||||
textCtrl.SetFocus()
|
||||
return False
|
||||
|
||||
|
||||
class TargetResistsEntityEditor(EntityEditor):
|
||||
def __init__(self, parent):
|
||||
EntityEditor.__init__(self, parent, "Target Resist Profile")
|
||||
self.SetEditorValidator(TargetResistsTextValidor)
|
||||
|
||||
def getEntitiesFromContext(self):
|
||||
sTR = service.TargetResists.getInstance()
|
||||
choices = sorted(sTR.getTargetResistsList(), key=lambda p: p.name)
|
||||
return choices
|
||||
|
||||
def DoNew(self, name):
|
||||
sTR = service.TargetResists.getInstance()
|
||||
return sTR.newPattern(name)
|
||||
|
||||
def DoRename(self, entity, name):
|
||||
sTR = service.TargetResists.getInstance()
|
||||
sTR.renamePattern(entity, name)
|
||||
|
||||
def DoCopy(self, entity, name):
|
||||
sTR = service.TargetResists.getInstance()
|
||||
copy = sTR.copyPattern(entity)
|
||||
sTR.renamePattern(copy, name)
|
||||
return copy
|
||||
|
||||
def DoDelete(self, entity):
|
||||
sTR = service.TargetResists.getInstance()
|
||||
sTR.deletePattern(entity)
|
||||
|
||||
class ResistsEditorDlg(wx.Dialog):
|
||||
|
||||
@@ -35,51 +90,8 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.headerSizer = headerSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
sTR = service.TargetResists.getInstance()
|
||||
|
||||
self.choices = sTR.getTargetResistsList()
|
||||
|
||||
# Sort the remaining list and continue on
|
||||
self.choices.sort(key=lambda p: p.name)
|
||||
self.ccResists = wx.Choice(self, choices=map(lambda p: p.name, self.choices))
|
||||
self.ccResists.Bind(wx.EVT_CHOICE, self.patternChanged)
|
||||
self.ccResists.SetSelection(0)
|
||||
|
||||
self.namePicker = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER)
|
||||
self.namePicker.Bind(wx.EVT_TEXT_ENTER, self.processRename)
|
||||
self.namePicker.Hide()
|
||||
|
||||
size = None
|
||||
headerSizer.Add(self.ccResists, 1, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 3)
|
||||
|
||||
buttons = (("new", wx.ART_NEW),
|
||||
("rename", BitmapLoader.getBitmap("rename", "gui")),
|
||||
("copy", wx.ART_COPY),
|
||||
("delete", wx.ART_DELETE))
|
||||
for name, art in buttons:
|
||||
bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art
|
||||
btn = wx.BitmapButton(self, wx.ID_ANY, bitmap)
|
||||
if size is None:
|
||||
size = btn.GetSize()
|
||||
|
||||
btn.SetMinSize(size)
|
||||
btn.SetMaxSize(size)
|
||||
|
||||
btn.Layout()
|
||||
setattr(self, name, btn)
|
||||
btn.Enable(True)
|
||||
btn.SetToolTipString("%s resist profile" % name.capitalize())
|
||||
headerSizer.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
|
||||
self.btnSave = wx.Button(self, wx.ID_SAVE)
|
||||
self.btnSave.Hide()
|
||||
self.btnSave.Bind(wx.EVT_BUTTON, self.processRename)
|
||||
headerSizer.Add(self.btnSave, 0, wx.ALIGN_CENTER)
|
||||
|
||||
mainSizer.Add(headerSizer, 0, wx.EXPAND | wx.ALL, 2)
|
||||
self.entityEditor = TargetResistsEntityEditor(self)
|
||||
mainSizer.Add(self.entityEditor, 0, wx.ALL | wx.EXPAND, 2)
|
||||
|
||||
self.sl = wx.StaticLine(self)
|
||||
mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
@@ -157,19 +169,21 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
btn.SetToolTipString("%s patterns %s clipboard" % (name, direction) )
|
||||
footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT)
|
||||
|
||||
if not self.entityEditor.checkEntitiesExist():
|
||||
self.Destroy()
|
||||
return
|
||||
|
||||
self.Layout()
|
||||
bsize = self.GetBestSize()
|
||||
self.SetSize((-1,bsize.height))
|
||||
self.SetSize((-1, bsize.height))
|
||||
self.CenterOnParent()
|
||||
|
||||
self.new.Bind(wx.EVT_BUTTON, self.newPattern)
|
||||
self.rename.Bind(wx.EVT_BUTTON, self.renamePattern)
|
||||
self.copy.Bind(wx.EVT_BUTTON, self.copyPattern)
|
||||
self.delete.Bind(wx.EVT_BUTTON, self.deletePattern)
|
||||
self.Import.Bind(wx.EVT_BUTTON, self.importPatterns)
|
||||
self.Export.Bind(wx.EVT_BUTTON, self.exportPatterns)
|
||||
self.Bind(wx.EVT_CHOICE, self.patternChanged)
|
||||
|
||||
self.patternChanged()
|
||||
|
||||
self.ShowModal()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.Destroy()
|
||||
|
||||
@@ -184,7 +198,7 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
return
|
||||
|
||||
try:
|
||||
p = self.getActivePattern()
|
||||
p = self.entityEditor.getActiveEntity()
|
||||
|
||||
for type in self.DAMAGE_TYPES:
|
||||
editObj = getattr(self, "%sEdit"%type)
|
||||
@@ -220,33 +234,15 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
finally: # Refresh for color changes to take effect immediately
|
||||
self.Refresh()
|
||||
|
||||
def restrict(self):
|
||||
for type in self.DAMAGE_TYPES:
|
||||
editObj = getattr(self, "%sEdit"%type)
|
||||
editObj.Enable(False)
|
||||
self.rename.Enable(False)
|
||||
self.delete.Enable(False)
|
||||
|
||||
def unrestrict(self):
|
||||
for type in self.DAMAGE_TYPES:
|
||||
editObj = getattr(self, "%sEdit"%type)
|
||||
editObj.Enable()
|
||||
self.rename.Enable()
|
||||
self.delete.Enable()
|
||||
|
||||
def getActivePattern(self):
|
||||
if len(self.choices) == 0:
|
||||
return None
|
||||
|
||||
return self.choices[self.ccResists.GetSelection()]
|
||||
|
||||
def patternChanged(self, event=None):
|
||||
"Event fired when user selects pattern. Can also be called from script"
|
||||
p = self.getActivePattern()
|
||||
|
||||
if not self.entityEditor.checkEntitiesExist():
|
||||
self.Destroy()
|
||||
return
|
||||
|
||||
p = self.entityEditor.getActiveEntity()
|
||||
if p is None:
|
||||
# This happens when there are no patterns in the DB. As such, force
|
||||
# user to create one first or exit dlg.
|
||||
self.newPattern(None)
|
||||
return
|
||||
|
||||
self.block = True
|
||||
@@ -259,142 +255,9 @@ class ResistsEditorDlg(wx.Dialog):
|
||||
self.block = False
|
||||
self.ValuesUpdated()
|
||||
|
||||
def newPattern(self, event):
|
||||
'''
|
||||
Simply does new-pattern specifics: replaces label on button, restricts,
|
||||
and resets values to default. Hands off to the rename function for
|
||||
further handling.
|
||||
'''
|
||||
self.btnSave.SetLabel("Create")
|
||||
self.restrict()
|
||||
# reset values
|
||||
for type in self.DAMAGE_TYPES:
|
||||
editObj = getattr(self, "%sEdit"%type)
|
||||
editObj.ChangeValue("0.0")
|
||||
editObj.SetForegroundColour(self.colorReset)
|
||||
|
||||
self.Refresh()
|
||||
self.renamePattern()
|
||||
|
||||
def renamePattern(self, event=None):
|
||||
"Changes layout to facilitate naming a pattern"
|
||||
|
||||
self.showInput(True)
|
||||
|
||||
if event is not None: # Rename mode
|
||||
self.btnSave.SetLabel("Rename")
|
||||
self.namePicker.SetValue(self.getActivePattern().name)
|
||||
else: # Create mode
|
||||
self.namePicker.SetValue("")
|
||||
|
||||
if event is not None:
|
||||
event.Skip()
|
||||
|
||||
def processRename(self, event):
|
||||
'''
|
||||
Processes rename event (which can be new or old patterns). If new
|
||||
pattern, creates it; if old, selects it. if checks are valid, rename
|
||||
saves pattern to DB.
|
||||
|
||||
Also resets to default layout and unrestricts.
|
||||
'''
|
||||
newName = self.namePicker.GetLineText(0)
|
||||
self.stNotice.SetLabel("")
|
||||
|
||||
if newName == "":
|
||||
self.stNotice.SetLabel("Invalid name")
|
||||
return
|
||||
|
||||
sTR = service.TargetResists.getInstance()
|
||||
if self.btnSave.Label == "Create":
|
||||
p = sTR.newPattern()
|
||||
else:
|
||||
# we are renaming, so get the current selection
|
||||
p = self.getActivePattern()
|
||||
|
||||
# test for patterns of the same name
|
||||
for pattern in self.choices:
|
||||
if pattern.name == newName and p != pattern:
|
||||
self.stNotice.SetLabel("Name already used, please choose another")
|
||||
return
|
||||
|
||||
# rename regardless of new or rename
|
||||
sTR.renamePattern(p, newName)
|
||||
|
||||
self.updateChoices(newName)
|
||||
self.showInput(False)
|
||||
sel = self.ccResists.GetSelection()
|
||||
self.ValuesUpdated()
|
||||
self.unrestrict()
|
||||
|
||||
def copyPattern(self,event):
|
||||
sTR = service.TargetResists.getInstance()
|
||||
p = sTR.copyPattern(self.getActivePattern())
|
||||
self.choices.append(p)
|
||||
id = self.ccResists.Append(p.name)
|
||||
self.ccResists.SetSelection(id)
|
||||
self.btnSave.SetLabel("Copy")
|
||||
self.renamePattern()
|
||||
self.patternChanged()
|
||||
|
||||
def deletePattern(self,event):
|
||||
sTR = service.TargetResists.getInstance()
|
||||
sel = self.ccResists.GetSelection()
|
||||
sTR.deletePattern(self.getActivePattern())
|
||||
self.ccResists.Delete(sel)
|
||||
self.ccResists.SetSelection(max(0, sel - 1))
|
||||
del self.choices[sel]
|
||||
self.patternChanged()
|
||||
|
||||
def showInput(self, bool):
|
||||
if bool and not self.namePicker.IsShown():
|
||||
self.ccResists.Hide()
|
||||
self.namePicker.Show()
|
||||
self.headerSizer.Replace(self.ccResists, self.namePicker)
|
||||
self.namePicker.SetFocus()
|
||||
for btn in (self.new, self.rename, self.delete, self.copy):
|
||||
btn.Hide()
|
||||
self.btnSave.Show()
|
||||
self.restrict()
|
||||
self.headerSizer.Layout()
|
||||
elif not bool and self.namePicker.IsShown():
|
||||
self.headerSizer.Replace(self.namePicker, self.ccResists)
|
||||
self.ccResists.Show()
|
||||
self.namePicker.Hide()
|
||||
self.btnSave.Hide()
|
||||
for btn in (self.new, self.rename, self.delete, self.copy):
|
||||
btn.Show()
|
||||
self.unrestrict()
|
||||
self.headerSizer.Layout()
|
||||
|
||||
|
||||
def __del__( self ):
|
||||
pass
|
||||
|
||||
def updateChoices(self, select=None):
|
||||
"Gathers list of patterns and updates choice selections"
|
||||
sTR = service.TargetResists.getInstance()
|
||||
self.choices = sTR.getTargetResistsList()
|
||||
|
||||
if len(self.choices) == 0:
|
||||
#self.newPattern(None)
|
||||
return
|
||||
|
||||
# Sort the remaining list and continue on
|
||||
self.choices.sort(key=lambda p: p.name)
|
||||
self.ccResists.Clear()
|
||||
|
||||
for i, choice in enumerate(map(lambda p: p.name, self.choices)):
|
||||
self.ccResists.Append(choice)
|
||||
|
||||
if select is not None and choice == select:
|
||||
self.ccResists.SetSelection(i)
|
||||
|
||||
if select is None:
|
||||
self.ccResists.SetSelection(0)
|
||||
|
||||
self.patternChanged()
|
||||
|
||||
def importPatterns(self, event):
|
||||
"Event fired when import from clipboard button is clicked"
|
||||
|
||||
|
||||
214
gui/setEditor.py
Normal file
214
gui/setEditor.py
Normal file
@@ -0,0 +1,214 @@
|
||||
#===============================================================================
|
||||
# Copyright (C) 2016 Ryan Holmes
|
||||
#
|
||||
# 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/>.
|
||||
#===============================================================================
|
||||
|
||||
import wx
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from gui.builtinViews.implantEditor import BaseImplantEditorView
|
||||
import service
|
||||
from gui.utils.clipboard import toClipboard, fromClipboard
|
||||
from service.implantSet import ImportError
|
||||
import logging
|
||||
from gui.builtinViews.entityEditor import EntityEditor, BaseValidator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ImplantTextValidor(BaseValidator):
|
||||
def __init__(self):
|
||||
BaseValidator.__init__(self)
|
||||
|
||||
def Clone(self):
|
||||
return ImplantTextValidor()
|
||||
|
||||
def Validate(self, win):
|
||||
profileEditor = win.parent.Parent
|
||||
textCtrl = self.GetWindow()
|
||||
text = textCtrl.GetValue().strip()
|
||||
|
||||
try:
|
||||
if len(text) == 0:
|
||||
raise ValueError("You must supply a name for the Implant Set!")
|
||||
elif text in [x.name for x in profileEditor.entityEditor.choices]:
|
||||
raise ValueError("Imlplant Set name already in use, please choose another.")
|
||||
|
||||
return True
|
||||
except ValueError, e:
|
||||
wx.MessageBox(u"{}".format(e), "Error")
|
||||
textCtrl.SetFocus()
|
||||
return False
|
||||
|
||||
|
||||
class ImplantSetEntityEditor(EntityEditor):
|
||||
def __init__(self, parent):
|
||||
EntityEditor.__init__(self, parent, "Implant Set")
|
||||
self.SetEditorValidator(ImplantTextValidor)
|
||||
|
||||
def getEntitiesFromContext(self):
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
return sorted(sIS.getImplantSetList(), key=lambda c: c.name)
|
||||
|
||||
def DoNew(self, name):
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
return sIS.newSet(name)
|
||||
|
||||
def DoRename(self, entity, name):
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
sIS.renameSet(entity, name)
|
||||
|
||||
def DoCopy(self, entity, name):
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
copy = sIS.copySet(entity)
|
||||
sIS.renameSet(copy, name)
|
||||
return copy
|
||||
|
||||
def DoDelete(self, entity):
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
sIS.deleteSet(entity)
|
||||
|
||||
|
||||
class ImplantSetEditor(BaseImplantEditorView):
|
||||
def __init__(self, parent):
|
||||
BaseImplantEditorView.__init__(self, parent)
|
||||
if 'wxMSW' in wx.PlatformInfo:
|
||||
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))
|
||||
|
||||
def bindContext(self):
|
||||
self.Parent.entityEditor.Bind(wx.EVT_CHOICE, self.contextChanged)
|
||||
|
||||
def getImplantsFromContext(self):
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
set = self.Parent.entityEditor.getActiveEntity()
|
||||
if set:
|
||||
return sIS.getImplants(set.ID)
|
||||
return []
|
||||
|
||||
def addImplantToContext(self, item):
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
set = self.Parent.entityEditor.getActiveEntity()
|
||||
|
||||
sIS.addImplant(set.ID, item.ID)
|
||||
|
||||
def removeImplantFromContext(self, implant):
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
set = self.Parent.entityEditor.getActiveEntity()
|
||||
|
||||
sIS.removeImplant(set.ID, implant)
|
||||
|
||||
class ImplantSetEditorDlg(wx.Dialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Implant Set Editor", size = wx.Size(640, 600))
|
||||
|
||||
self.block = False
|
||||
self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize)
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.entityEditor = ImplantSetEntityEditor(self)
|
||||
mainSizer.Add(self.entityEditor, 0, wx.ALL | wx.EXPAND, 2)
|
||||
|
||||
self.sl = wx.StaticLine(self)
|
||||
mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
|
||||
|
||||
self.iview = ImplantSetEditor(self)
|
||||
mainSizer.Add(self.iview, 1, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.slfooter = wx.StaticLine(self)
|
||||
mainSizer.Add(self.slfooter, 0, wx.EXPAND | wx.TOP, 5)
|
||||
|
||||
footerSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.stNotice = wx.StaticText(self, wx.ID_ANY, u"")
|
||||
self.stNotice.Wrap(-1)
|
||||
footerSizer.Add(self.stNotice, 1, wx.BOTTOM | wx.TOP | wx.LEFT, 5)
|
||||
|
||||
if "wxGTK" in wx.PlatformInfo:
|
||||
self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 )
|
||||
self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent)
|
||||
|
||||
importExport = (("Import", wx.ART_FILE_OPEN, "from"),
|
||||
("Export", wx.ART_FILE_SAVE_AS, "to"))
|
||||
|
||||
for name, art, direction in importExport:
|
||||
bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON)
|
||||
btn = wx.BitmapButton(self, wx.ID_ANY, bitmap)
|
||||
|
||||
btn.SetMinSize( btn.GetSize() )
|
||||
btn.SetMaxSize( btn.GetSize() )
|
||||
|
||||
btn.Layout()
|
||||
setattr(self, name, btn)
|
||||
btn.Enable(True)
|
||||
btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction) )
|
||||
footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT)
|
||||
|
||||
mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
self.SetSizer(mainSizer)
|
||||
self.Layout()
|
||||
|
||||
if not self.entityEditor.checkEntitiesExist():
|
||||
self.Destroy()
|
||||
return
|
||||
|
||||
self.Bind(wx.EVT_CHOICE, self.entityChanged)
|
||||
|
||||
self.Import.Bind(wx.EVT_BUTTON, self.importPatterns)
|
||||
self.Export.Bind(wx.EVT_BUTTON, self.exportPatterns)
|
||||
|
||||
self.CenterOnParent()
|
||||
self.ShowModal()
|
||||
|
||||
def entityChanged(self, event):
|
||||
if not self.entityEditor.checkEntitiesExist():
|
||||
self.Destroy()
|
||||
return
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.Destroy()
|
||||
|
||||
def __del__( self ):
|
||||
pass
|
||||
|
||||
def importPatterns(self, event):
|
||||
"Event fired when import from clipboard button is clicked"
|
||||
|
||||
text = fromClipboard()
|
||||
if text:
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
try:
|
||||
sIS.importSets(text)
|
||||
self.stNotice.SetLabel("Patterns successfully imported from clipboard")
|
||||
self.showInput(False)
|
||||
except ImportError, e:
|
||||
self.stNotice.SetLabel(str(e))
|
||||
except Exception, e:
|
||||
logging.exception("Unhandled Exception")
|
||||
self.stNotice.SetLabel("Could not import from clipboard: unknown errors")
|
||||
finally:
|
||||
self.updateChoices()
|
||||
else:
|
||||
self.stNotice.SetLabel("Could not import from clipboard")
|
||||
|
||||
def exportPatterns(self, event):
|
||||
"Event fired when export to clipboard button is clicked"
|
||||
|
||||
sIS = service.ImplantSets.getInstance()
|
||||
toClipboard(sIS.exportSets())
|
||||
self.stNotice.SetLabel("Sets exported to clipboard")
|
||||
BIN
imgs/gui/hardwire_small.png
Normal file
BIN
imgs/gui/hardwire_small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 762 B |
@@ -10,6 +10,7 @@ from service.update import Update
|
||||
from service.price import Price
|
||||
from service.network import Network
|
||||
from service.eveapi import EVEAPIConnection, ParseXML
|
||||
from service.implantSet import ImplantSets
|
||||
|
||||
import wx
|
||||
if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)):
|
||||
|
||||
@@ -32,7 +32,9 @@ import eos.db
|
||||
import eos.types
|
||||
import service
|
||||
import config
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class CharacterImportThread(threading.Thread):
|
||||
def __init__(self, paths, callback):
|
||||
@@ -48,9 +50,8 @@ class CharacterImportThread(threading.Thread):
|
||||
# we try to parse api XML data first
|
||||
with open(path, mode='r') as charFile:
|
||||
sheet = service.ParseXML(charFile)
|
||||
charID = sCharacter.new()
|
||||
sCharacter.rename(charID, sheet.name+" (imported)")
|
||||
sCharacter.apiUpdateCharSheet(charID, sheet.skills)
|
||||
char = sCharacter.new(sheet.name+" (imported)")
|
||||
sCharacter.apiUpdateCharSheet(char.ID, sheet.skills)
|
||||
except:
|
||||
# if it's not api XML data, try this
|
||||
# this is a horrible logic flow, but whatever
|
||||
@@ -67,9 +68,8 @@ class CharacterImportThread(threading.Thread):
|
||||
"typeID": int(skill.getAttribute("typeID")),
|
||||
"level": int(skill.getAttribute("level")),
|
||||
})
|
||||
charID = sCharacter.new()
|
||||
sCharacter.rename(charID, name+" (EVEMon)")
|
||||
sCharacter.apiUpdateCharSheet(charID, skills)
|
||||
char = sCharacter.new(name+" (EVEMon)")
|
||||
sCharacter.apiUpdateCharSheet(char.ID, skills)
|
||||
except:
|
||||
continue
|
||||
|
||||
@@ -114,6 +114,11 @@ class Character(object):
|
||||
|
||||
return cls.instance
|
||||
|
||||
def __init__(self):
|
||||
# Simply initializes default characters in case they aren't in the database yet
|
||||
self.all0()
|
||||
self.all5()
|
||||
|
||||
def exportText(self):
|
||||
data = "Pyfa exported plan for \""+self.skillReqsDict['charname']+"\"\n"
|
||||
data += "=" * 79 + "\n"
|
||||
@@ -183,10 +188,7 @@ class Character(object):
|
||||
return self.all5().ID
|
||||
|
||||
def getCharacterList(self):
|
||||
baseChars = [eos.types.Character.getAll0(), eos.types.Character.getAll5()]
|
||||
sFit = service.Fit.getInstance()
|
||||
|
||||
return map(lambda c: (c.ID, c.name if not c.isDirty else "{} *".format(c.name), c == sFit.character), eos.db.getCharacterList())
|
||||
return eos.db.getCharacterList()
|
||||
|
||||
def getCharacter(self, charID):
|
||||
char = eos.db.getCharacter(charID)
|
||||
@@ -246,25 +248,21 @@ class Character(object):
|
||||
def getCharName(self, charID):
|
||||
return eos.db.getCharacter(charID).name
|
||||
|
||||
def new(self):
|
||||
char = eos.types.Character("New Character")
|
||||
def new(self, name="New Character"):
|
||||
char = eos.types.Character(name)
|
||||
eos.db.save(char)
|
||||
return char.ID
|
||||
return char
|
||||
|
||||
def rename(self, charID, newName):
|
||||
char = eos.db.getCharacter(charID)
|
||||
def rename(self, char, newName):
|
||||
char.name = newName
|
||||
eos.db.commit()
|
||||
|
||||
def copy(self, charID):
|
||||
char = eos.db.getCharacter(charID)
|
||||
def copy(self, char):
|
||||
newChar = copy.deepcopy(char)
|
||||
eos.db.save(newChar)
|
||||
return newChar.ID
|
||||
return newChar
|
||||
|
||||
def delete(self, charID):
|
||||
char = eos.db.getCharacter(charID)
|
||||
eos.db.commit()
|
||||
def delete(self, char):
|
||||
eos.db.remove(char)
|
||||
|
||||
def getApiDetails(self, charID):
|
||||
@@ -343,13 +341,18 @@ class Character(object):
|
||||
|
||||
def addImplant(self, charID, itemID):
|
||||
char = eos.db.getCharacter(charID)
|
||||
implant = eos.types.Implant(eos.db.getItem(itemID))
|
||||
char.implants.freeSlot(implant.slot)
|
||||
char.implants.append(implant)
|
||||
if char.ro:
|
||||
logger.error("Trying to add implant to read-only character")
|
||||
return
|
||||
|
||||
def removeImplant(self, charID, slot):
|
||||
implant = eos.types.Implant(eos.db.getItem(itemID))
|
||||
char.implants.append(implant)
|
||||
eos.db.commit()
|
||||
|
||||
def removeImplant(self, charID, implant):
|
||||
char = eos.db.getCharacter(charID)
|
||||
char.implants.freeSlot(slot)
|
||||
char.implants.remove(implant)
|
||||
eos.db.commit()
|
||||
|
||||
def getImplants(self, charID):
|
||||
char = eos.db.getCharacter(charID)
|
||||
|
||||
@@ -46,9 +46,10 @@ class DamagePattern():
|
||||
def getDamagePattern(self, name):
|
||||
return eos.db.getDamagePattern(name)
|
||||
|
||||
def newPattern(self):
|
||||
def newPattern(self, name):
|
||||
p = eos.types.DamagePattern(0, 0, 0, 0)
|
||||
p.name = ""
|
||||
p.name = name
|
||||
eos.db.save(p)
|
||||
return p
|
||||
|
||||
def renamePattern(self, p, newName):
|
||||
|
||||
@@ -277,7 +277,7 @@ class Fit(object):
|
||||
fit.timestamp))
|
||||
return fits
|
||||
|
||||
def addImplant(self, fitID, itemID):
|
||||
def addImplant(self, fitID, itemID, recalc=True):
|
||||
if fitID is None:
|
||||
return False
|
||||
|
||||
@@ -289,7 +289,8 @@ class Fit(object):
|
||||
return False
|
||||
|
||||
fit.implants.append(implant)
|
||||
self.recalc(fit)
|
||||
if recalc:
|
||||
self.recalc(fit)
|
||||
return True
|
||||
|
||||
def removeImplant(self, fitID, position):
|
||||
@@ -760,6 +761,14 @@ class Fit(object):
|
||||
self.recalc(fit)
|
||||
return True
|
||||
|
||||
def toggleImplantSource(self, fitID, source):
|
||||
fit = eos.db.getFit(fitID)
|
||||
fit.implantSource = source
|
||||
|
||||
eos.db.commit()
|
||||
self.recalc(fit)
|
||||
return True
|
||||
|
||||
def toggleBooster(self, fitID, i):
|
||||
fit = eos.db.getFit(fitID)
|
||||
booster = fit.boosters[i]
|
||||
|
||||
125
service/implantSet.py
Normal file
125
service/implantSet.py
Normal file
@@ -0,0 +1,125 @@
|
||||
#===============================================================================
|
||||
# Copyright (C) 2016 Ryan Holmes
|
||||
#
|
||||
# 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/>.
|
||||
#===============================================================================
|
||||
|
||||
import eos.db
|
||||
import eos.types
|
||||
import copy
|
||||
import service.market
|
||||
|
||||
class ImportError(Exception):
|
||||
pass
|
||||
|
||||
class ImplantSets():
|
||||
instance = None
|
||||
@classmethod
|
||||
def getInstance(cls):
|
||||
if cls.instance is None:
|
||||
cls.instance = ImplantSets()
|
||||
|
||||
return cls.instance
|
||||
|
||||
def getImplantSetList(self):
|
||||
return eos.db.getImplantSetList(None)
|
||||
|
||||
def getImplantSet(self, name):
|
||||
return eos.db.getImplantSet(name)
|
||||
|
||||
def getImplants(self, setID):
|
||||
set = eos.db.getImplantSet(setID)
|
||||
return set.implants
|
||||
|
||||
def addImplant(self, setID, itemID):
|
||||
set = eos.db.getImplantSet(setID)
|
||||
implant = eos.types.Implant(eos.db.getItem(itemID))
|
||||
set.implants.append(implant)
|
||||
eos.db.commit()
|
||||
|
||||
def removeImplant(self, setID, implant):
|
||||
set = eos.db.getImplantSet(setID)
|
||||
set.implants.remove(implant)
|
||||
eos.db.commit()
|
||||
|
||||
def newSet(self, name):
|
||||
s = eos.types.ImplantSet()
|
||||
s.name = name
|
||||
eos.db.save(s)
|
||||
return s
|
||||
|
||||
def renameSet(self, s, newName):
|
||||
s.name = newName
|
||||
eos.db.save(s)
|
||||
|
||||
def deleteSet(self, s):
|
||||
eos.db.remove(s)
|
||||
|
||||
def copySet(self, s):
|
||||
newS = copy.deepcopy(s)
|
||||
eos.db.save(newS)
|
||||
return newS
|
||||
|
||||
def saveChanges(self, s):
|
||||
eos.db.save(s)
|
||||
|
||||
def importSets(self, text):
|
||||
sMkt = service.Market.getInstance()
|
||||
lines = text.splitlines()
|
||||
newSets = []
|
||||
errors = 0
|
||||
current = None
|
||||
lookup = {}
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
line = line.strip()
|
||||
try:
|
||||
if line == '' or line[0] == "#": # comments / empty string
|
||||
continue
|
||||
if line[:1] == "[" and line[-1:] == "]":
|
||||
current = eos.types.ImplantSet(line[1:-1])
|
||||
newSets.append(current)
|
||||
else:
|
||||
item = sMkt.getItem(line)
|
||||
current.implants.append(eos.types.Implant(item))
|
||||
except:
|
||||
errors += 1
|
||||
continue
|
||||
|
||||
for set in self.getImplantSetList():
|
||||
lookup[set.name] = set
|
||||
|
||||
for set in newSets:
|
||||
if set.name in lookup:
|
||||
match = lookup[set.name]
|
||||
for implant in set.implants:
|
||||
match.implants.append(eos.types.Implant(implant.item))
|
||||
else:
|
||||
eos.db.save(set)
|
||||
|
||||
eos.db.commit()
|
||||
|
||||
lenImports = len(newSets)
|
||||
if lenImports == 0:
|
||||
raise ImportError("No patterns found for import")
|
||||
if errors > 0:
|
||||
raise ImportError("%d sets imported from clipboard; %d errors"%(lenImports, errors))
|
||||
|
||||
def exportSets(self):
|
||||
patterns = self.getImplantSetList()
|
||||
patterns.sort(key=lambda p: p.name)
|
||||
return eos.types.ImplantSet.exportSets(*patterns)
|
||||
|
||||
@@ -29,12 +29,15 @@ import eos.types
|
||||
from service.settings import SettingsProvider, NetworkSettings
|
||||
import service
|
||||
import service.conversions as conversions
|
||||
import logging
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from utils.compat import OrderedDict
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Event which tells threads dependent on Market that it's initialized
|
||||
mktRdy = threading.Event()
|
||||
|
||||
@@ -120,11 +123,14 @@ class SearchWorkerThread(threading.Thread):
|
||||
self.searchRequest = None
|
||||
cv.release()
|
||||
sMkt = Market.getInstance()
|
||||
if filterOn:
|
||||
if filterOn is True:
|
||||
# Rely on category data provided by eos as we don't hardcode them much in service
|
||||
filter = eos.types.Category.name.in_(sMkt.SEARCH_CATEGORIES)
|
||||
elif filterOn: # filter by selected categories
|
||||
filter = eos.types.Category.name.in_(filterOn)
|
||||
else:
|
||||
filter=None
|
||||
|
||||
results = eos.db.searchItems(request, where=filter,
|
||||
join=(eos.types.Item.group, eos.types.Group.category),
|
||||
eager=("icon", "group.category", "metaGroup", "metaGroup.parent"))
|
||||
@@ -346,20 +352,25 @@ class Market():
|
||||
|
||||
def getItem(self, identity, *args, **kwargs):
|
||||
"""Get item by its ID or name"""
|
||||
if isinstance(identity, eos.types.Item):
|
||||
item = identity
|
||||
elif isinstance(identity, int):
|
||||
item = eos.db.getItem(identity, *args, **kwargs)
|
||||
elif isinstance(identity, basestring):
|
||||
# We normally lookup with string when we are using import/export
|
||||
# features. Check against overrides
|
||||
identity = conversions.all.get(identity, identity)
|
||||
item = eos.db.getItem(identity, *args, **kwargs)
|
||||
elif isinstance(identity, float):
|
||||
id = int(identity)
|
||||
item = eos.db.getItem(id, *args, **kwargs)
|
||||
else:
|
||||
raise TypeError("Need Item object, integer, float or string as argument")
|
||||
try:
|
||||
if isinstance(identity, eos.types.Item):
|
||||
item = identity
|
||||
elif isinstance(identity, int):
|
||||
item = eos.db.getItem(identity, *args, **kwargs)
|
||||
elif isinstance(identity, basestring):
|
||||
# We normally lookup with string when we are using import/export
|
||||
# features. Check against overrides
|
||||
identity = conversions.all.get(identity, identity)
|
||||
item = eos.db.getItem(identity, *args, **kwargs)
|
||||
elif isinstance(identity, float):
|
||||
id = int(identity)
|
||||
item = eos.db.getItem(id, *args, **kwargs)
|
||||
else:
|
||||
raise TypeError("Need Item object, integer, float or string as argument")
|
||||
except:
|
||||
logger.error("Could not get item: %s", identity)
|
||||
raise
|
||||
|
||||
return item
|
||||
|
||||
def getGroup(self, identity, *args, **kwargs):
|
||||
|
||||
@@ -39,9 +39,10 @@ class TargetResists():
|
||||
def getTargetResists(self, name):
|
||||
return eos.db.getTargetResists(name)
|
||||
|
||||
def newPattern(self):
|
||||
def newPattern(self, name):
|
||||
p = eos.types.TargetResists(0.0, 0.0, 0.0, 0.0)
|
||||
p.name = ""
|
||||
p.name = name
|
||||
eos.db.save(p)
|
||||
return p
|
||||
|
||||
def renamePattern(self, p, newName):
|
||||
|
||||
Reference in New Issue
Block a user