Merge branch 'master' into colour-item-list

This commit is contained in:
Ryan Holmes
2019-03-02 15:27:54 -05:00
committed by GitHub
16 changed files with 543 additions and 125 deletions

View File

@@ -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)

View 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
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()