Merge branch 'master' into colour-item-list
This commit is contained in:
@@ -110,7 +110,7 @@ def defPaths(customSavePath=None):
|
||||
# Version data
|
||||
|
||||
with open(os.path.join(pyfaPath, "version.yml"), 'r') as file:
|
||||
data = yaml.load(file)
|
||||
data = yaml.load(file, Loader=yaml.FullLoader)
|
||||
version = data['version']
|
||||
|
||||
# Where we store the saved fits etc, default is the current users home directory
|
||||
|
||||
@@ -8,7 +8,7 @@ import yaml
|
||||
|
||||
|
||||
with open("version.yml", 'r') as file:
|
||||
data = yaml.load(file)
|
||||
data = yaml.load(file, Loader=yaml.FullLoader)
|
||||
version = data['version']
|
||||
|
||||
os.environ["PYFA_DIST_DIR"] = os.path.join(os.getcwd(), 'dist')
|
||||
|
||||
@@ -39,6 +39,8 @@ attributes_table = Table("dgmattribs", gamedata_meta,
|
||||
Column("displayName", String),
|
||||
Column("highIsGood", Boolean),
|
||||
Column("iconID", Integer),
|
||||
Column("attributeCategory", Integer),
|
||||
Column("tooltipDescription", Integer),
|
||||
Column("unitID", Integer, ForeignKey("dgmunits.unitID")))
|
||||
|
||||
mapper(Attribute, typeattributes_table,
|
||||
|
||||
@@ -577,6 +577,15 @@ class Unit(EqBase):
|
||||
self.name = None
|
||||
self.displayName = None
|
||||
|
||||
@property
|
||||
def rigSizes(self):
|
||||
return {
|
||||
1: "Small",
|
||||
2: "Medium",
|
||||
3: "Large",
|
||||
4: "X-Large"
|
||||
}
|
||||
|
||||
@property
|
||||
def translations(self):
|
||||
""" This is a mapping of various tweaks that we have to do between the internal representation of an attribute
|
||||
@@ -609,10 +618,10 @@ class Unit(EqBase):
|
||||
lambda u: "m³",
|
||||
lambda d: d),
|
||||
"Sizeclass": (
|
||||
lambda v: v,
|
||||
lambda v: v,
|
||||
lambda u: "",
|
||||
lambda d: d),
|
||||
lambda v: self.rigSizes[v],
|
||||
lambda v: self.rigSizes[v],
|
||||
lambda d: next(i for i in self.rigSizes.keys() if self.rigSizes[i] == 'Medium'),
|
||||
lambda u: ""),
|
||||
"Absolute Percent": (
|
||||
lambda v: v * 100,
|
||||
lambda v: v * 100,
|
||||
|
||||
@@ -82,9 +82,7 @@ class BitmapLoader(object):
|
||||
@classmethod
|
||||
def loadBitmap(cls, name, location):
|
||||
if cls.scaling_factor is None:
|
||||
import gui.mainFrame
|
||||
cls.scaling_factor = int(gui.mainFrame.MainFrame.getInstance().GetContentScaleFactor())
|
||||
|
||||
cls.scaling_factor = int(wx.GetApp().GetTopWindow().GetContentScaleFactor())
|
||||
scale = cls.scaling_factor
|
||||
|
||||
filename, img = cls.loadScaledBitmap(name, location, scale)
|
||||
|
||||
254
gui/builtinItemStatsViews/attributeGrouping.py
Normal file
254
gui/builtinItemStatsViews/attributeGrouping.py
Normal file
@@ -0,0 +1,254 @@
|
||||
from enum import Enum, auto
|
||||
|
||||
|
||||
# Define the various groups of attributes
|
||||
class AttrGroup(Enum):
|
||||
FITTING = auto()
|
||||
STRUCTURE = auto()
|
||||
SHIELD = auto()
|
||||
ARMOR = auto()
|
||||
TARGETING = auto()
|
||||
EWAR_RESISTS = auto()
|
||||
CAPACITOR = auto()
|
||||
SHARED_FACILITIES = auto()
|
||||
FIGHTER_FACILITIES = auto()
|
||||
ON_DEATH = auto()
|
||||
JUMP_SYSTEMS = auto()
|
||||
PROPULSIONS = auto()
|
||||
FIGHTERS = auto()
|
||||
|
||||
|
||||
RequiredSkillAttrs = sum((["requiredSkill{}".format(x), "requiredSkill{}Level".format(x)] for x in range(1, 7)), [])
|
||||
|
||||
#todo: maybe moved some of these basic definitions into eos proper? Can really be useful with effect writing as a lot of these are used over and over
|
||||
damage_types = ["em", "thermal", "kinetic", "explosive"]
|
||||
scan_types = ["radar", "magnetometric", "gravimetric", "ladar"]
|
||||
|
||||
DamageAttrs = ["{}Damage".format(x) for x in damage_types]
|
||||
HullResistsAttrs = ["{}DamageResonance".format(x) for x in damage_types]
|
||||
ArmorResistsAttrs = ["armor{}DamageResonance".format(x.capitalize()) for x in damage_types]
|
||||
ShieldResistsAttrs = ["shield{}DamageResonance".format(x.capitalize()) for x in damage_types]
|
||||
ScanStrAttrs = ["scan{}Strength".format(x.capitalize()) for x in scan_types]
|
||||
|
||||
# todo: convert to named tuples?
|
||||
AttrGroups = [
|
||||
(DamageAttrs, "Damage"),
|
||||
(HullResistsAttrs, "Resistances"),
|
||||
(ArmorResistsAttrs, "Resistances"),
|
||||
(ShieldResistsAttrs, "Resistances"),
|
||||
(ScanStrAttrs, "Sensor Strengths")
|
||||
]
|
||||
|
||||
GroupedAttributes = []
|
||||
for x in AttrGroups:
|
||||
GroupedAttributes += x[0]
|
||||
|
||||
# Start defining all the known attribute groups
|
||||
AttrGroupDict = {
|
||||
AttrGroup.FITTING : {
|
||||
"label" : "Fitting",
|
||||
"attributes": [
|
||||
# parent-level attributes
|
||||
"cpuOutput",
|
||||
"powerOutput",
|
||||
"upgradeCapacity",
|
||||
"hiSlots",
|
||||
"medSlots",
|
||||
"lowSlots",
|
||||
"serviceSlots",
|
||||
"turretSlotsLeft",
|
||||
"launcherSlotsLeft",
|
||||
"upgradeSlotsLeft",
|
||||
# child-level attributes
|
||||
"cpu",
|
||||
"power",
|
||||
"rigSize",
|
||||
"upgradeCost",
|
||||
# "mass",
|
||||
]
|
||||
},
|
||||
AttrGroup.STRUCTURE : {
|
||||
"label" : "Structure",
|
||||
"attributes": [
|
||||
"hp",
|
||||
"capacity",
|
||||
"mass",
|
||||
"volume",
|
||||
"agility",
|
||||
"droneCapacity",
|
||||
"droneBandwidth",
|
||||
"specialOreHoldCapacity",
|
||||
"specialGasHoldCapacity",
|
||||
"specialMineralHoldCapacity",
|
||||
"specialSalvageHoldCapacity",
|
||||
"specialShipHoldCapacity",
|
||||
"specialSmallShipHoldCapacity",
|
||||
"specialMediumShipHoldCapacity",
|
||||
"specialLargeShipHoldCapacity",
|
||||
"specialIndustrialShipHoldCapacity",
|
||||
"specialAmmoHoldCapacity",
|
||||
"specialCommandCenterHoldCapacity",
|
||||
"specialPlanetaryCommoditiesHoldCapacity",
|
||||
"structureDamageLimit",
|
||||
"specialSubsystemHoldCapacity",
|
||||
"emDamageResonance",
|
||||
"thermalDamageResonance",
|
||||
"kineticDamageResonance",
|
||||
"explosiveDamageResonance"
|
||||
]
|
||||
},
|
||||
AttrGroup.ARMOR : {
|
||||
"label": "Armor",
|
||||
"attributes":[
|
||||
"armorHP",
|
||||
"armorDamageLimit",
|
||||
"armorEmDamageResonance",
|
||||
"armorThermalDamageResonance",
|
||||
"armorKineticDamageResonance",
|
||||
"armorExplosiveDamageResonance",
|
||||
]
|
||||
|
||||
},
|
||||
AttrGroup.SHIELD : {
|
||||
"label": "Shield",
|
||||
"attributes": [
|
||||
"shieldCapacity",
|
||||
"shieldRechargeRate",
|
||||
"shieldDamageLimit",
|
||||
"shieldEmDamageResonance",
|
||||
"shieldExplosiveDamageResonance",
|
||||
"shieldKineticDamageResonance",
|
||||
"shieldThermalDamageResonance",
|
||||
]
|
||||
|
||||
},
|
||||
AttrGroup.EWAR_RESISTS : {
|
||||
"label": "Electronic Warfare",
|
||||
"attributes": [
|
||||
"ECMResistance",
|
||||
"remoteAssistanceImpedance",
|
||||
"remoteRepairImpedance",
|
||||
"energyWarfareResistance",
|
||||
"sensorDampenerResistance",
|
||||
"stasisWebifierResistance",
|
||||
"targetPainterResistance",
|
||||
"weaponDisruptionResistance",
|
||||
]
|
||||
},
|
||||
AttrGroup.CAPACITOR : {
|
||||
"label": "Capacitor",
|
||||
"attributes": [
|
||||
"capacitorCapacity",
|
||||
"rechargeRate",
|
||||
]
|
||||
},
|
||||
AttrGroup.TARGETING : {
|
||||
"label": "Targeting",
|
||||
"attributes": [
|
||||
"maxTargetRange",
|
||||
"maxRange",
|
||||
"maxLockedTargets",
|
||||
"signatureRadius",
|
||||
"optimalSigRadius",
|
||||
"scanResolution",
|
||||
"proximityRange",
|
||||
"falloff",
|
||||
"trackingSpeed",
|
||||
"scanRadarStrength",
|
||||
"scanMagnetometricStrength",
|
||||
"scanGravimetricStrength",
|
||||
"scanLadarStrength",
|
||||
]
|
||||
},
|
||||
AttrGroup.SHARED_FACILITIES : {
|
||||
"label" : "Shared Facilities",
|
||||
"attributes": [
|
||||
"fleetHangarCapacity",
|
||||
"shipMaintenanceBayCapacity",
|
||||
"maxJumpClones",
|
||||
]
|
||||
},
|
||||
AttrGroup.FIGHTER_FACILITIES: {
|
||||
"label": "Fighter Squadron Facilities",
|
||||
"attributes": [
|
||||
"fighterCapacity",
|
||||
"fighterTubes",
|
||||
"fighterLightSlots",
|
||||
"fighterSupportSlots",
|
||||
"fighterHeavySlots",
|
||||
"fighterStandupLightSlots",
|
||||
"fighterStandupSupportSlots",
|
||||
"fighterStandupHeavySlots",
|
||||
]
|
||||
},
|
||||
AttrGroup.ON_DEATH : {
|
||||
"label": "On Death",
|
||||
"attributes": [
|
||||
"onDeathDamageEM",
|
||||
"onDeathDamageTherm",
|
||||
"onDeathDamageKin",
|
||||
"onDeathDamageExp",
|
||||
"onDeathAOERadius",
|
||||
"onDeathSignatureRadius",
|
||||
]
|
||||
},
|
||||
AttrGroup.JUMP_SYSTEMS : {
|
||||
"label": "Jump Drive Systems",
|
||||
"attributes": [
|
||||
"jumpDriveCapacitorNeed",
|
||||
"jumpDriveRange",
|
||||
"jumpDriveConsumptionType",
|
||||
"jumpDriveConsumptionAmount",
|
||||
"jumpPortalCapacitorNeed",
|
||||
"jumpDriveDuration",
|
||||
"specialFuelBayCapacity",
|
||||
"jumpPortalConsumptionMassFactor",
|
||||
"jumpPortalDuration",
|
||||
]
|
||||
},
|
||||
AttrGroup.PROPULSIONS : {
|
||||
"label": "Propulsion",
|
||||
"attributes": [
|
||||
"maxVelocity"
|
||||
]
|
||||
},
|
||||
AttrGroup.FIGHTERS : {
|
||||
"label": "Fighter",
|
||||
"attributes": [
|
||||
"mass",
|
||||
"maxVelocity",
|
||||
"agility",
|
||||
"volume",
|
||||
"signatureRadius",
|
||||
"fighterSquadronMaxSize",
|
||||
"fighterRefuelingTime",
|
||||
"fighterSquadronOrbitRange",
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
Group1 = [
|
||||
AttrGroup.FITTING,
|
||||
AttrGroup.STRUCTURE,
|
||||
AttrGroup.ARMOR,
|
||||
AttrGroup.SHIELD,
|
||||
AttrGroup.EWAR_RESISTS,
|
||||
AttrGroup.CAPACITOR,
|
||||
AttrGroup.TARGETING,
|
||||
AttrGroup.SHARED_FACILITIES,
|
||||
AttrGroup.FIGHTER_FACILITIES,
|
||||
AttrGroup.ON_DEATH,
|
||||
AttrGroup.JUMP_SYSTEMS,
|
||||
AttrGroup.PROPULSIONS,
|
||||
]
|
||||
|
||||
CategoryGroups = {
|
||||
"Fighter" : [
|
||||
AttrGroup.FIGHTERS,
|
||||
AttrGroup.SHIELD,
|
||||
AttrGroup.TARGETING,
|
||||
],
|
||||
"Ship" : Group1,
|
||||
"Drone" : Group1,
|
||||
"Structure": Group1
|
||||
}
|
||||
@@ -3,11 +3,18 @@ import config
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from .helpers import AutoListCtrl
|
||||
import wx.lib.agw.hypertreelist
|
||||
from gui.builtinItemStatsViews.helpers import AutoListCtrl
|
||||
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.utils.numberFormatter import formatAmount, roundDec
|
||||
from enum import IntEnum
|
||||
from gui.builtinItemStatsViews.attributeGrouping import *
|
||||
|
||||
|
||||
class AttributeView(IntEnum):
|
||||
NORMAL = 1
|
||||
RAW = -1
|
||||
|
||||
|
||||
class ItemParams(wx.Panel):
|
||||
@@ -15,8 +22,9 @@ class ItemParams(wx.Panel):
|
||||
wx.Panel.__init__(self, parent)
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.paramList = AutoListCtrl(self, wx.ID_ANY,
|
||||
style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER)
|
||||
self.paramList = wx.lib.agw.hypertreelist.HyperTreeList(self, wx.ID_ANY, agwStyle=wx.TR_HIDE_ROOT | wx.TR_NO_LINES | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_HAS_BUTTONS)
|
||||
self.paramList.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
|
||||
|
||||
mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
@@ -27,14 +35,19 @@ class ItemParams(wx.Panel):
|
||||
self.attrValues = {}
|
||||
self._fetchValues()
|
||||
|
||||
self.paramList.AddColumn("Attribute")
|
||||
self.paramList.AddColumn("Current Value")
|
||||
if self.stuff is not None:
|
||||
self.paramList.AddColumn("Base Value")
|
||||
|
||||
self.paramList.SetMainColumn(0) # the one with the tree in it...
|
||||
self.paramList.SetColumnWidth(0, 300)
|
||||
|
||||
self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
|
||||
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle view mode", wx.DefaultPosition, wx.DefaultSize,
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "Veiw Raw Data", wx.DefaultPosition, wx.DefaultSize,
|
||||
0)
|
||||
bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
@@ -76,10 +89,10 @@ class ItemParams(wx.Panel):
|
||||
|
||||
def UpdateList(self):
|
||||
self.Freeze()
|
||||
self.paramList.ClearAll()
|
||||
self.paramList.DeleteRoot()
|
||||
self.PopulateList()
|
||||
self.Thaw()
|
||||
self.paramList.resizeLastColumn(100)
|
||||
# self.paramList.resizeLastColumn(100)
|
||||
|
||||
def RefreshValues(self, event):
|
||||
self._fetchValues()
|
||||
@@ -151,89 +164,154 @@ class ItemParams(wx.Panel):
|
||||
]
|
||||
)
|
||||
|
||||
def AddAttribute(self, parent, attr):
|
||||
if attr in self.attrValues and attr not in self.processed_attribs:
|
||||
|
||||
data = self.GetData(attr)
|
||||
if data is None:
|
||||
return
|
||||
|
||||
attrIcon, attrName, currentVal, baseVal = data
|
||||
attr_item = self.paramList.AppendItem(parent, attrName)
|
||||
|
||||
self.paramList.SetItemText(attr_item, currentVal, 1)
|
||||
if self.stuff is not None:
|
||||
self.paramList.SetItemText(attr_item, baseVal, 2)
|
||||
self.paramList.SetItemImage(attr_item, attrIcon, which=wx.TreeItemIcon_Normal)
|
||||
self.processed_attribs.add(attr)
|
||||
|
||||
def ExpandOrDelete(self, item):
|
||||
if self.paramList.GetChildrenCount(item) == 0:
|
||||
self.paramList.Delete(item)
|
||||
else:
|
||||
self.paramList.Expand(item)
|
||||
|
||||
def PopulateList(self):
|
||||
self.paramList.InsertColumn(0, "Attribute")
|
||||
self.paramList.InsertColumn(1, "Current Value")
|
||||
if self.stuff is not None:
|
||||
self.paramList.InsertColumn(2, "Base Value")
|
||||
self.paramList.SetColumnWidth(0, 110)
|
||||
self.paramList.SetColumnWidth(1, 90)
|
||||
if self.stuff is not None:
|
||||
self.paramList.SetColumnWidth(2, 90)
|
||||
self.paramList.setResizeColumn(0)
|
||||
# self.paramList.setResizeColumn(0)
|
||||
self.imageList = wx.ImageList(16, 16)
|
||||
self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL)
|
||||
|
||||
self.processed_attribs = set()
|
||||
root = self.paramList.AddRoot("The Root Item")
|
||||
misc_parent = root
|
||||
|
||||
# We must first deet4ermine if it's categorey already has defined groupings set for it. Otherwise, we default to just using the fitting group
|
||||
order = CategoryGroups.get(self.item.category.categoryName, [AttrGroup.FITTING])
|
||||
# start building out the tree
|
||||
for data in [AttrGroupDict[o] for o in order]:
|
||||
heading = data.get("label")
|
||||
|
||||
header_item = self.paramList.AppendItem(root, heading)
|
||||
for attr in data.get("attributes", []):
|
||||
# Attribute is a "grouped" attr (eg: damage, sensor strengths, etc). Automatically group these into a child item
|
||||
if attr in GroupedAttributes:
|
||||
# find which group it's in
|
||||
for grouping in AttrGroups:
|
||||
if attr in grouping[0]:
|
||||
break
|
||||
|
||||
# create a child item with the groups label
|
||||
item = self.paramList.AppendItem(header_item, grouping[1])
|
||||
for attr2 in grouping[0]:
|
||||
# add each attribute in the group
|
||||
self.AddAttribute(item, attr2)
|
||||
|
||||
self.ExpandOrDelete(item)
|
||||
continue
|
||||
|
||||
self.AddAttribute(header_item, attr)
|
||||
|
||||
self.ExpandOrDelete(header_item)
|
||||
|
||||
names = list(self.attrValues.keys())
|
||||
names.sort()
|
||||
|
||||
idNameMap = {}
|
||||
idCount = 0
|
||||
# this will take care of any attributes that weren't collected withe the defined grouping (or all attributes if the item ddidn't have anything defined)
|
||||
for name in names:
|
||||
info = self.attrInfo.get(name)
|
||||
att = self.attrValues[name]
|
||||
if name in GroupedAttributes:
|
||||
# find which group it's in
|
||||
for grouping in AttrGroups:
|
||||
if name in grouping[0]:
|
||||
break
|
||||
|
||||
# If we're working with a stuff object, we should get the original value from our getBaseAttrValue function,
|
||||
# which will return the value with respect to the effective base (with mutators / overrides in place)
|
||||
valDefault = getattr(info, "value", None) # Get default value from attribute
|
||||
if self.stuff is not None:
|
||||
# if it's a stuff, overwrite default (with fallback to current value)
|
||||
valDefault = self.stuff.getBaseAttrValue(name, valDefault)
|
||||
valueDefault = valDefault if valDefault is not None else att
|
||||
# get all attributes in group
|
||||
item = self.paramList.AppendItem(root, grouping[1])
|
||||
for attr2 in grouping[0]:
|
||||
self.AddAttribute(item, attr2)
|
||||
|
||||
val = getattr(att, "value", None)
|
||||
value = val if val is not None else att
|
||||
self.ExpandOrDelete(item)
|
||||
continue
|
||||
|
||||
if info and info.displayName and self.toggleView == 1:
|
||||
attrName = info.displayName
|
||||
else:
|
||||
attrName = name
|
||||
self.AddAttribute(root, name)
|
||||
|
||||
if info and config.debug:
|
||||
attrName += " ({})".format(info.ID)
|
||||
self.paramList.AssignImageList(self.imageList)
|
||||
self.Layout()
|
||||
|
||||
if info:
|
||||
if info.iconID is not None:
|
||||
iconFile = info.iconID
|
||||
icon = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
def GetData(self, attr):
|
||||
info = self.attrInfo.get(attr)
|
||||
att = self.attrValues[attr]
|
||||
|
||||
if icon is None:
|
||||
icon = BitmapLoader.getBitmap("transparent16x16", "gui")
|
||||
# If we're working with a stuff object, we should get the original value from our getBaseAttrValue function,
|
||||
# which will return the value with respect to the effective base (with mutators / overrides in place)
|
||||
valDefault = getattr(info, "value", None) # Get default value from attribute
|
||||
if self.stuff is not None:
|
||||
# if it's a stuff, overwrite default (with fallback to current value)
|
||||
valDefault = self.stuff.getBaseAttrValue(attr, valDefault)
|
||||
valueDefault = valDefault if valDefault is not None else att
|
||||
|
||||
attrIcon = self.imageList.Add(icon)
|
||||
else:
|
||||
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("0", "icons"))
|
||||
val = getattr(att, "value", None)
|
||||
value = val if val is not None else att
|
||||
|
||||
if self.toggleView == AttributeView.NORMAL and ((attr not in GroupedAttributes and not value) or info is None or not info.published or attr in RequiredSkillAttrs):
|
||||
return None
|
||||
|
||||
if info and info.displayName and self.toggleView == 1:
|
||||
attrName = info.displayName
|
||||
else:
|
||||
attrName = attr
|
||||
|
||||
if info and config.debug:
|
||||
attrName += " ({})".format(info.ID)
|
||||
|
||||
if info:
|
||||
if info.iconID is not None:
|
||||
iconFile = info.iconID
|
||||
icon = BitmapLoader.getBitmap(iconFile, "icons")
|
||||
|
||||
if icon is None:
|
||||
icon = BitmapLoader.getBitmap("transparent16x16", "gui")
|
||||
|
||||
attrIcon = self.imageList.Add(icon)
|
||||
else:
|
||||
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("0", "icons"))
|
||||
else:
|
||||
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("0", "icons"))
|
||||
|
||||
index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName, attrIcon)
|
||||
idNameMap[idCount] = attrName
|
||||
self.paramList.SetItemData(index, idCount)
|
||||
idCount += 1
|
||||
# index = self.paramList.AppendItem(root, attrName)
|
||||
# idNameMap[idCount] = attrName
|
||||
# self.paramList.SetPyData(index, idCount)
|
||||
# idCount += 1
|
||||
|
||||
if self.toggleView != 1:
|
||||
valueUnit = str(value)
|
||||
elif info and info.unit:
|
||||
valueUnit = self.FormatValue(*info.unit.PreformatValue(value))
|
||||
else:
|
||||
valueUnit = formatAmount(value, 3, 0, 0)
|
||||
if self.toggleView != 1:
|
||||
valueUnit = str(value)
|
||||
elif info and info.unit:
|
||||
valueUnit = self.FormatValue(*info.unit.PreformatValue(value))
|
||||
else:
|
||||
valueUnit = formatAmount(value, 3, 0, 0)
|
||||
|
||||
if self.toggleView != 1:
|
||||
valueUnitDefault = str(valueDefault)
|
||||
elif info and info.unit:
|
||||
valueUnitDefault = self.FormatValue(*info.unit.PreformatValue(valueDefault))
|
||||
else:
|
||||
valueUnitDefault = formatAmount(valueDefault, 3, 0, 0)
|
||||
if self.toggleView != 1:
|
||||
valueUnitDefault = str(valueDefault)
|
||||
elif info and info.unit:
|
||||
valueUnitDefault = self.FormatValue(*info.unit.PreformatValue(valueDefault))
|
||||
else:
|
||||
valueUnitDefault = formatAmount(valueDefault, 3, 0, 0)
|
||||
|
||||
self.paramList.SetItem(index, 1, valueUnit)
|
||||
if self.stuff is not None:
|
||||
self.paramList.SetItem(index, 2, valueUnitDefault)
|
||||
# @todo: pheonix, this lamda used cmp() which no longer exists in py3. Probably a better way to do this in the
|
||||
# long run, take a look
|
||||
self.paramList.SortItems(lambda id1, id2: (idNameMap[id1] > idNameMap[id2]) - (idNameMap[id1] < idNameMap[id2]))
|
||||
self.paramList.RefreshRows()
|
||||
self.totalAttrsLabel.SetLabel("%d attributes. " % idCount)
|
||||
self.Layout()
|
||||
# todo: attribute that point to another item should load that item's icon.
|
||||
return (attrIcon, attrName, valueUnit, valueUnitDefault)
|
||||
|
||||
# self.paramList.SetItemText(index, valueUnit, 1)
|
||||
# if self.stuff is not None:
|
||||
# self.paramList.SetItemText(index, valueUnitDefault, 2)
|
||||
# self.paramList.SetItemImage(index, attrIcon, which=wx.TreeItemIcon_Normal)
|
||||
|
||||
@staticmethod
|
||||
def FormatValue(value, unit, rounding='prec', digits=3):
|
||||
@@ -246,3 +324,43 @@ class ItemParams(wx.Panel):
|
||||
else:
|
||||
fvalue = value
|
||||
return "%s %s" % (fvalue, unit)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import eos.db
|
||||
# need to set up some paths, since bitmap loader requires config to have things
|
||||
# Should probably change that so that it's not dependant on config
|
||||
import os
|
||||
os.chdir('..')
|
||||
import config
|
||||
config.defPaths(None)
|
||||
config.debug = True
|
||||
class Frame(wx.Frame):
|
||||
def __init__(self, ):
|
||||
# item = eos.db.getItem(23773) # Ragnarok
|
||||
item = eos.db.getItem(23061) # Einherji I
|
||||
#item = eos.db.getItem(24483) # Nidhoggur
|
||||
#item = eos.db.getItem(587) # Rifter
|
||||
#item = eos.db.getItem(2486) # Warrior I
|
||||
#item = eos.db.getItem(526) # Stasis Webifier I
|
||||
item = eos.db.getItem(486) # 200mm AutoCannon I
|
||||
#item = eos.db.getItem(200) # Phased Plasma L
|
||||
super().__init__(None, title="Test Attribute Window | {} - {}".format(item.ID, item.name), size=(1000, 500))
|
||||
|
||||
if 'wxMSW' in wx.PlatformInfo:
|
||||
color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)
|
||||
self.SetBackgroundColour(color)
|
||||
|
||||
main_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
panel = ItemParams(self, None, item)
|
||||
|
||||
main_sizer.Add(panel, 1, wx.EXPAND | wx.ALL, 2)
|
||||
|
||||
self.SetSizer(main_sizer)
|
||||
|
||||
app = wx.App(redirect=False) # Error messages go to popup window
|
||||
top = Frame()
|
||||
top.Show()
|
||||
app.MainLoop()
|
||||
|
||||
@@ -62,13 +62,11 @@ from gui.preferenceDialog import PreferenceDialog
|
||||
from gui.resistsEditor import ResistsEditorDlg
|
||||
from gui.setEditor import ImplantSetEditorDlg
|
||||
from gui.shipBrowser import ShipBrowser
|
||||
from gui.ssoLogin import SsoLogin
|
||||
from gui.statsPane import StatsPane
|
||||
from gui.updateDialog import UpdateDialog
|
||||
from gui.utils.clipboard import fromClipboard, toClipboard
|
||||
from service.character import Character
|
||||
from service.esi import Esi, LoginMethod
|
||||
from service.esiAccess import SsoMode
|
||||
from service.esi import Esi
|
||||
from service.fit import Fit
|
||||
from service.port import EfsPort, IPortUser, Port
|
||||
from service.settings import HTMLExportSettings, SettingsProvider
|
||||
@@ -230,20 +228,11 @@ class MainFrame(wx.Frame):
|
||||
self.sUpdate.CheckUpdate(self.ShowUpdateBox)
|
||||
|
||||
self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin)
|
||||
self.Bind(GE.EVT_SSO_LOGGING_IN, self.ShowSsoLogin)
|
||||
|
||||
@property
|
||||
def command(self) -> wx.CommandProcessor:
|
||||
return Fit.getCommandProcessor(self.getActiveFit())
|
||||
|
||||
def ShowSsoLogin(self, event):
|
||||
if getattr(event, "login_mode", LoginMethod.SERVER) == LoginMethod.MANUAL and getattr(event, "sso_mode", SsoMode.AUTO) == SsoMode.AUTO:
|
||||
dlg = SsoLogin(self)
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
sEsi = Esi.getInstance()
|
||||
# todo: verify that this is a correct SSO Info block
|
||||
sEsi.handleLogin({'SSOInfo': [dlg.ssoInfoCtrl.Value.strip()]})
|
||||
|
||||
def ShowUpdateBox(self, release, version):
|
||||
dlg = UpdateDialog(self, release, version)
|
||||
dlg.ShowModal()
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import wx
|
||||
import gui.mainFrame
|
||||
import webbrowser
|
||||
import gui.globalEvents as GE
|
||||
|
||||
|
||||
class SsoLogin(wx.Dialog):
|
||||
def __init__(self, parent):
|
||||
wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="SSO Login", size=wx.Size(400, 240))
|
||||
def __init__(self):
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
wx.Dialog.__init__(self, mainFrame, id=wx.ID_ANY, title="SSO Login", size=wx.Size(400, 240))
|
||||
|
||||
bSizer1 = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
@@ -24,3 +29,55 @@ class SsoLogin(wx.Dialog):
|
||||
|
||||
self.SetSizer(bSizer1)
|
||||
self.Center()
|
||||
|
||||
mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin)
|
||||
|
||||
from service.esi import Esi
|
||||
|
||||
self.sEsi = Esi.getInstance()
|
||||
uri = self.sEsi.getLoginURI(None)
|
||||
webbrowser.open(uri)
|
||||
|
||||
def OnLogin(self, event):
|
||||
self.Close()
|
||||
event.Skip()
|
||||
|
||||
|
||||
class SsoLoginServer(wx.Dialog):
|
||||
def __init__(self, port):
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
wx.Dialog.__init__(self, mainFrame, id=wx.ID_ANY, title="SSO Login", size=(-1, -1))
|
||||
|
||||
from service.esi import Esi
|
||||
|
||||
self.sEsi = Esi.getInstance()
|
||||
serverAddr = self.sEsi.startServer(port)
|
||||
|
||||
uri = self.sEsi.getLoginURI(serverAddr)
|
||||
|
||||
bSizer1 = wx.BoxSizer(wx.VERTICAL)
|
||||
mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin)
|
||||
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
||||
|
||||
text = wx.StaticText(self, wx.ID_ANY, "Waiting for character login through EVE Single Sign-On.")
|
||||
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
|
||||
|
||||
bSizer3 = wx.BoxSizer(wx.VERTICAL)
|
||||
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10)
|
||||
|
||||
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.CANCEL), 0, wx.EXPAND)
|
||||
bSizer1.Add(bSizer3, 0, wx.BOTTOM | wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
|
||||
|
||||
self.SetSizer(bSizer1)
|
||||
self.Fit()
|
||||
self.Center()
|
||||
|
||||
webbrowser.open(uri)
|
||||
|
||||
def OnLogin(self, event):
|
||||
self.Close()
|
||||
event.Skip()
|
||||
|
||||
def OnClose(self, event):
|
||||
self.sEsi.stopServer()
|
||||
event.Skip()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
wxPython == 4.0.0b2
|
||||
wxPython == 4.0.4
|
||||
logbook >= 1.0.0
|
||||
matplotlib >= 2.0.0
|
||||
python-dateutil
|
||||
requests >= 2.0.0
|
||||
sqlalchemy == 1.0.5
|
||||
cryptography ==2.2.2
|
||||
cryptography>=2.3
|
||||
markdown2==2.3.5
|
||||
packaging==16.8
|
||||
roman==2.0.0
|
||||
beautifulsoup4==4.6.0
|
||||
PyYAML==3.12
|
||||
pyyaml>=5.1b1
|
||||
PyInstaller == 3.3
|
||||
@@ -10,7 +10,7 @@ import os
|
||||
|
||||
|
||||
with open("version.yml", 'r+') as file:
|
||||
data = yaml.load(file)
|
||||
data = yaml.load(file, Loader=yaml.FullLoader)
|
||||
file.seek(0)
|
||||
file.truncate()
|
||||
# todo: run Version() on the tag to ensure that it's of proper formatting - fail a test if not and prevent building
|
||||
|
||||
@@ -10,7 +10,7 @@ import json
|
||||
iconDict = {}
|
||||
|
||||
stream = open('iconIDs.yaml', 'r')
|
||||
docs = yaml.load_all(stream)
|
||||
docs = yaml.load_all(stream, Loader=yaml.FullLoader)
|
||||
|
||||
for doc in docs:
|
||||
for k,v in list(doc.items()):
|
||||
|
||||
@@ -13,9 +13,11 @@ from eos.enum import Enum
|
||||
from eos.saveddata.ssocharacter import SsoCharacter
|
||||
from service.esiAccess import APIException, SsoMode
|
||||
import gui.globalEvents as GE
|
||||
from gui.ssoLogin import SsoLogin, SsoLoginServer
|
||||
from service.server import StoppableHTTPServer, AuthHandler
|
||||
from service.settings import EsiSettings
|
||||
from service.esiAccess import EsiAccess
|
||||
import gui.mainFrame
|
||||
|
||||
from requests import Session
|
||||
|
||||
@@ -104,19 +106,21 @@ class Esi(EsiAccess):
|
||||
self.fittings_deleted.add(fittingID)
|
||||
|
||||
def login(self):
|
||||
serverAddr = None
|
||||
# always start the local server if user is using client details. Otherwise, start only if they choose to do so.
|
||||
if self.settings.get('ssoMode') == SsoMode.CUSTOM or self.settings.get('loginMode') == LoginMethod.SERVER:
|
||||
# random port, or if it's custom application, use a defined port
|
||||
serverAddr = self.startServer(6461 if self.settings.get('ssoMode') == SsoMode.CUSTOM else 0)
|
||||
uri = self.getLoginURI(serverAddr)
|
||||
webbrowser.open(uri)
|
||||
wx.PostEvent(self.mainFrame, GE.SsoLoggingIn(sso_mode=self.settings.get('ssoMode'), login_mode=self.settings.get('loginMode')))
|
||||
dlg = gui.ssoLogin.SsoLoginServer(6461 if self.settings.get('ssoMode') == SsoMode.CUSTOM else 0)
|
||||
dlg.ShowModal()
|
||||
else:
|
||||
dlg = gui.ssoLogin.SsoLogin()
|
||||
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
self.handleLogin({'SSOInfo': [dlg.ssoInfoCtrl.Value.strip()]})
|
||||
|
||||
def stopServer(self):
|
||||
pyfalog.debug("Stopping Server")
|
||||
self.httpd.stop()
|
||||
self.httpd = None
|
||||
if self.httpd:
|
||||
self.httpd.stop()
|
||||
self.httpd = None
|
||||
|
||||
def startServer(self, port): # todo: break this out into two functions: starting the server, and getting the URI
|
||||
pyfalog.debug("Starting server")
|
||||
|
||||
@@ -43,9 +43,9 @@ class JargonLoader(object):
|
||||
self.jargon_mtime != self._get_jargon_file_mtime())
|
||||
|
||||
def _load_jargon(self):
|
||||
jargondata = yaml.load(DEFAULT_DATA)
|
||||
jargondata = yaml.load(DEFAULT_DATA, Loader=yaml.FullLoader)
|
||||
with open(JARGON_PATH) as f:
|
||||
userdata = yaml.load(f)
|
||||
userdata = yaml.load(f, Loader=yaml.FullLoader)
|
||||
jargondata.update(userdata)
|
||||
self.jargon_mtime = self._get_jargon_file_mtime()
|
||||
self._jargon = Jargon(jargondata)
|
||||
@@ -57,7 +57,7 @@ class JargonLoader(object):
|
||||
|
||||
@staticmethod
|
||||
def init_user_jargon(jargon_path):
|
||||
values = yaml.load(DEFAULT_DATA)
|
||||
values = yaml.load(DEFAULT_DATA, Loader=yaml.FullLoader)
|
||||
|
||||
# Disabled for issue/1533; do not overwrite existing user config
|
||||
# if os.path.exists(jargon_path):
|
||||
|
||||
@@ -117,13 +117,7 @@ class StoppableHTTPServer(socketserver.TCPServer):
|
||||
|
||||
# self.settings = CRESTSettings.getInstance()
|
||||
|
||||
# Allow listening for x seconds
|
||||
sec = 120
|
||||
pyfalog.debug("Running server for {0} seconds", sec)
|
||||
|
||||
self.socket.settimeout(1)
|
||||
self.max_tries = sec / self.socket.gettimeout()
|
||||
self.tries = 0
|
||||
self.run = True
|
||||
|
||||
def get_request(self):
|
||||
@@ -140,13 +134,6 @@ class StoppableHTTPServer(socketserver.TCPServer):
|
||||
pyfalog.warning("Setting pyfa server to stop.")
|
||||
self.run = False
|
||||
|
||||
def handle_timeout(self):
|
||||
pyfalog.debug("Number of tries: {0}", self.tries)
|
||||
self.tries += 1
|
||||
if self.tries == self.max_tries:
|
||||
pyfalog.debug("Server timed out waiting for connection")
|
||||
self.stop()
|
||||
|
||||
def serve(self, callback=None):
|
||||
self.callback = callback
|
||||
while self.run:
|
||||
|
||||
Reference in New Issue
Block a user