Merge branch 'master' into test-3
Conflicts: eos/gamedata.py eos/saveddata/booster.py eos/saveddata/character.py gui/builtinAdditionPanes/commandView.py gui/builtinContextMenus/commandFits.py gui/builtinMarketBrowser/itemView.py gui/builtinMarketBrowser/marketTree.py gui/builtinPreferenceViews/pyfaGeneralPreferences.py gui/builtinShipBrowser/categoryItem.py gui/builtinShipBrowser/fitItem.py gui/builtinShipBrowser/navigationPanel.py gui/builtinShipBrowser/raceSelector.py gui/builtinShipBrowser/shipItem.py gui/builtinStatsViews/priceViewFull.py gui/builtinViews/fittingView.py gui/characterEditor.py gui/characterSelection.py gui/chromeTabs.py gui/crestFittings.py gui/itemStats.py gui/mainFrame.py scripts/itemDiff.py service/price.py
This commit is contained in:
@@ -49,6 +49,7 @@ class BoosterView(d.Display):
|
||||
"State",
|
||||
"attr:boosterness",
|
||||
"Base Name",
|
||||
"Side Effects",
|
||||
"Price",
|
||||
]
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import wx
|
||||
import gui.builtinAdditionPanes.droneView
|
||||
import gui.display as d
|
||||
import gui.globalEvents as GE
|
||||
from gui.builtinShipBrowser.events import EVT_FIT_REMOVED
|
||||
from eos.saveddata.drone import Drone as es_Drone
|
||||
from gui.builtinContextMenus.commandFits import CommandFits
|
||||
from gui.builtinViewColumns.state import State
|
||||
@@ -66,6 +67,8 @@ class CommandView(d.Display):
|
||||
|
||||
self.lastFitId = None
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, CommandFits.fitChanged)
|
||||
self.mainFrame.Bind(EVT_FIT_REMOVED, CommandFits.populateFits)
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_RIGHT_DOWN, self.click)
|
||||
@@ -169,6 +172,9 @@ class CommandView(d.Display):
|
||||
event.Skip()
|
||||
|
||||
def get(self, row):
|
||||
if row == -1:
|
||||
return None
|
||||
|
||||
numFits = len(self.fits)
|
||||
|
||||
if numFits == 0:
|
||||
@@ -194,23 +200,21 @@ class CommandView(d.Display):
|
||||
wx.CallAfter(self.spawnMenu)
|
||||
|
||||
def spawnMenu(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return
|
||||
|
||||
sel = self.GetFirstSelected()
|
||||
menu = None
|
||||
if sel != -1:
|
||||
item = self.get(sel)
|
||||
if item is None:
|
||||
return
|
||||
context = ()
|
||||
item = self.get(sel)
|
||||
|
||||
if item is not None:
|
||||
fitSrcContext = "commandFit"
|
||||
fitItemContext = item.name
|
||||
context = ((fitSrcContext, fitItemContext),)
|
||||
context += ("commandView",),
|
||||
menu = ContextMenu.getMenu((item,), *context)
|
||||
elif sel == -1:
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return
|
||||
context = (("commandView",),)
|
||||
menu = ContextMenu.getMenu([], *context)
|
||||
|
||||
context += (("commandView",),)
|
||||
menu = ContextMenu.getMenu((item,) if item is not None else [], *context)
|
||||
if menu is not None:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
import gui.display as d
|
||||
import gui.marketBrowser as marketBrowser
|
||||
from gui.builtinMarketBrowser.events import ITEM_SELECTED
|
||||
import gui.mainFrame
|
||||
from gui.builtinViewColumns.state import State
|
||||
from gui.contextMenu import ContextMenu
|
||||
@@ -96,7 +96,7 @@ class ImplantDisplay(d.Display):
|
||||
self.lastFitId = None
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged)
|
||||
self.mainFrame.Bind(marketBrowser.ITEM_SELECTED, self.addItem)
|
||||
self.mainFrame.Bind(ITEM_SELECTED, self.addItem)
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
|
||||
self.Bind(wx.EVT_LEFT_DOWN, self.click)
|
||||
self.Bind(wx.EVT_KEY_UP, self.kbEvent)
|
||||
|
||||
@@ -191,7 +191,7 @@ class ProjectedView(d.Display):
|
||||
|
||||
stuff = []
|
||||
if fit is not None:
|
||||
pyfalog.debug(" Collecting list of stuff to display in ProjectedView")
|
||||
# pyfalog.debug(" Collecting list of stuff to display in ProjectedView")
|
||||
self.modules = fit.projectedModules[:]
|
||||
self.drones = fit.projectedDrones[:]
|
||||
self.fighters = fit.projectedFighters[:]
|
||||
@@ -218,13 +218,16 @@ class ProjectedView(d.Display):
|
||||
self.deselectItems()
|
||||
|
||||
if not stuff:
|
||||
stuff = [DummyEntry("Drag an item or fit, or use right-click menu for system effects")]
|
||||
stuff = [DummyEntry("Drag an item or fit, or use right-click menu for wormhole effects")]
|
||||
|
||||
self.update(stuff)
|
||||
|
||||
event.Skip()
|
||||
|
||||
def get(self, row):
|
||||
if row == -1:
|
||||
return None
|
||||
|
||||
numMods = len(self.modules)
|
||||
numDrones = len(self.drones)
|
||||
numFighters = len(self.fighters)
|
||||
@@ -262,13 +265,17 @@ class ProjectedView(d.Display):
|
||||
wx.CallAfter(self.spawnMenu)
|
||||
|
||||
def spawnMenu(self):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return
|
||||
|
||||
sel = self.GetFirstSelected()
|
||||
menu = None
|
||||
if sel != -1:
|
||||
item = self.get(sel)
|
||||
if item is None:
|
||||
return
|
||||
context = ()
|
||||
item = self.get(sel)
|
||||
|
||||
if item is not None:
|
||||
sMkt = Market.getInstance()
|
||||
|
||||
if isinstance(item, es_Drone):
|
||||
srcContext = "projectedDrone"
|
||||
itemContext = sMkt.getCategoryByItem(item.item).name
|
||||
@@ -292,14 +299,10 @@ class ProjectedView(d.Display):
|
||||
fitSrcContext = "projectedFit"
|
||||
fitItemContext = item.name
|
||||
context = ((fitSrcContext, fitItemContext),)
|
||||
context += ("projected",),
|
||||
menu = ContextMenu.getMenu((item,), *context)
|
||||
elif sel == -1:
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is None:
|
||||
return
|
||||
context = (("projected",),)
|
||||
menu = ContextMenu.getMenu([], *context)
|
||||
|
||||
context += (("projected",),)
|
||||
menu = ContextMenu.getMenu((item,) if item is not None else [], *context)
|
||||
|
||||
if menu is not None:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import gui.mainFrame
|
||||
import gui.globalEvents as GE
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
import re
|
||||
from service.fit import Fit
|
||||
from eos.saveddata.cargo import Cargo as es_Cargo
|
||||
from eos.saveddata.fighter import Fighter as es_Fighter
|
||||
@@ -22,7 +23,7 @@ class ChangeAmount(ContextMenu):
|
||||
return srcContext in ("cargoItem", "projectedFit", "fighterItem", "projectedFighter")
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "Change {0} Quantity".format(itmContext)
|
||||
return u"Change {0} Quantity".format(itmContext)
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
srcContext = fullContext[0]
|
||||
@@ -61,15 +62,16 @@ class AmountChanger(wx.Dialog):
|
||||
return
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
cleanInput = re.sub(r'[^0-9.]', '', self.input.GetLineText(0).strip())
|
||||
mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
fitID = mainFrame.getActiveFit()
|
||||
|
||||
if isinstance(self.thing, es_Cargo):
|
||||
sFit.addCargo(fitID, self.thing.item.ID, int(float(self.input.GetLineText(0))), replace=True)
|
||||
sFit.addCargo(fitID, self.thing.item.ID, int(float(cleanInput)), replace=True)
|
||||
elif isinstance(self.thing, es_Fit):
|
||||
sFit.changeAmount(fitID, self.thing, int(float(self.input.GetLineText(0))))
|
||||
sFit.changeAmount(fitID, self.thing, int(float(cleanInput)))
|
||||
elif isinstance(self.thing, es_Fighter):
|
||||
sFit.changeActiveFighters(fitID, self.thing, int(float(self.input.GetLineText(0))))
|
||||
sFit.changeActiveFighters(fitID, self.thing, int(float(cleanInput)))
|
||||
|
||||
wx.PostEvent(mainFrame, GE.FitChanged(fitID=fitID))
|
||||
|
||||
|
||||
69
gui/builtinContextMenus/boosterSideEffects.py
Normal file
69
gui/builtinContextMenus/boosterSideEffects.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from gui.contextMenu import ContextMenu
|
||||
import gui.mainFrame
|
||||
import gui.globalEvents as GE
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
class BoosterSideEffect(ContextMenu):
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = ContextMenuSettings.getInstance()
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
# if not self.settings.get('fighterAbilities'):
|
||||
# return False
|
||||
|
||||
if self.mainFrame.getActiveFit() is None or srcContext not in ("boosterItem"):
|
||||
return False
|
||||
|
||||
self.booster = selection[0]
|
||||
|
||||
for effect in self.booster.sideEffects:
|
||||
if effect.effect.isImplemented:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "Side Effects"
|
||||
|
||||
def addEffect(self, menu, ability):
|
||||
label = ability.name
|
||||
id = ContextMenu.nextID()
|
||||
self.effectIds[id] = ability
|
||||
menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_CHECK)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.context = context
|
||||
self.effectIds = {}
|
||||
|
||||
sub = wx.Menu()
|
||||
|
||||
for effect in self.booster.sideEffects:
|
||||
if not effect.effect.isImplemented:
|
||||
continue
|
||||
menuItem = self.addEffect(rootMenu if msw else sub, effect)
|
||||
sub.AppendItem(menuItem)
|
||||
menuItem.Check(effect.active)
|
||||
|
||||
return sub
|
||||
|
||||
def handleMode(self, event):
|
||||
effect = self.effectIds[event.Id]
|
||||
if effect is False or effect not in self.booster.sideEffects:
|
||||
event.Skip()
|
||||
return
|
||||
|
||||
sFit = Fit.getInstance()
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
sFit.toggleBoosterSideEffect(fitID, effect)
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
|
||||
|
||||
|
||||
BoosterSideEffect.register()
|
||||
@@ -18,7 +18,7 @@ class CommandFits(ContextMenu):
|
||||
menu = None
|
||||
|
||||
@classmethod
|
||||
def populateFits(cls, evt):
|
||||
def fitChanged(cls, evt):
|
||||
# This fires on a FitChanged event and updates the command fits whenever a command burst module is added or
|
||||
# removed from a fit. evt.typeID can be either a int or a set (in the case of multiple module deletions)
|
||||
if evt is None or (getattr(evt, 'action', None) in ("modadd", "moddel") and getattr(evt, 'typeID', None)):
|
||||
@@ -29,8 +29,12 @@ class CommandFits(ContextMenu):
|
||||
|
||||
if evt is None or not ids.isdisjoint(cls.commandTypeIDs):
|
||||
# we are adding or removing an item that defines a command fit. Need to refresh fit list
|
||||
sFit = Fit.getInstance()
|
||||
cls.commandFits = sFit.getFitsWithModules(cls.commandTypeIDs)
|
||||
cls.populateFits(evt)
|
||||
|
||||
@classmethod
|
||||
def populateFits(cls, evt):
|
||||
sFit = Fit.getInstance()
|
||||
cls.commandFits = sFit.getFitsWithModules(cls.commandTypeIDs)
|
||||
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
@@ -62,7 +66,6 @@ class CommandFits(ContextMenu):
|
||||
|
||||
if len(self.__class__.commandFits) < 15:
|
||||
for fit in sorted(self.__class__.commandFits, key=lambda x: x.name):
|
||||
print(fit)
|
||||
menuItem = self.addFit(rootMenu if msw else sub, fit, True)
|
||||
sub.Append(menuItem)
|
||||
else:
|
||||
|
||||
@@ -75,10 +75,10 @@ class ImplantSets(ContextMenu):
|
||||
if self.context == "implantEditor":
|
||||
# we are calling from character editor, the implant source is different
|
||||
sChar = Character.getInstance()
|
||||
charID = self.selection.getActiveCharacter()
|
||||
char = self.selection.entityEditor.getActiveEntity()
|
||||
|
||||
for implant in set.implants:
|
||||
sChar.addImplant(charID, implant.item.ID)
|
||||
sChar.addImplant(char.ID, implant.item.ID)
|
||||
|
||||
wx.PostEvent(self.selection, GE.CharChanged())
|
||||
else:
|
||||
|
||||
@@ -25,7 +25,7 @@ class ItemRemove(ContextMenu):
|
||||
"commandFit")
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "Remove {0}".format(itmContext if itmContext is not None else "Item")
|
||||
return u"Remove {0}".format(itmContext if itmContext is not None else "Item")
|
||||
|
||||
def activate(self, fullContext, selection, i):
|
||||
|
||||
@@ -50,7 +50,7 @@ class ItemRemove(ContextMenu):
|
||||
sFit.removeBooster(fitID, fit.boosters.index(selection[0]))
|
||||
elif srcContext == "cargoItem":
|
||||
sFit.removeCargo(fitID, fit.cargo.index(selection[0]))
|
||||
elif srcContext == "projectedFit":
|
||||
elif srcContext in ("projectedFit", "projectedModule", "projectedDrone", "projectedFighter"):
|
||||
sFit.removeProjected(fitID, selection[0])
|
||||
elif srcContext == "commandFit":
|
||||
sFit.removeCommand(fitID, selection[0])
|
||||
|
||||
@@ -14,6 +14,7 @@ from eos.saveddata.module import Module
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fighter import Fighter
|
||||
from eos.saveddata.implant import Implant
|
||||
from eos.saveddata.cargo import Cargo
|
||||
|
||||
|
||||
class MetaSwap(ContextMenu):
|
||||
@@ -31,6 +32,7 @@ class MetaSwap(ContextMenu):
|
||||
"fighterItem",
|
||||
"boosterItem",
|
||||
"implantItem",
|
||||
"cargoItem",
|
||||
):
|
||||
return False
|
||||
|
||||
@@ -104,7 +106,9 @@ class MetaSwap(ContextMenu):
|
||||
group = None
|
||||
for item in items:
|
||||
# Apparently no metaGroup for the Tech I variant:
|
||||
if item.metaGroup is None:
|
||||
if "subSystem" in item.effects:
|
||||
thisgroup = item.marketGroup.marketGroupName
|
||||
elif item.metaGroup is None:
|
||||
thisgroup = "Tech I"
|
||||
else:
|
||||
thisgroup = item.metaGroup.name
|
||||
@@ -183,6 +187,13 @@ class MetaSwap(ContextMenu):
|
||||
sFit.addImplant(fitID, item.ID, True)
|
||||
break
|
||||
|
||||
elif isinstance(selected_item, Cargo):
|
||||
for idx, cargo_stack in enumerate(fit.cargo):
|
||||
if cargo_stack is selected_item:
|
||||
sFit.removeCargo(fitID, idx)
|
||||
sFit.addCargo(fitID, item.ID, cargo_stack.amount, True)
|
||||
break
|
||||
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from gui.contextMenu import ContextMenu
|
||||
import gui.mainFrame
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from gui.shipBrowser import FitSelected
|
||||
from gui.builtinShipBrowser.events import FitSelected
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
|
||||
50
gui/builtinContextMenus/priceOptions.py
Normal file
50
gui/builtinContextMenus/priceOptions.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import wx
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from service.settings import PriceMenuSettings
|
||||
|
||||
|
||||
class PriceOptions(ContextMenu):
|
||||
def __init__(self):
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.settings = PriceMenuSettings.getInstance()
|
||||
self.optionList = ["Ship", "Modules", "Drones", "Cargo", "Character"]
|
||||
|
||||
def display(self, srcContext, selection):
|
||||
return srcContext in ("priceViewFull", "priceViewMinimal")
|
||||
|
||||
def getText(self, itmContext, selection):
|
||||
return "Include in total"
|
||||
|
||||
def addOption(self, menu, option):
|
||||
label = option
|
||||
id = ContextMenu.nextID()
|
||||
self.optionIds[id] = option
|
||||
menuItem = wx.MenuItem(menu, id, label, kind=wx.ITEM_CHECK)
|
||||
menu.Bind(wx.EVT_MENU, self.handleMode, menuItem)
|
||||
return menuItem
|
||||
|
||||
def getSubMenu(self, context, selection, rootMenu, i, pitem):
|
||||
msw = True if "wxMSW" in wx.PlatformInfo else False
|
||||
self.context = context
|
||||
self.optionIds = {}
|
||||
|
||||
sub = wx.Menu()
|
||||
|
||||
for option in self.optionList:
|
||||
menuItem = self.addOption(rootMenu if msw else sub, option)
|
||||
sub.AppendItem(menuItem)
|
||||
menuItem.Check(self.settings.get(option.lower()))
|
||||
|
||||
return sub
|
||||
|
||||
def handleMode(self, event):
|
||||
option = self.optionIds[event.Id]
|
||||
self.settings.set(option.lower(), event.Int)
|
||||
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
|
||||
|
||||
|
||||
PriceOptions.register()
|
||||
@@ -2,7 +2,7 @@
|
||||
import wx
|
||||
from gui.contextMenu import ContextMenu
|
||||
import gui.mainFrame
|
||||
from gui.shipBrowser import Stage3Selected
|
||||
from gui.builtinShipBrowser.events import Stage3Selected
|
||||
from service.fit import Fit
|
||||
from service.settings import ContextMenuSettings
|
||||
|
||||
|
||||
0
gui/builtinItemStatsViews/__init__.py
Normal file
0
gui/builtinItemStatsViews/__init__.py
Normal file
18
gui/builtinItemStatsViews/helpers.py
Normal file
18
gui/builtinItemStatsViews/helpers.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx.lib.mixins.listctrl as listmix
|
||||
|
||||
|
||||
class AutoListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter):
|
||||
def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
|
||||
wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
|
||||
listmix.ListCtrlAutoWidthMixin.__init__(self)
|
||||
listmix.ListRowHighlighter.__init__(self)
|
||||
|
||||
|
||||
class AutoListCtrlNoHighlight(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter):
|
||||
def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
|
||||
wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
|
||||
listmix.ListCtrlAutoWidthMixin.__init__(self)
|
||||
447
gui/builtinItemStatsViews/itemAffectedBy.py
Normal file
447
gui/builtinItemStatsViews/itemAffectedBy.py
Normal file
@@ -0,0 +1,447 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from eos.saveddata.mode import Mode
|
||||
from eos.saveddata.character import Skill
|
||||
from eos.saveddata.implant import Implant
|
||||
from eos.saveddata.booster import Booster
|
||||
from eos.saveddata.drone import Drone
|
||||
from eos.saveddata.fighter import Fighter
|
||||
from eos.saveddata.module import Module
|
||||
from eos.saveddata.ship import Ship
|
||||
from eos.saveddata.citadel import Citadel
|
||||
from eos.saveddata.fit import Fit
|
||||
|
||||
import gui.mainFrame
|
||||
from gui.contextMenu import ContextMenu
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
|
||||
|
||||
class ItemAffectedBy(wx.Panel):
|
||||
ORDER = [Fit, Ship, Citadel, Mode, Module, Drone, Fighter, Implant, Booster, Skill]
|
||||
|
||||
def __init__(self, parent, stuff, item):
|
||||
wx.Panel.__init__(self, parent)
|
||||
self.stuff = stuff
|
||||
self.item = item
|
||||
|
||||
self.activeFit = gui.mainFrame.MainFrame.getInstance().getActiveFit()
|
||||
|
||||
self.showRealNames = False
|
||||
self.showAttrView = False
|
||||
self.expand = -1
|
||||
|
||||
self.treeItems = []
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.affectedBy = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER)
|
||||
mainSizer.Add(self.affectedBy, 1, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
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.toggleExpandBtn = wx.ToggleButton(self, wx.ID_ANY, u"Expand All", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.toggleExpandBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.toggleNameBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle Names", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.toggleNameBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle View", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
if stuff is not None:
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT)
|
||||
bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshTree)
|
||||
|
||||
self.toggleNameBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleNameMode)
|
||||
self.toggleExpandBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleExpand)
|
||||
self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode)
|
||||
|
||||
mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT)
|
||||
self.SetSizer(mainSizer)
|
||||
self.PopulateTree()
|
||||
self.Layout()
|
||||
self.affectedBy.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.scheduleMenu)
|
||||
|
||||
def scheduleMenu(self, event):
|
||||
event.Skip()
|
||||
wx.CallAfter(self.spawnMenu, event.Item)
|
||||
|
||||
def spawnMenu(self, item):
|
||||
self.affectedBy.SelectItem(item)
|
||||
|
||||
stuff = self.affectedBy.GetPyData(item)
|
||||
# String is set as data when we are dealing with attributes, not stuff containers
|
||||
if stuff is None or isinstance(stuff, basestring):
|
||||
return
|
||||
contexts = []
|
||||
|
||||
# Skills are different in that they don't have itemModifiedAttributes,
|
||||
# which is needed if we send the container to itemStats dialog. So
|
||||
# instead, we send the item.
|
||||
type_ = stuff.__class__.__name__
|
||||
contexts.append(("itemStats", type_))
|
||||
menu = ContextMenu.getMenu(stuff if type_ != "Skill" else stuff.item, *contexts)
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def ExpandCollapseTree(self):
|
||||
|
||||
self.Freeze()
|
||||
if self.expand == 1:
|
||||
self.affectedBy.ExpandAll()
|
||||
else:
|
||||
try:
|
||||
self.affectedBy.CollapseAll()
|
||||
except:
|
||||
pass
|
||||
|
||||
self.Thaw()
|
||||
|
||||
def ToggleExpand(self, event):
|
||||
self.expand *= -1
|
||||
self.ExpandCollapseTree()
|
||||
|
||||
def ToggleViewTree(self):
|
||||
self.Freeze()
|
||||
|
||||
for item in self.treeItems:
|
||||
change = self.affectedBy.GetPyData(item)
|
||||
display = self.affectedBy.GetItemText(item)
|
||||
self.affectedBy.SetItemText(item, change)
|
||||
self.affectedBy.SetPyData(item, display)
|
||||
|
||||
self.Thaw()
|
||||
|
||||
def UpdateTree(self):
|
||||
self.Freeze()
|
||||
self.affectedBy.DeleteAllItems()
|
||||
self.PopulateTree()
|
||||
self.Thaw()
|
||||
|
||||
def RefreshTree(self, event):
|
||||
self.UpdateTree()
|
||||
event.Skip()
|
||||
|
||||
def ToggleViewMode(self, event):
|
||||
self.showAttrView = not self.showAttrView
|
||||
self.affectedBy.DeleteAllItems()
|
||||
self.PopulateTree()
|
||||
event.Skip()
|
||||
|
||||
def ToggleNameMode(self, event):
|
||||
self.showRealNames = not self.showRealNames
|
||||
self.ToggleViewTree()
|
||||
event.Skip()
|
||||
|
||||
def PopulateTree(self):
|
||||
# sheri was here
|
||||
del self.treeItems[:]
|
||||
root = self.affectedBy.AddRoot("WINPWNZ0R")
|
||||
self.affectedBy.SetPyData(root, None)
|
||||
|
||||
self.imageList = wx.ImageList(16, 16)
|
||||
self.affectedBy.SetImageList(self.imageList)
|
||||
|
||||
if self.showAttrView:
|
||||
self.buildAttributeView(root)
|
||||
else:
|
||||
self.buildModuleView(root)
|
||||
|
||||
self.ExpandCollapseTree()
|
||||
|
||||
def sortAttrDisplayName(self, attr):
|
||||
info = self.stuff.item.attributes.get(attr)
|
||||
if info and info.displayName != "":
|
||||
return info.displayName
|
||||
|
||||
return attr
|
||||
|
||||
def buildAttributeView(self, root):
|
||||
"""
|
||||
We first build a usable dictionary of items. The key is either a fit
|
||||
if the afflictions stem from a projected fit, or self.stuff if they
|
||||
are local afflictions (everything else, even gang boosts at this time)
|
||||
The value of this is yet another dictionary in the following format:
|
||||
|
||||
"attribute name": {
|
||||
"Module Name": [
|
||||
class of affliction,
|
||||
affliction item (required due to GH issue #335)
|
||||
modifier type
|
||||
amount of modification
|
||||
whether this affliction was projected
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
attributes = self.stuff.itemModifiedAttributes if self.item == self.stuff.item else self.stuff.chargeModifiedAttributes
|
||||
container = {}
|
||||
for attrName in attributes.iterAfflictions():
|
||||
# if value is 0 or there has been no change from original to modified, return
|
||||
if attributes[attrName] == (attributes.getOriginal(attrName, 0)):
|
||||
continue
|
||||
|
||||
for fit, afflictors in attributes.getAfflictions(attrName).iteritems():
|
||||
for afflictor, modifier, amount, used in afflictors:
|
||||
|
||||
if not used or afflictor.item is None:
|
||||
continue
|
||||
|
||||
if fit.ID != self.activeFit:
|
||||
# affliction fit does not match our fit
|
||||
if fit not in container:
|
||||
container[fit] = {}
|
||||
items = container[fit]
|
||||
else:
|
||||
# local afflictions
|
||||
if self.stuff not in container:
|
||||
container[self.stuff] = {}
|
||||
items = container[self.stuff]
|
||||
|
||||
# items hold our module: info mappings
|
||||
if attrName not in items:
|
||||
items[attrName] = []
|
||||
|
||||
if afflictor == self.stuff and getattr(afflictor, 'charge', None):
|
||||
# we are showing a charges modifications, see #335
|
||||
item = afflictor.charge
|
||||
else:
|
||||
item = afflictor.item
|
||||
|
||||
items[attrName].append(
|
||||
(type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False)))
|
||||
|
||||
# Make sure projected fits are on top
|
||||
rootOrder = container.keys()
|
||||
rootOrder.sort(key=lambda x: self.ORDER.index(type(x)))
|
||||
|
||||
# Now, we take our created dictionary and start adding stuff to our tree
|
||||
for thing in rootOrder:
|
||||
# This block simply directs which parent we are adding to (root or projected fit)
|
||||
if thing == self.stuff:
|
||||
parent = root
|
||||
else: # projected fit
|
||||
icon = self.imageList.Add(BitmapLoader.getBitmap("ship_small", "gui"))
|
||||
child = self.affectedBy.AppendItem(root, "{} ({})".format(thing.name, thing.ship.item.name), icon)
|
||||
parent = child
|
||||
|
||||
attributes = container[thing]
|
||||
attrOrder = sorted(attributes.keys(), key=self.sortAttrDisplayName)
|
||||
|
||||
for attrName in attrOrder:
|
||||
attrInfo = self.stuff.item.attributes.get(attrName)
|
||||
displayName = attrInfo.displayName if attrInfo and attrInfo.displayName != "" else attrName
|
||||
|
||||
if attrInfo:
|
||||
if attrInfo.icon is not None:
|
||||
iconFile = attrInfo.icon.iconFile
|
||||
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("7_15", "icons"))
|
||||
else:
|
||||
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons"))
|
||||
|
||||
if self.showRealNames:
|
||||
display = attrName
|
||||
saved = displayName
|
||||
else:
|
||||
display = displayName
|
||||
saved = attrName
|
||||
|
||||
# this is the attribute node
|
||||
child = self.affectedBy.AppendItem(parent, display, attrIcon)
|
||||
self.affectedBy.SetPyData(child, saved)
|
||||
self.treeItems.append(child)
|
||||
|
||||
items = attributes[attrName]
|
||||
items.sort(key=lambda x: self.ORDER.index(x[0]))
|
||||
for itemInfo in items:
|
||||
afflictorType, afflictor, item, attrModifier, attrAmount, projected = itemInfo
|
||||
|
||||
if afflictorType == Ship:
|
||||
itemIcon = self.imageList.Add(BitmapLoader.getBitmap("ship_small", "gui"))
|
||||
elif item.icon:
|
||||
bitmap = BitmapLoader.getBitmap(item.icon.iconFile, "icons")
|
||||
itemIcon = self.imageList.Add(bitmap) if bitmap else -1
|
||||
else:
|
||||
itemIcon = -1
|
||||
|
||||
displayStr = item.name
|
||||
|
||||
if projected:
|
||||
displayStr += " (projected)"
|
||||
|
||||
penalized = ""
|
||||
if '*' in attrModifier:
|
||||
if 's' in attrModifier:
|
||||
penalized += "(penalized)"
|
||||
if 'r' in attrModifier:
|
||||
penalized += "(resisted)"
|
||||
attrModifier = "*"
|
||||
|
||||
# this is the Module node, the attribute will be attached to this
|
||||
display = "%s %s %.2f %s" % (displayStr, attrModifier, attrAmount, penalized)
|
||||
treeItem = self.affectedBy.AppendItem(child, display, itemIcon)
|
||||
self.affectedBy.SetPyData(treeItem, afflictor)
|
||||
|
||||
def buildModuleView(self, root):
|
||||
"""
|
||||
We first build a usable dictionary of items. The key is either a fit
|
||||
if the afflictions stem from a projected fit, or self.stuff if they
|
||||
are local afflictions (everything else, even gang boosts at this time)
|
||||
The value of this is yet another dictionary in the following format:
|
||||
|
||||
"Module Name": [
|
||||
class of affliction,
|
||||
set of afflictors (such as 2 of the same module),
|
||||
info on affliction (attribute name, modifier, and modification amount),
|
||||
item that will be used to determine icon (required due to GH issue #335)
|
||||
whether this affliction is actually used (unlearned skills are not used)
|
||||
]
|
||||
"""
|
||||
|
||||
attributes = self.stuff.itemModifiedAttributes if self.item == self.stuff.item else self.stuff.chargeModifiedAttributes
|
||||
container = {}
|
||||
for attrName in attributes.iterAfflictions():
|
||||
# if value is 0 or there has been no change from original to modified, return
|
||||
if attributes[attrName] == (attributes.getOriginal(attrName, 0)):
|
||||
continue
|
||||
|
||||
for fit, afflictors in attributes.getAfflictions(attrName).iteritems():
|
||||
for afflictor, modifier, amount, used in afflictors:
|
||||
if not used or getattr(afflictor, 'item', None) is None:
|
||||
continue
|
||||
|
||||
if fit.ID != self.activeFit:
|
||||
# affliction fit does not match our fit
|
||||
if fit not in container:
|
||||
container[fit] = {}
|
||||
items = container[fit]
|
||||
else:
|
||||
# local afflictions
|
||||
if self.stuff not in container:
|
||||
container[self.stuff] = {}
|
||||
items = container[self.stuff]
|
||||
|
||||
if afflictor == self.stuff and getattr(afflictor, 'charge', None):
|
||||
# we are showing a charges modifications, see #335
|
||||
item = afflictor.charge
|
||||
else:
|
||||
item = afflictor.item
|
||||
|
||||
# items hold our module: info mappings
|
||||
if item.name not in items:
|
||||
items[item.name] = [type(afflictor), set(), [], item, getattr(afflictor, "projected", False)]
|
||||
|
||||
info = items[item.name]
|
||||
info[1].add(afflictor)
|
||||
# If info[1] > 1, there are two separate modules working.
|
||||
# Check to make sure we only include the modifier once
|
||||
# See GH issue 154
|
||||
if len(info[1]) > 1 and (attrName, modifier, amount) in info[2]:
|
||||
continue
|
||||
info[2].append((attrName, modifier, amount))
|
||||
|
||||
# Make sure projected fits are on top
|
||||
rootOrder = container.keys()
|
||||
rootOrder.sort(key=lambda x: self.ORDER.index(type(x)))
|
||||
|
||||
# Now, we take our created dictionary and start adding stuff to our tree
|
||||
for thing in rootOrder:
|
||||
# This block simply directs which parent we are adding to (root or projected fit)
|
||||
if thing == self.stuff:
|
||||
parent = root
|
||||
else: # projected fit
|
||||
icon = self.imageList.Add(BitmapLoader.getBitmap("ship_small", "gui"))
|
||||
child = self.affectedBy.AppendItem(root, "{} ({})".format(thing.name, thing.ship.item.name), icon)
|
||||
parent = child
|
||||
|
||||
items = container[thing]
|
||||
order = items.keys()
|
||||
order.sort(key=lambda x: (self.ORDER.index(items[x][0]), x))
|
||||
|
||||
for itemName in order:
|
||||
info = items[itemName]
|
||||
afflictorType, afflictors, attrData, item, projected = info
|
||||
counter = len(afflictors)
|
||||
if afflictorType == Ship:
|
||||
itemIcon = self.imageList.Add(BitmapLoader.getBitmap("ship_small", "gui"))
|
||||
elif item.icon:
|
||||
bitmap = BitmapLoader.getBitmap(item.icon.iconFile, "icons")
|
||||
itemIcon = self.imageList.Add(bitmap) if bitmap else -1
|
||||
else:
|
||||
itemIcon = -1
|
||||
|
||||
displayStr = itemName
|
||||
|
||||
if counter > 1:
|
||||
displayStr += " x {}".format(counter)
|
||||
|
||||
if projected:
|
||||
displayStr += " (projected)"
|
||||
|
||||
# this is the Module node, the attribute will be attached to this
|
||||
child = self.affectedBy.AppendItem(parent, displayStr, itemIcon)
|
||||
self.affectedBy.SetPyData(child, afflictors.pop())
|
||||
|
||||
if counter > 0:
|
||||
attributes = []
|
||||
for attrName, attrModifier, attrAmount in attrData:
|
||||
attrInfo = self.stuff.item.attributes.get(attrName)
|
||||
displayName = attrInfo.displayName if attrInfo else ""
|
||||
|
||||
if attrInfo:
|
||||
if attrInfo.icon is not None:
|
||||
iconFile = attrInfo.icon.iconFile
|
||||
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("7_15", "icons"))
|
||||
else:
|
||||
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons"))
|
||||
|
||||
penalized = ""
|
||||
if '*' in attrModifier:
|
||||
if 's' in attrModifier:
|
||||
penalized += "(penalized)"
|
||||
if 'r' in attrModifier:
|
||||
penalized += "(resisted)"
|
||||
attrModifier = "*"
|
||||
|
||||
attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier,
|
||||
attrAmount, penalized, attrIcon))
|
||||
|
||||
attrSorted = sorted(attributes, key=lambda attribName: attribName[0])
|
||||
for attr in attrSorted:
|
||||
attrName, displayName, attrModifier, attrAmount, penalized, attrIcon = attr
|
||||
|
||||
if self.showRealNames:
|
||||
display = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized)
|
||||
saved = "%s %s %.2f %s" % (
|
||||
displayName if displayName != "" else attrName,
|
||||
attrModifier,
|
||||
attrAmount,
|
||||
penalized
|
||||
)
|
||||
else:
|
||||
display = "%s %s %.2f %s" % (
|
||||
displayName if displayName != "" else attrName,
|
||||
attrModifier,
|
||||
attrAmount,
|
||||
penalized
|
||||
)
|
||||
saved = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized)
|
||||
|
||||
treeitem = self.affectedBy.AppendItem(child, display, attrIcon)
|
||||
self.affectedBy.SetPyData(treeitem, saved)
|
||||
self.treeItems.append(treeitem)
|
||||
273
gui/builtinItemStatsViews/itemAttributes.py
Normal file
273
gui/builtinItemStatsViews/itemAttributes.py
Normal file
@@ -0,0 +1,273 @@
|
||||
import sys
|
||||
import csv
|
||||
import config
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from helpers import AutoListCtrl
|
||||
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
from service.market import Market
|
||||
from service.attribute import Attribute
|
||||
from gui.utils.numberFormatter import formatAmount
|
||||
|
||||
|
||||
class ItemParams(wx.Panel):
|
||||
def __init__(self, parent, stuff, item, context=None):
|
||||
wx.Panel.__init__(self, parent)
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.paramList = AutoListCtrl(self, wx.ID_ANY,
|
||||
style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER)
|
||||
mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
self.toggleView = 1
|
||||
self.stuff = stuff
|
||||
self.item = item
|
||||
self.attrInfo = {}
|
||||
self.attrValues = {}
|
||||
self._fetchValues()
|
||||
|
||||
self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
|
||||
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, wx.DefaultSize,
|
||||
0)
|
||||
bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, u"Export Item Stats", wx.DefaultPosition, wx.DefaultSize,
|
||||
0)
|
||||
bSizer.Add(self.exportStatsBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
if stuff is not None:
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT)
|
||||
bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshValues)
|
||||
|
||||
mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT)
|
||||
|
||||
self.PopulateList()
|
||||
|
||||
self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode)
|
||||
self.exportStatsBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ExportItemStats)
|
||||
|
||||
def _fetchValues(self):
|
||||
if self.stuff is None:
|
||||
self.attrInfo.clear()
|
||||
self.attrValues.clear()
|
||||
self.attrInfo.update(self.item.attributes)
|
||||
self.attrValues.update(self.item.attributes)
|
||||
elif self.stuff.item == self.item:
|
||||
self.attrInfo.clear()
|
||||
self.attrValues.clear()
|
||||
self.attrInfo.update(self.stuff.item.attributes)
|
||||
self.attrValues.update(self.stuff.itemModifiedAttributes)
|
||||
elif self.stuff.charge == self.item:
|
||||
self.attrInfo.clear()
|
||||
self.attrValues.clear()
|
||||
self.attrInfo.update(self.stuff.charge.attributes)
|
||||
self.attrValues.update(self.stuff.chargeModifiedAttributes)
|
||||
# When item for stats window no longer exists, don't change anything
|
||||
else:
|
||||
return
|
||||
|
||||
def UpdateList(self):
|
||||
self.Freeze()
|
||||
self.paramList.ClearAll()
|
||||
self.PopulateList()
|
||||
self.Thaw()
|
||||
self.paramList.resizeLastColumn(100)
|
||||
|
||||
def RefreshValues(self, event):
|
||||
self._fetchValues()
|
||||
self.UpdateList()
|
||||
event.Skip()
|
||||
|
||||
def ToggleViewMode(self, event):
|
||||
self.toggleView *= -1
|
||||
self.UpdateList()
|
||||
event.Skip()
|
||||
|
||||
def ExportItemStats(self, event):
|
||||
exportFileName = self.item.name + " (" + str(self.item.ID) + ").csv"
|
||||
|
||||
saveFileDialog = wx.FileDialog(self, "Save CSV file", "", exportFileName,
|
||||
"CSV files (*.csv)|*.csv", wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
|
||||
|
||||
if saveFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||
return # the user hit cancel...
|
||||
|
||||
with open(saveFileDialog.GetPath(), "wb") as exportFile:
|
||||
writer = csv.writer(exportFile, delimiter=',')
|
||||
|
||||
writer.writerow(
|
||||
[
|
||||
"ID",
|
||||
"Internal Name",
|
||||
"Friendly Name",
|
||||
"Modified Value",
|
||||
"Base Value",
|
||||
]
|
||||
)
|
||||
|
||||
for attribute in self.attrValues:
|
||||
|
||||
try:
|
||||
attribute_id = self.attrInfo[attribute].ID
|
||||
except (KeyError, AttributeError):
|
||||
attribute_id = ''
|
||||
|
||||
try:
|
||||
attribute_name = self.attrInfo[attribute].name
|
||||
except (KeyError, AttributeError):
|
||||
attribute_name = attribute
|
||||
|
||||
try:
|
||||
attribute_displayname = self.attrInfo[attribute].displayName
|
||||
except (KeyError, AttributeError):
|
||||
attribute_displayname = ''
|
||||
|
||||
try:
|
||||
attribute_value = self.attrInfo[attribute].value
|
||||
except (KeyError, AttributeError):
|
||||
attribute_value = ''
|
||||
|
||||
try:
|
||||
attribute_modified_value = self.attrValues[attribute].value
|
||||
except (KeyError, AttributeError):
|
||||
attribute_modified_value = self.attrValues[attribute]
|
||||
|
||||
writer.writerow(
|
||||
[
|
||||
attribute_id,
|
||||
attribute_name,
|
||||
attribute_displayname,
|
||||
attribute_modified_value,
|
||||
attribute_value,
|
||||
]
|
||||
)
|
||||
|
||||
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.imageList = wx.ImageList(16, 16)
|
||||
self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL)
|
||||
|
||||
names = list(self.attrValues.iterkeys())
|
||||
names.sort()
|
||||
|
||||
idNameMap = {}
|
||||
idCount = 0
|
||||
for name in names:
|
||||
info = self.attrInfo.get(name)
|
||||
att = self.attrValues[name]
|
||||
|
||||
valDefault = getattr(info, "value", None)
|
||||
valueDefault = valDefault if valDefault is not None else att
|
||||
|
||||
val = getattr(att, "value", None)
|
||||
value = val if val is not None else att
|
||||
|
||||
if info and info.displayName and self.toggleView == 1:
|
||||
attrName = info.displayName
|
||||
else:
|
||||
attrName = name
|
||||
|
||||
if info and config.debug:
|
||||
attrName += " ({})".format(info.ID)
|
||||
|
||||
if info:
|
||||
if info.icon is not None:
|
||||
iconFile = info.icon.iconFile
|
||||
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("7_15", "icons"))
|
||||
else:
|
||||
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons"))
|
||||
|
||||
index = self.paramList.InsertImageStringItem(sys.maxint, attrName, attrIcon)
|
||||
idNameMap[idCount] = attrName
|
||||
self.paramList.SetItemData(index, idCount)
|
||||
idCount += 1
|
||||
|
||||
if self.toggleView != 1:
|
||||
valueUnit = str(value)
|
||||
elif info and info.unit:
|
||||
valueUnit = self.TranslateValueUnit(value, info.unit.displayName, info.unit.name)
|
||||
else:
|
||||
valueUnit = formatAmount(value, 3, 0, 0)
|
||||
|
||||
if self.toggleView != 1:
|
||||
valueUnitDefault = str(valueDefault)
|
||||
elif info and info.unit:
|
||||
valueUnitDefault = self.TranslateValueUnit(valueDefault, info.unit.displayName, info.unit.name)
|
||||
else:
|
||||
valueUnitDefault = formatAmount(valueDefault, 3, 0, 0)
|
||||
|
||||
self.paramList.SetStringItem(index, 1, valueUnit)
|
||||
if self.stuff is not None:
|
||||
self.paramList.SetStringItem(index, 2, valueUnitDefault)
|
||||
|
||||
self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2]))
|
||||
self.paramList.RefreshRows()
|
||||
self.totalAttrsLabel.SetLabel("%d attributes. " % idCount)
|
||||
self.Layout()
|
||||
|
||||
@staticmethod
|
||||
def TranslateValueUnit(value, unitName, unitDisplayName):
|
||||
def itemIDCallback():
|
||||
item = Market.getInstance().getItem(value)
|
||||
return "%s (%d)" % (item.name, value) if item is not None else str(value)
|
||||
|
||||
def groupIDCallback():
|
||||
group = Market.getInstance().getGroup(value)
|
||||
return "%s (%d)" % (group.name, value) if group is not None else str(value)
|
||||
|
||||
def attributeIDCallback():
|
||||
attribute = Attribute.getInstance().getAttributeInfo(value)
|
||||
return "%s (%d)" % (attribute.name.capitalize(), value)
|
||||
|
||||
trans = {
|
||||
"Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName),
|
||||
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
|
||||
"Modifier Percent" : (
|
||||
lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
|
||||
"Volume" : (lambda: value, u"m\u00B3"),
|
||||
"Sizeclass" : (lambda: value, ""),
|
||||
"Absolute Percent" : (lambda: (value * 100), unitName),
|
||||
"Milliseconds" : (lambda: value / 1000.0, unitName),
|
||||
"typeID" : (itemIDCallback, ""),
|
||||
"groupID" : (groupIDCallback, ""),
|
||||
"attributeID" : (attributeIDCallback, "")
|
||||
}
|
||||
|
||||
override = trans.get(unitDisplayName)
|
||||
if override is not None:
|
||||
v = override[0]()
|
||||
if isinstance(v, str):
|
||||
fvalue = v
|
||||
elif isinstance(v, (int, float, long)):
|
||||
fvalue = formatAmount(v, 3, 0, 0)
|
||||
else:
|
||||
fvalue = v
|
||||
return "%s %s" % (fvalue, override[1])
|
||||
else:
|
||||
return "%s %s" % (formatAmount(value, 3, 0), unitName)
|
||||
208
gui/builtinItemStatsViews/itemCompare.py
Normal file
208
gui/builtinItemStatsViews/itemCompare.py
Normal file
@@ -0,0 +1,208 @@
|
||||
import sys
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from helpers import AutoListCtrl
|
||||
from service.price import Price as ServicePrice
|
||||
from service.market import Market
|
||||
from service.attribute import Attribute
|
||||
from gui.utils.numberFormatter import formatAmount
|
||||
|
||||
|
||||
class ItemCompare(wx.Panel):
|
||||
def __init__(self, parent, stuff, item, items, context=None):
|
||||
# Start dealing with Price stuff to get that thread going
|
||||
sPrice = ServicePrice.getInstance()
|
||||
sPrice.getPrices(items, self.UpdateList)
|
||||
|
||||
wx.Panel.__init__(self, parent)
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.paramList = AutoListCtrl(self, wx.ID_ANY,
|
||||
style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER)
|
||||
mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
self.toggleView = 1
|
||||
self.stuff = stuff
|
||||
self.currentSort = None
|
||||
self.sortReverse = False
|
||||
self.item = item
|
||||
self.items = sorted(items,
|
||||
key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else None)
|
||||
self.attrs = {}
|
||||
|
||||
# get a dict of attrName: attrInfo of all unique attributes across all items
|
||||
for item in self.items:
|
||||
for attr in item.attributes.keys():
|
||||
if item.attributes[attr].info.displayName:
|
||||
self.attrs[attr] = item.attributes[attr].info
|
||||
|
||||
# Process attributes for items and find ones that differ
|
||||
for attr in self.attrs.keys():
|
||||
value = None
|
||||
|
||||
for item in self.items:
|
||||
# we can automatically break here if this item doesn't have the attribute,
|
||||
# as that means at least one item did
|
||||
if attr not in item.attributes:
|
||||
break
|
||||
|
||||
# this is the first attribute for the item set, set the initial value
|
||||
if value is None:
|
||||
value = item.attributes[attr].value
|
||||
continue
|
||||
|
||||
if attr not in item.attributes or item.attributes[attr].value != value:
|
||||
break
|
||||
else:
|
||||
# attribute values were all the same, delete
|
||||
del self.attrs[attr]
|
||||
|
||||
self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
|
||||
|
||||
self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition,
|
||||
wx.DefaultSize, 0)
|
||||
bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
|
||||
self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize,
|
||||
wx.BU_EXACTFIT)
|
||||
bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL)
|
||||
self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshValues)
|
||||
|
||||
mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT)
|
||||
|
||||
self.PopulateList()
|
||||
|
||||
self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode)
|
||||
self.Bind(wx.EVT_LIST_COL_CLICK, self.SortCompareCols)
|
||||
|
||||
def SortCompareCols(self, event):
|
||||
self.Freeze()
|
||||
self.paramList.ClearAll()
|
||||
self.PopulateList(event.Column)
|
||||
self.Thaw()
|
||||
|
||||
def UpdateList(self, items=None):
|
||||
# We do nothing with `items`, but it gets returned by the price service thread
|
||||
self.Freeze()
|
||||
self.paramList.ClearAll()
|
||||
self.PopulateList()
|
||||
self.Thaw()
|
||||
self.paramList.resizeLastColumn(100)
|
||||
|
||||
def RefreshValues(self, event):
|
||||
self.UpdateList()
|
||||
event.Skip()
|
||||
|
||||
def ToggleViewMode(self, event):
|
||||
self.toggleView *= -1
|
||||
self.UpdateList()
|
||||
event.Skip()
|
||||
|
||||
def processPrices(self, prices):
|
||||
for i, price in enumerate(prices):
|
||||
self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(price.value, 3, 3, 9, currency=True))
|
||||
|
||||
def PopulateList(self, sort=None):
|
||||
|
||||
if sort is not None and self.currentSort == sort:
|
||||
self.sortReverse = not self.sortReverse
|
||||
else:
|
||||
self.currentSort = sort
|
||||
self.sortReverse = False
|
||||
|
||||
if sort is not None:
|
||||
if sort == 0: # Name sort
|
||||
func = lambda _val: _val.name
|
||||
else:
|
||||
try:
|
||||
# Remember to reduce by 1, because the attrs array
|
||||
# starts at 0 while the list has the item name as column 0.
|
||||
attr = str(self.attrs.keys()[sort - 1])
|
||||
func = lambda _val: _val.attributes[attr].value if attr in _val.attributes else None
|
||||
except IndexError:
|
||||
# Clicked on a column that's not part of our array (price most likely)
|
||||
self.sortReverse = False
|
||||
func = lambda _val: _val.attributes['metaLevel'].value if 'metaLevel' in _val.attributes else None
|
||||
|
||||
self.items = sorted(self.items, key=func, reverse=self.sortReverse)
|
||||
|
||||
self.paramList.InsertColumn(0, "Item")
|
||||
self.paramList.SetColumnWidth(0, 200)
|
||||
|
||||
for i, attr in enumerate(self.attrs.keys()):
|
||||
name = self.attrs[attr].displayName if self.attrs[attr].displayName else attr
|
||||
self.paramList.InsertColumn(i + 1, name)
|
||||
self.paramList.SetColumnWidth(i + 1, 120)
|
||||
|
||||
self.paramList.InsertColumn(len(self.attrs) + 1, "Price")
|
||||
self.paramList.SetColumnWidth(len(self.attrs) + 1, 60)
|
||||
|
||||
for item in self.items:
|
||||
i = self.paramList.InsertStringItem(sys.maxint, item.name)
|
||||
for x, attr in enumerate(self.attrs.keys()):
|
||||
if attr in item.attributes:
|
||||
info = self.attrs[attr]
|
||||
value = item.attributes[attr].value
|
||||
if self.toggleView != 1:
|
||||
valueUnit = str(value)
|
||||
elif info and info.unit and self.toggleView == 1:
|
||||
valueUnit = self.TranslateValueUnit(value, info.unit.displayName, info.unit.name)
|
||||
else:
|
||||
valueUnit = formatAmount(value, 3, 0, 0)
|
||||
|
||||
self.paramList.SetStringItem(i, x + 1, valueUnit)
|
||||
|
||||
# Add prices
|
||||
self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(item.price.price, 3, 3, 9, currency=True))
|
||||
|
||||
self.paramList.RefreshRows()
|
||||
self.Layout()
|
||||
|
||||
@staticmethod
|
||||
def TranslateValueUnit(value, unitName, unitDisplayName):
|
||||
def itemIDCallback():
|
||||
item = Market.getInstance().getItem(value)
|
||||
return "%s (%d)" % (item.name, value) if item is not None else str(value)
|
||||
|
||||
def groupIDCallback():
|
||||
group = Market.getInstance().getGroup(value)
|
||||
return "%s (%d)" % (group.name, value) if group is not None else str(value)
|
||||
|
||||
def attributeIDCallback():
|
||||
attribute = Attribute.getInstance().getAttributeInfo(value)
|
||||
return "%s (%d)" % (attribute.name.capitalize(), value)
|
||||
|
||||
trans = {
|
||||
"Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName),
|
||||
"Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName),
|
||||
"Modifier Percent" : (lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName),
|
||||
"Volume" : (lambda: value, u"m\u00B3"),
|
||||
"Sizeclass" : (lambda: value, ""),
|
||||
"Absolute Percent" : (lambda: (value * 100), unitName),
|
||||
"Milliseconds" : (lambda: value / 1000.0, unitName),
|
||||
"typeID" : (itemIDCallback, ""),
|
||||
"groupID" : (groupIDCallback, ""),
|
||||
"attributeID" : (attributeIDCallback, "")
|
||||
}
|
||||
|
||||
override = trans.get(unitDisplayName)
|
||||
if override is not None:
|
||||
v = override[0]()
|
||||
if isinstance(v, str):
|
||||
fvalue = v
|
||||
elif isinstance(v, (int, float, long)):
|
||||
fvalue = formatAmount(v, 3, 0, 0)
|
||||
else:
|
||||
fvalue = v
|
||||
return "%s %s" % (fvalue, override[1])
|
||||
else:
|
||||
return "%s %s" % (formatAmount(value, 3, 0), unitName)
|
||||
53
gui/builtinItemStatsViews/itemDependants.py
Normal file
53
gui/builtinItemStatsViews/itemDependants.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
|
||||
|
||||
class ItemDependents(wx.Panel):
|
||||
def __init__(self, parent, stuff, item):
|
||||
wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
|
||||
|
||||
# itemId is set by the parent.
|
||||
self.romanNb = ["0", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"]
|
||||
self.skillIdHistory = []
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.reqTree = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER)
|
||||
|
||||
mainSizer.Add(self.reqTree, 1, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
self.SetSizer(mainSizer)
|
||||
self.root = self.reqTree.AddRoot("WINRARZOR")
|
||||
self.reqTree.SetPyData(self.root, None)
|
||||
|
||||
self.imageList = wx.ImageList(16, 16)
|
||||
self.reqTree.SetImageList(self.imageList)
|
||||
skillBookId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui"))
|
||||
|
||||
self.getFullSkillTree(item, self.root, skillBookId)
|
||||
|
||||
self.Layout()
|
||||
|
||||
def getFullSkillTree(self, parentSkill, parent, sbIconId):
|
||||
levelToItems = {}
|
||||
|
||||
for item, level in parentSkill.requiredFor.iteritems():
|
||||
if level not in levelToItems:
|
||||
levelToItems[level] = []
|
||||
levelToItems[level].append(item)
|
||||
|
||||
for x in sorted(levelToItems.keys()):
|
||||
items = levelToItems[x]
|
||||
items.sort(key=lambda x: x.name)
|
||||
|
||||
child = self.reqTree.AppendItem(parent, "Level {}".format(self.romanNb[int(x)]), sbIconId)
|
||||
for item in items:
|
||||
|
||||
if item.icon:
|
||||
bitmap = BitmapLoader.getBitmap(item.icon.iconFile, "icons")
|
||||
itemIcon = self.imageList.Add(bitmap) if bitmap else -1
|
||||
else:
|
||||
itemIcon = -1
|
||||
|
||||
self.reqTree.AppendItem(child, "{}".format(item.name), itemIcon)
|
||||
33
gui/builtinItemStatsViews/itemDescription.py
Normal file
33
gui/builtinItemStatsViews/itemDescription.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
# noinspection PyPackageRequirements
|
||||
import wx.html
|
||||
import re
|
||||
|
||||
|
||||
class ItemDescription(wx.Panel):
|
||||
def __init__(self, parent, stuff, item):
|
||||
wx.Panel.__init__(self, parent)
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
bgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)
|
||||
fgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)
|
||||
|
||||
self.description = wx.html.HtmlWindow(self)
|
||||
|
||||
if not item.description:
|
||||
return
|
||||
|
||||
desc = item.description.replace("\n", "<br>")
|
||||
# Strip font tags
|
||||
desc = re.sub("<( *)font( *)color( *)=(.*?)>(?P<inside>.*?)<( *)/( *)font( *)>", "\g<inside>", desc)
|
||||
# Strip URLs
|
||||
desc = re.sub("<( *)a(.*?)>(?P<inside>.*?)<( *)/( *)a( *)>", "\g<inside>", desc)
|
||||
desc = "<body bgcolor='" + bgcolor.GetAsString(wx.C2S_HTML_SYNTAX) + "' text='" + fgcolor.GetAsString(
|
||||
wx.C2S_HTML_SYNTAX) + "' >" + desc + "</body>"
|
||||
|
||||
self.description.SetPage(desc)
|
||||
|
||||
mainSizer.Add(self.description, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.Layout()
|
||||
130
gui/builtinItemStatsViews/itemEffects.py
Normal file
130
gui/builtinItemStatsViews/itemEffects.py
Normal file
@@ -0,0 +1,130 @@
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import config
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from helpers import AutoListCtrl
|
||||
|
||||
|
||||
class ItemEffects(wx.Panel):
|
||||
def __init__(self, parent, stuff, item):
|
||||
wx.Panel.__init__(self, parent)
|
||||
self.item = item
|
||||
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.effectList = AutoListCtrl(self, wx.ID_ANY,
|
||||
style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER)
|
||||
mainSizer.Add(self.effectList, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnClick, self.effectList)
|
||||
if config.debug:
|
||||
self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnRightClick, self.effectList)
|
||||
|
||||
self.PopulateList()
|
||||
|
||||
def PopulateList(self):
|
||||
|
||||
self.effectList.InsertColumn(0, "Name")
|
||||
self.effectList.InsertColumn(1, "Active")
|
||||
self.effectList.InsertColumn(2, "Type")
|
||||
if config.debug:
|
||||
self.effectList.InsertColumn(3, "Run Time")
|
||||
self.effectList.InsertColumn(4, "ID")
|
||||
|
||||
# self.effectList.SetColumnWidth(0,385)
|
||||
|
||||
self.effectList.setResizeColumn(0)
|
||||
self.effectList.SetColumnWidth(1, 50)
|
||||
self.effectList.SetColumnWidth(2, 80)
|
||||
if config.debug:
|
||||
self.effectList.SetColumnWidth(3, 65)
|
||||
self.effectList.SetColumnWidth(4, 40)
|
||||
|
||||
item = self.item
|
||||
effects = item.effects
|
||||
names = list(effects.iterkeys())
|
||||
names.sort()
|
||||
|
||||
for name in names:
|
||||
index = self.effectList.InsertStringItem(sys.maxint, name)
|
||||
|
||||
if effects[name].isImplemented:
|
||||
if effects[name].activeByDefault:
|
||||
activeByDefault = "Yes"
|
||||
else:
|
||||
activeByDefault = "No"
|
||||
else:
|
||||
activeByDefault = ""
|
||||
|
||||
effectTypeText = ""
|
||||
if effects[name].type:
|
||||
for effectType in effects[name].type:
|
||||
effectTypeText += effectType + " "
|
||||
pass
|
||||
|
||||
if effects[name].runTime and effects[name].isImplemented:
|
||||
effectRunTime = str(effects[name].runTime)
|
||||
else:
|
||||
effectRunTime = ""
|
||||
|
||||
self.effectList.SetStringItem(index, 1, activeByDefault)
|
||||
self.effectList.SetStringItem(index, 2, effectTypeText)
|
||||
if config.debug:
|
||||
self.effectList.SetStringItem(index, 3, effectRunTime)
|
||||
self.effectList.SetStringItem(index, 4, str(effects[name].ID))
|
||||
|
||||
self.effectList.RefreshRows()
|
||||
self.Layout()
|
||||
|
||||
def OnClick(self, event):
|
||||
"""
|
||||
Debug use: toggle effects on/off.
|
||||
Affects *ALL* items that use that effect.
|
||||
Is not stateful. Will reset if Pyfa is closed and reopened.
|
||||
"""
|
||||
|
||||
try:
|
||||
activeByDefault = getattr(self.item.effects[event.GetText()], "activeByDefault")
|
||||
if activeByDefault:
|
||||
setattr(self.item.effects[event.GetText()], "activeByDefault", False)
|
||||
else:
|
||||
setattr(self.item.effects[event.GetText()], "activeByDefault", True)
|
||||
|
||||
except AttributeError:
|
||||
# Attribute doesn't exist, do nothing
|
||||
pass
|
||||
|
||||
self.RefreshValues(event)
|
||||
|
||||
@staticmethod
|
||||
def OnRightClick(event):
|
||||
"""
|
||||
Debug use: open effect file with default application.
|
||||
If effect file does not exist, create it
|
||||
"""
|
||||
|
||||
file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % event.GetText().lower())
|
||||
|
||||
if not os.path.isfile(file_):
|
||||
open(file_, 'a').close()
|
||||
|
||||
if 'wxMSW' in wx.PlatformInfo:
|
||||
os.startfile(file_)
|
||||
elif 'wxMac' in wx.PlatformInfo:
|
||||
os.system("open " + file_)
|
||||
else:
|
||||
subprocess.call(["xdg-open", file_])
|
||||
|
||||
def RefreshValues(self, event):
|
||||
self.Freeze()
|
||||
self.effectList.ClearAll()
|
||||
self.PopulateList()
|
||||
self.effectList.RefreshRows()
|
||||
self.Layout()
|
||||
self.Thaw()
|
||||
event.Skip()
|
||||
99
gui/builtinItemStatsViews/itemProperties.py
Normal file
99
gui/builtinItemStatsViews/itemProperties.py
Normal file
@@ -0,0 +1,99 @@
|
||||
import sys
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from helpers import AutoListCtrl
|
||||
|
||||
|
||||
class ItemProperties(wx.Panel):
|
||||
def __init__(self, parent, stuff, item, context=None):
|
||||
wx.Panel.__init__(self, parent)
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.paramList = AutoListCtrl(self, wx.ID_ANY,
|
||||
style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_VRULES | wx.NO_BORDER)
|
||||
mainSizer.Add(self.paramList, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
self.toggleView = 1
|
||||
self.stuff = stuff
|
||||
self.item = item
|
||||
self.attrInfo = {}
|
||||
self.attrValues = {}
|
||||
self._fetchValues()
|
||||
|
||||
self.m_staticline = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL)
|
||||
mainSizer.Add(self.m_staticline, 0, wx.EXPAND)
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
|
||||
|
||||
mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT)
|
||||
|
||||
self.PopulateList()
|
||||
|
||||
def _fetchValues(self):
|
||||
if self.stuff is None:
|
||||
self.attrInfo.clear()
|
||||
self.attrValues.clear()
|
||||
self.attrInfo.update(self.item.attributes)
|
||||
self.attrValues.update(self.item.attributes)
|
||||
elif self.stuff.item == self.item:
|
||||
self.attrInfo.clear()
|
||||
self.attrValues.clear()
|
||||
self.attrInfo.update(self.stuff.item.attributes)
|
||||
self.attrValues.update(self.stuff.itemModifiedAttributes)
|
||||
elif self.stuff.charge == self.item:
|
||||
self.attrInfo.clear()
|
||||
self.attrValues.clear()
|
||||
self.attrInfo.update(self.stuff.charge.attributes)
|
||||
self.attrValues.update(self.stuff.chargeModifiedAttributes)
|
||||
# When item for stats window no longer exists, don't change anything
|
||||
else:
|
||||
return
|
||||
|
||||
def PopulateList(self):
|
||||
self.paramList.InsertColumn(0, "Attribute")
|
||||
self.paramList.InsertColumn(1, "Current Value")
|
||||
self.paramList.SetColumnWidth(0, 110)
|
||||
self.paramList.SetColumnWidth(1, 1500)
|
||||
self.paramList.setResizeColumn(0)
|
||||
|
||||
if self.stuff:
|
||||
names = dir(self.stuff)
|
||||
else:
|
||||
names = dir(self.item)
|
||||
|
||||
names = [a for a in names if not (a.startswith('__') and a.endswith('__'))]
|
||||
|
||||
idNameMap = {}
|
||||
idCount = 0
|
||||
for name in names:
|
||||
try:
|
||||
if self.stuff:
|
||||
attrName = name.title()
|
||||
value = getattr(self.stuff, name)
|
||||
else:
|
||||
attrName = name.title()
|
||||
value = getattr(self.item, name)
|
||||
|
||||
index = self.paramList.InsertStringItem(sys.maxint, attrName)
|
||||
# index = self.paramList.InsertImageStringItem(sys.maxint, attrName)
|
||||
idNameMap[idCount] = attrName
|
||||
self.paramList.SetItemData(index, idCount)
|
||||
idCount += 1
|
||||
|
||||
valueUnit = str(value)
|
||||
|
||||
self.paramList.SetStringItem(index, 1, valueUnit)
|
||||
except:
|
||||
# TODO: Add logging to this.
|
||||
# We couldn't get a property for some reason. Skip it for now.
|
||||
continue
|
||||
|
||||
self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2]))
|
||||
self.paramList.RefreshRows()
|
||||
self.totalAttrsLabel.SetLabel("%d attributes. " % idCount)
|
||||
self.Layout()
|
||||
39
gui/builtinItemStatsViews/itemRequirements.py
Normal file
39
gui/builtinItemStatsViews/itemRequirements.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
|
||||
|
||||
class ItemRequirements(wx.Panel):
|
||||
def __init__(self, parent, stuff, item):
|
||||
wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
|
||||
|
||||
# itemId is set by the parent.
|
||||
self.romanNb = ["0", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X"]
|
||||
self.skillIdHistory = []
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.reqTree = wx.TreeCtrl(self, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.NO_BORDER)
|
||||
|
||||
mainSizer.Add(self.reqTree, 1, wx.ALL | wx.EXPAND, 0)
|
||||
|
||||
self.SetSizer(mainSizer)
|
||||
self.root = self.reqTree.AddRoot("WINRARZOR")
|
||||
self.reqTree.SetPyData(self.root, None)
|
||||
|
||||
self.imageList = wx.ImageList(16, 16)
|
||||
self.reqTree.SetImageList(self.imageList)
|
||||
skillBookId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui"))
|
||||
|
||||
self.getFullSkillTree(item, self.root, skillBookId)
|
||||
|
||||
self.reqTree.ExpandAll()
|
||||
|
||||
self.Layout()
|
||||
|
||||
def getFullSkillTree(self, parentSkill, parent, sbIconId):
|
||||
for skill, level in parentSkill.requiredSkills.iteritems():
|
||||
child = self.reqTree.AppendItem(parent, "%s %s" % (skill.name, self.romanNb[int(level)]), sbIconId)
|
||||
if skill.ID not in self.skillIdHistory:
|
||||
self.getFullSkillTree(skill, child, sbIconId)
|
||||
self.skillIdHistory.append(skill.ID)
|
||||
17
gui/builtinItemStatsViews/itemTraits.py
Normal file
17
gui/builtinItemStatsViews/itemTraits.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
# noinspection PyPackageRequirements
|
||||
import wx.html
|
||||
|
||||
|
||||
class ItemTraits(wx.Panel):
|
||||
def __init__(self, parent, stuff, item):
|
||||
wx.Panel.__init__(self, parent)
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.SetSizer(mainSizer)
|
||||
|
||||
self.traits = wx.html.HtmlWindow(self)
|
||||
self.traits.SetPage(item.traits.traitText)
|
||||
|
||||
mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0)
|
||||
self.Layout()
|
||||
@@ -4,4 +4,4 @@ import wx.lib.newevent
|
||||
ItemSelected, ITEM_SELECTED = wx.lib.newevent.NewEvent()
|
||||
|
||||
RECENTLY_USED_MODULES = -2
|
||||
MAX_RECENTLY_USED_MODULES = 20
|
||||
MAX_RECENTLY_USED_MODULES = 20
|
||||
|
||||
@@ -96,4 +96,3 @@ class MarketTree(wx.TreeCtrl):
|
||||
|
||||
self.SelectItem(item)
|
||||
self.marketBrowser.itemView.selectionMade()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from gui.builtinMarketBrowser.events import *
|
||||
import wx
|
||||
from logbook import Logger
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -10,4 +10,4 @@ class SearchBox(PFSearchBox):
|
||||
self.SetSearchBitmap(searchBitmap)
|
||||
self.SetCancelBitmap(cancelBitmap)
|
||||
self.ShowSearchButton()
|
||||
self.ShowCancelButton()
|
||||
self.ShowCancelButton()
|
||||
|
||||
@@ -90,7 +90,9 @@ class PFGeneralPref(PreferenceView):
|
||||
self.stDefaultSystem.Wrap(-1)
|
||||
priceSizer.Add(self.stDefaultSystem, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
|
||||
|
||||
self.chPriceSystem = wx.Choice(panel, choices=list(Price.systemsList.keys()))
|
||||
self.chPriceSource = wx.Choice(panel, choices=sorted(Price.sources.keys()))
|
||||
self.chPriceSystem = wx.Choice(panel, choices=Price.systemsList.keys())
|
||||
priceSizer.Add(self.chPriceSource, 1, wx.ALL | wx.EXPAND, 5)
|
||||
priceSizer.Add(self.chPriceSystem, 1, wx.ALL | wx.EXPAND, 5)
|
||||
|
||||
mainSizer.Add(priceSizer, 0, wx.ALL | wx.EXPAND, 0)
|
||||
@@ -124,6 +126,7 @@ class PFGeneralPref(PreferenceView):
|
||||
self.cbGaugeAnimation.SetValue(self.sFit.serviceFittingOptions["enableGaugeAnimation"])
|
||||
self.cbExportCharges.SetValue(self.sFit.serviceFittingOptions["exportCharges"])
|
||||
self.cbOpenFitInNew.SetValue(self.sFit.serviceFittingOptions["openFitInNew"])
|
||||
self.chPriceSource.SetStringSelection(self.sFit.serviceFittingOptions["priceSource"])
|
||||
self.chPriceSystem.SetStringSelection(self.sFit.serviceFittingOptions["priceSystem"])
|
||||
self.cbShowShipBrowserTooltip.SetValue(self.sFit.serviceFittingOptions["showShipBrowserTooltip"])
|
||||
self.intDelay.SetValue(self.sFit.serviceFittingOptions["marketSearchDelay"])
|
||||
@@ -140,6 +143,7 @@ class PFGeneralPref(PreferenceView):
|
||||
self.cbGaugeAnimation.Bind(wx.EVT_CHECKBOX, self.onCBGaugeAnimation)
|
||||
self.cbExportCharges.Bind(wx.EVT_CHECKBOX, self.onCBExportCharges)
|
||||
self.cbOpenFitInNew.Bind(wx.EVT_CHECKBOX, self.onCBOpenFitInNew)
|
||||
self.chPriceSource.Bind(wx.EVT_CHOICE, self.onPricesSourceSelection)
|
||||
self.chPriceSystem.Bind(wx.EVT_CHOICE, self.onPriceSelection)
|
||||
self.cbShowShipBrowserTooltip.Bind(wx.EVT_CHECKBOX, self.onCBShowShipBrowserTooltip)
|
||||
self.intDelay.Bind(wx.lib.intctrl.EVT_INT, self.onMarketDelayChange)
|
||||
@@ -224,5 +228,9 @@ class PFGeneralPref(PreferenceView):
|
||||
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
|
||||
event.Skip()
|
||||
|
||||
def onPricesSourceSelection(self, event):
|
||||
source = self.chPriceSource.GetString(self.chPriceSource.GetSelection())
|
||||
self.sFit.serviceFittingOptions["priceSource"] = source
|
||||
|
||||
|
||||
PFGeneralPref.register()
|
||||
|
||||
@@ -11,4 +11,4 @@ Stage1Selected, EVT_SB_STAGE1_SEL = wx.lib.newevent.NewEvent()
|
||||
Stage2Selected, EVT_SB_STAGE2_SEL = wx.lib.newevent.NewEvent()
|
||||
Stage3Selected, EVT_SB_STAGE3_SEL = wx.lib.newevent.NewEvent()
|
||||
SearchSelected, EVT_SB_SEARCH_SEL = wx.lib.newevent.NewEvent()
|
||||
ImportSelected, EVT_SB_IMPORT_SEL = wx.lib.newevent.NewEvent()
|
||||
ImportSelected, EVT_SB_IMPORT_SEL = wx.lib.newevent.NewEvent()
|
||||
|
||||
@@ -70,8 +70,8 @@ class NavigationPanel(SFItem.SFBrowserItem):
|
||||
(wx.BORDER_NONE if 'wxGTK' in wx.PlatformInfo else 0))
|
||||
self.BrowserSearchBox.Show(False)
|
||||
|
||||
#self.BrowserSearchBox.Bind(wx.EVT_TEXT_ENTER, self.OnBrowserSearchBoxEnter)
|
||||
#self.BrowserSearchBox.Bind(wx.EVT_KILL_FOCUS, self.OnBrowserSearchBoxLostFocus)
|
||||
# self.BrowserSearchBox.Bind(wx.EVT_TEXT_ENTER, self.OnBrowserSearchBoxEnter)
|
||||
# self.BrowserSearchBox.Bind(wx.EVT_KILL_FOCUS, self.OnBrowserSearchBoxLostFocus)
|
||||
self.BrowserSearchBox.Bind(wx.EVT_KEY_DOWN, self.OnBrowserSearchBoxEsc)
|
||||
self.BrowserSearchBox.Bind(wx.EVT_TEXT, self.OnScheduleSearch)
|
||||
|
||||
|
||||
@@ -20,4 +20,4 @@ class PFStaticText(wx.Panel):
|
||||
|
||||
@staticmethod
|
||||
def GetType():
|
||||
return -1
|
||||
return -1
|
||||
|
||||
@@ -255,7 +255,6 @@ class SFBrowserItem(wx.Window):
|
||||
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
|
||||
self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
|
||||
|
||||
|
||||
if "wxMSW" in wx.PlatformInfo:
|
||||
self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown)
|
||||
|
||||
|
||||
@@ -289,5 +289,3 @@ class ShipItem(SFItem.SFBrowserItem):
|
||||
else:
|
||||
editCtl.SetSize((self.editWidth, -1))
|
||||
editCtl.SetPosition((fnEditPosX, fnEditPosY))
|
||||
|
||||
|
||||
|
||||
@@ -122,6 +122,8 @@ class MiningYieldViewFull(StatsView):
|
||||
self.parent.views.append(view)
|
||||
# Get the TogglePanel
|
||||
tp = self.panel.GetParent()
|
||||
# Bind the new panel's children to allow context menu access
|
||||
self.parent.applyBinding(self.parent, tp.GetContentPane())
|
||||
tp.SetLabel(view.getHeaderText(fit))
|
||||
view.refreshPanel(fit)
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ from gui.statsView import StatsView
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.utils.numberFormatter import formatAmount
|
||||
from service.price import Price
|
||||
from service.settings import PriceMenuSettings
|
||||
|
||||
|
||||
class PriceViewFull(StatsView):
|
||||
@@ -31,6 +32,7 @@ class PriceViewFull(StatsView):
|
||||
def __init__(self, parent):
|
||||
StatsView.__init__(self)
|
||||
self.parent = parent
|
||||
self.settings = PriceMenuSettings.getInstance()
|
||||
|
||||
def getHeaderText(self, fit):
|
||||
return "Price"
|
||||
@@ -49,7 +51,7 @@ class PriceViewFull(StatsView):
|
||||
|
||||
gridPrice = wx.GridSizer(2, 3, 0, 0)
|
||||
contentSizer.Add(gridPrice, 0, wx.EXPAND | wx.ALL, 0)
|
||||
for _type in ("ship", "fittings", "drones", "cargoBay", "character", "total"):
|
||||
for _type in ("ship", "fittings", "total", "drones", "cargoBay", "character"):
|
||||
if _type in "ship":
|
||||
image = "ship_big"
|
||||
elif _type in ("fittings", "total"):
|
||||
@@ -125,8 +127,18 @@ class PriceViewFull(StatsView):
|
||||
for implant in fit.implants:
|
||||
implant_price += implant.item.price.price
|
||||
|
||||
fitting_price = module_price + drone_price + fighter_price + cargo_price + booster_price + implant_price
|
||||
total_price = ship_price + fitting_price
|
||||
total_price = 0
|
||||
|
||||
if (self.settings.get("ship")):
|
||||
total_price += ship_price
|
||||
if (self.settings.get("modules")):
|
||||
total_price += module_price
|
||||
if (self.settings.get("drones")):
|
||||
total_price += drone_price + fighter_price
|
||||
if (self.settings.get("cargo")):
|
||||
total_price += cargo_price
|
||||
if (self.settings.get("character")):
|
||||
total_price += booster_price + implant_price
|
||||
|
||||
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True))
|
||||
self.labelPriceShip.SetToolTip(wx.ToolTip('{:,.2f}'.format(ship_price)))
|
||||
@@ -134,18 +146,19 @@ class PriceViewFull(StatsView):
|
||||
self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(module_price, 3, 3, 9, currency=True))
|
||||
self.labelPriceFittings.SetToolTip(wx.ToolTip('{:,.2f}'.format(module_price)))
|
||||
|
||||
self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(total_price, 3, 3, 9, currency=True))
|
||||
self.labelPriceTotal.SetToolTip(wx.ToolTip('{:,.2f}'.format(total_price)))
|
||||
|
||||
self.labelPriceDrones.SetLabel("%s ISK" % formatAmount(drone_price + fighter_price, 3, 3, 9, currency=True))
|
||||
self.labelPriceDrones.SetToolTip(wx.ToolTip('{:,.2f}'.format(drone_price + fighter_price)))
|
||||
|
||||
self.labelPriceCargobay.SetLabel("%s ISK" % formatAmount(cargo_price, 3, 3, 9, currency=True))
|
||||
self.labelPriceCargobay.SetToolTip(wx.ToolTip('{:,.2f}'.format(cargo_price)))
|
||||
|
||||
self.labelPriceCharacter.SetLabel("%s ISK" % formatAmount(booster_price + implant_price, 3, 3, 9, currency=True))
|
||||
self.labelPriceCharacter.SetLabel(
|
||||
"%s ISK" % formatAmount(booster_price + implant_price, 3, 3, 9, currency=True))
|
||||
self.labelPriceCharacter.SetToolTip(wx.ToolTip('{:,.2f}'.format(booster_price + implant_price)))
|
||||
|
||||
self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(total_price, 3, 3, 9, currency=True))
|
||||
self.labelPriceTotal.SetToolTip(wx.ToolTip('{:,.2f}'.format(total_price)))
|
||||
|
||||
def processPrices(self, prices):
|
||||
self.refreshPanelPrices(self.fit)
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ from gui.statsView import StatsView
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
from gui.utils.numberFormatter import formatAmount
|
||||
from service.price import Price
|
||||
from service.settings import PriceMenuSettings
|
||||
|
||||
|
||||
class PriceViewMinimal(StatsView):
|
||||
@@ -31,6 +32,7 @@ class PriceViewMinimal(StatsView):
|
||||
def __init__(self, parent):
|
||||
StatsView.__init__(self)
|
||||
self.parent = parent
|
||||
self.settings = PriceMenuSettings.getInstance()
|
||||
|
||||
def getHeaderText(self, fit):
|
||||
return "Price"
|
||||
@@ -119,8 +121,20 @@ class PriceViewMinimal(StatsView):
|
||||
for implant in fit.implants:
|
||||
implant_price += implant.item.price.price
|
||||
|
||||
fitting_price = module_price + drone_price + fighter_price + cargo_price + booster_price + implant_price
|
||||
total_price = ship_price + fitting_price
|
||||
fitting_price = module_price
|
||||
|
||||
total_price = 0
|
||||
|
||||
if (self.settings.get("ship")):
|
||||
total_price += ship_price
|
||||
if (self.settings.get("modules")):
|
||||
total_price += module_price
|
||||
if (self.settings.get("drones")):
|
||||
total_price += drone_price + fighter_price
|
||||
if (self.settings.get("cargo")):
|
||||
total_price += cargo_price
|
||||
if (self.settings.get("character")):
|
||||
total_price += booster_price + implant_price
|
||||
|
||||
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(ship_price, 3, 3, 9, currency=True))
|
||||
self.labelPriceShip.SetToolTip(wx.ToolTip('{:,.2f}'.format(ship_price)))
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
__all__ = ["ammo", "ammoIcon", "attributeDisplay", "baseIcon", "baseName",
|
||||
"capacitorUse", "maxRange", "price", "propertyDisplay", "state", "misc", "abilities"]
|
||||
"capacitorUse", "maxRange", "price", "propertyDisplay", "state", "misc", "abilities", "sideEffects"]
|
||||
|
||||
@@ -49,6 +49,7 @@ class Miscellanea(ViewColumn):
|
||||
self.columnText = "Misc data"
|
||||
self.mask |= wx.LIST_MASK_TEXT
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.fittingView = fittingView
|
||||
|
||||
def getText(self, stuff):
|
||||
return self.__getData(stuff)[0]
|
||||
@@ -487,7 +488,7 @@ class Miscellanea(ViewColumn):
|
||||
if not hp or not cycleTime or not cycles:
|
||||
return "", None
|
||||
|
||||
fit = Fit.getInstance().getFit(self.mainFrame.getActiveFit())
|
||||
fit = Fit.getInstance().getFit(self.fittingView.getActiveFit())
|
||||
ehpTotal = fit.ehp
|
||||
hpTotal = fit.hp
|
||||
useEhp = self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective
|
||||
@@ -543,25 +544,7 @@ class Miscellanea(ViewColumn):
|
||||
return text, tooltip
|
||||
elif stuff.charge is not None:
|
||||
chargeGroup = stuff.charge.group.name
|
||||
if chargeGroup in (
|
||||
"Rocket",
|
||||
"Advanced Rocket",
|
||||
"Light Missile",
|
||||
"Advanced Light Missile",
|
||||
"FoF Light Missile",
|
||||
"Heavy Assault Missile",
|
||||
"Advanced Heavy Assault Missile",
|
||||
"Heavy Missile",
|
||||
"Advanced Heavy Missile",
|
||||
"FoF Heavy Missile",
|
||||
"Torpedo",
|
||||
"Advanced Torpedo",
|
||||
"Cruise Missile",
|
||||
"Advanced Cruise Missile",
|
||||
"FoF Cruise Missile",
|
||||
"XL Torpedo",
|
||||
"XL Cruise Missile"
|
||||
):
|
||||
if chargeGroup.endswith("Rocket") or chargeGroup.endswith("Missile") or chargeGroup.endswith("Torpedo"):
|
||||
cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize")
|
||||
aoeVelocity = stuff.getModifiedChargeAttr("aoeVelocity")
|
||||
if not cloudSize or not aoeVelocity:
|
||||
|
||||
46
gui/builtinViewColumns/sideEffects.py
Normal file
46
gui/builtinViewColumns/sideEffects.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# =============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of pyfa.
|
||||
#
|
||||
# pyfa is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# pyfa is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from eos.saveddata.booster import Booster
|
||||
from gui.viewColumn import ViewColumn
|
||||
import gui.mainFrame
|
||||
|
||||
|
||||
class SideEffects(ViewColumn):
|
||||
name = "Side Effects"
|
||||
|
||||
def __init__(self, fittingView, params):
|
||||
ViewColumn.__init__(self, fittingView)
|
||||
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
self.columnText = "Active Side Effects"
|
||||
self.mask = wx.LIST_MASK_TEXT
|
||||
|
||||
def getText(self, stuff):
|
||||
if isinstance(stuff, Booster):
|
||||
active = [x.name for x in stuff.sideEffects if x.active]
|
||||
if len(active) == 0:
|
||||
return "None"
|
||||
return ", ".join(active)
|
||||
|
||||
|
||||
SideEffects.register()
|
||||
@@ -22,6 +22,7 @@ import wx
|
||||
import wx.dataview
|
||||
import wx.lib.agw.hyperlink
|
||||
|
||||
from utils.floatspin import FloatSpin
|
||||
# noinspection PyPackageRequirements
|
||||
import wx.lib.newevent
|
||||
# noinspection PyPackageRequirements
|
||||
@@ -39,6 +40,9 @@ from logbook import Logger
|
||||
|
||||
from wx.lib.agw.floatspin import FloatSpin
|
||||
|
||||
|
||||
from gui.utils.clipboard import toClipboard, fromClipboard
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
|
||||
@@ -154,7 +158,7 @@ class CharacterEditor(wx.Frame):
|
||||
self.viewsNBContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0)
|
||||
|
||||
self.sview = SkillTreeView(self.viewsNBContainer)
|
||||
self.iview = ImplantEditorView(self.viewsNBContainer)
|
||||
self.iview = ImplantEditorView(self.viewsNBContainer, self)
|
||||
self.aview = APIView(self.viewsNBContainer)
|
||||
|
||||
self.viewsNBContainer.AddPage(self.sview, "Skills")
|
||||
@@ -203,6 +207,7 @@ class CharacterEditor(wx.Frame):
|
||||
self.btnSaveChar.Enable(not char.ro and char.isDirty)
|
||||
self.btnSaveAs.Enable(char.isDirty)
|
||||
self.btnRevert.Enable(char.isDirty)
|
||||
self.sview.importBtn.Enable(not char.ro)
|
||||
|
||||
def refreshCharacterList(self, event=None):
|
||||
"""This is only called when we save a modified character"""
|
||||
@@ -340,6 +345,26 @@ class SkillTreeView(wx.Panel):
|
||||
bSizerButtons = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
bSizerButtons.Add(self.btnSecStatus, 0, wx.ALL, 5)
|
||||
|
||||
bSizerButtons.AddSpacer((0, 0), 1, wx.EXPAND, 5)
|
||||
|
||||
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, "{}Btn".format(name.lower()), btn)
|
||||
btn.Enable(True)
|
||||
btn.SetToolTipString("%s skills %s clipboard" % (name, direction))
|
||||
bSizerButtons.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT | wx.ALL, 5)
|
||||
btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Skills".format(name.lower())))
|
||||
|
||||
pmainSizer.Add(bSizerButtons, 0, wx.EXPAND, 5)
|
||||
|
||||
# bind the Character selection event
|
||||
@@ -375,6 +400,48 @@ class SkillTreeView(wx.Panel):
|
||||
|
||||
self.Layout()
|
||||
|
||||
def importSkills(self, evt):
|
||||
|
||||
dlg = wx.MessageDialog(self, "Importing skills into this character will set the skill levels as pending. " +
|
||||
"To save the skills permanently, please click the Save button at the bottom of the window after importing"
|
||||
, "Import Skills", wx.OK)
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
|
||||
text = fromClipboard().strip()
|
||||
if text:
|
||||
char = self.charEditor.entityEditor.getActiveEntity()
|
||||
try:
|
||||
lines = text.splitlines()
|
||||
|
||||
for l in lines:
|
||||
skill, level = l.strip()[:-1].strip(), int(l.strip()[-1])
|
||||
skill = char.getSkill(skill)
|
||||
if skill:
|
||||
skill.setLevel(level, ignoreRestrict=True)
|
||||
|
||||
except Exception as e:
|
||||
dlg = wx.MessageDialog(self, "There was an error importing skills, please see log file", "Error", wx.ICON_ERROR)
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
pyfalog.error(e)
|
||||
|
||||
finally:
|
||||
self.charEditor.btnRestrict()
|
||||
self.populateSkillTree()
|
||||
self.charEditor.entityEditor.refreshEntityList(char)
|
||||
|
||||
def exportSkills(self, evt):
|
||||
char = self.charEditor.entityEditor.getActiveEntity()
|
||||
|
||||
skills = sorted(char.__class__.getSkillNameMap().keys())
|
||||
list = ""
|
||||
for s in skills:
|
||||
skill = char.getSkill(s)
|
||||
list += "{} {}\n".format(skill.item.name, skill.level)
|
||||
|
||||
toClipboard(list)
|
||||
|
||||
def onSecStatus(self, event):
|
||||
sChar = Character.getInstance()
|
||||
char = self.charEditor.entityEditor.getActiveEntity()
|
||||
@@ -561,10 +628,11 @@ class SkillTreeView(wx.Panel):
|
||||
|
||||
|
||||
class ImplantEditorView(BaseImplantEditorView):
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent, charEditor):
|
||||
BaseImplantEditorView.__init__(self, parent)
|
||||
|
||||
self.determineEnabled()
|
||||
charEditor.Bind(GE.CHAR_CHANGED, self.contextChanged)
|
||||
|
||||
if "__WXGTK__" in wx.PlatformInfo:
|
||||
self.pluggedImplantsTree.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu)
|
||||
@@ -605,7 +673,11 @@ class ImplantEditorView(BaseImplantEditorView):
|
||||
# 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)
|
||||
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
else:
|
||||
pyfalog.debug("ContextMenu.getMenu returned false do not attempt PopupMenu")
|
||||
|
||||
def determineEnabled(self):
|
||||
char = self.Parent.Parent.entityEditor.getActiveEntity()
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
# noinspection PyPackageRequirements
|
||||
import wx
|
||||
from gui.bitmap_loader import BitmapLoader
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
import gui.globalEvents as GE
|
||||
import gui.mainFrame
|
||||
from service.character import Character
|
||||
from service.fit import Fit
|
||||
from logbook import Logger
|
||||
|
||||
pyfalog = Logger(__name__)
|
||||
|
||||
@@ -94,6 +96,9 @@ class CharacterSelection(wx.Panel):
|
||||
grantItem = menu.Append(wx.ID_ANY, "Grant Missing Skills")
|
||||
self.Bind(wx.EVT_MENU, self.grantMissingSkills, grantItem)
|
||||
|
||||
exportItem = menu.Append(wx.ID_ANY, "Export Missing Skills")
|
||||
self.Bind(wx.EVT_MENU, self.exportSkills, exportItem)
|
||||
|
||||
self.PopupMenu(menu, pos)
|
||||
|
||||
event.Skip()
|
||||
@@ -102,16 +107,10 @@ class CharacterSelection(wx.Panel):
|
||||
charID = self.getActiveCharacter()
|
||||
sChar = Character.getInstance()
|
||||
|
||||
skillsMap = {}
|
||||
for item, stuff in self.reqs.items():
|
||||
for things in list(stuff.values()):
|
||||
if things[1] not in skillsMap:
|
||||
skillsMap[things[1]] = things[0]
|
||||
elif things[0] > skillsMap[things[1]]:
|
||||
skillsMap[things[1]] = things[0]
|
||||
skillsMap = self._buildSkillsTooltipCondensed(self.reqs, skillsMap={})
|
||||
|
||||
for skillID, level in skillsMap.items():
|
||||
sChar.changeLevel(charID, skillID, level, ifHigher=True)
|
||||
for index in skillsMap:
|
||||
sChar.changeLevel(charID, skillsMap[index][1], skillsMap[index][0], ifHigher=True)
|
||||
|
||||
self.refreshCharacterList()
|
||||
|
||||
@@ -229,7 +228,7 @@ class CharacterSelection(wx.Panel):
|
||||
if condensed:
|
||||
dict_ = self._buildSkillsTooltipCondensed(self.reqs, skillsMap={})
|
||||
for key in sorted(dict_):
|
||||
tip += "%s: %d\n" % (key, dict_[key])
|
||||
tip += "%s: %d\n" % (key, dict_[key][0])
|
||||
else:
|
||||
tip += self._buildSkillsTooltip(self.reqs)
|
||||
self.skillReqsStaticBitmap.SetBitmap(self.redSkills)
|
||||
@@ -246,6 +245,15 @@ class CharacterSelection(wx.Panel):
|
||||
|
||||
event.Skip()
|
||||
|
||||
def exportSkills(self, evt):
|
||||
skillsMap = self._buildSkillsTooltipCondensed(self.reqs, skillsMap={})
|
||||
|
||||
list = ""
|
||||
for key in sorted(skillsMap):
|
||||
list += "%s %d\n" % (key, skillsMap[key][0])
|
||||
|
||||
toClipboard(list)
|
||||
|
||||
def _buildSkillsTooltip(self, reqs, currItem="", tabulationLevel=0):
|
||||
tip = ""
|
||||
sCharacter = Character.getInstance()
|
||||
@@ -292,9 +300,9 @@ class CharacterSelection(wx.Panel):
|
||||
})
|
||||
|
||||
if name not in skillsMap:
|
||||
skillsMap[name] = level
|
||||
elif skillsMap[name] < level:
|
||||
skillsMap[name] = level
|
||||
skillsMap[name] = level, ID
|
||||
elif skillsMap[name][0] < level:
|
||||
skillsMap[name] = level, ID
|
||||
|
||||
skillsMap = self._buildSkillsTooltipCondensed(more, currItem, tabulationLevel + 1, skillsMap)
|
||||
|
||||
|
||||
0
gui/chromeTabs.py
Normal file
0
gui/chromeTabs.py
Normal file
@@ -199,12 +199,14 @@ from gui.builtinContextMenus import ( # noqa: E402,F401
|
||||
tacticalMode,
|
||||
targetResists,
|
||||
priceClear,
|
||||
priceOptions,
|
||||
amount,
|
||||
cargoAmmo,
|
||||
droneStack,
|
||||
metaSwap,
|
||||
implantSets,
|
||||
fighterAbilities,
|
||||
boosterSideEffects,
|
||||
commandFits,
|
||||
tabbedFits
|
||||
)
|
||||
|
||||
@@ -256,6 +256,8 @@ class ExportToEve(wx.Frame):
|
||||
|
||||
def OnClose(self, event):
|
||||
self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT)
|
||||
self.mainFrame.Unbind(GE.EVT_SSO_LOGIN)
|
||||
|
||||
event.Skip()
|
||||
|
||||
def getActiveCharacter(self):
|
||||
|
||||
@@ -78,7 +78,7 @@ class ErrorFrame(wx.Frame):
|
||||
wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE)
|
||||
box.Add(github, 0, wx.ALL, 5)
|
||||
|
||||
eveForums = wx.HyperlinkCtrl(self, wx.ID_ANY, "EVE Forums", "https://forums.eveonline.com/default.aspx?g=posts&t=466425",
|
||||
eveForums = wx.HyperlinkCtrl(self, wx.ID_ANY, "EVE Forums", "https://forums.eveonline.com/t/27156",
|
||||
wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE)
|
||||
box.Add(eveForums, 0, wx.ALL, 5)
|
||||
|
||||
|
||||
1279
gui/itemStats.py
1279
gui/itemStats.py
File diff suppressed because it is too large
Load Diff
@@ -456,7 +456,7 @@ class MainFrame(wx.Frame):
|
||||
|
||||
@staticmethod
|
||||
def goForums(event):
|
||||
webbrowser.open('https://forums.eveonline.com/default.aspx?g=posts&t=466425')
|
||||
webbrowser.open('https://forums.eveonline.com/t/27156')
|
||||
|
||||
@staticmethod
|
||||
def loadDatabaseDefaults(event):
|
||||
@@ -1016,7 +1016,6 @@ class MainFrame(wx.Frame):
|
||||
del self.waitDialog
|
||||
|
||||
def openGraphFrame(self, event):
|
||||
|
||||
if not self.graphFrame:
|
||||
self.graphFrame = GraphFrame(self)
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ from gui.builtinMarketBrowser.searchBox import SearchBox
|
||||
from gui.builtinMarketBrowser.itemView import ItemView
|
||||
from gui.builtinMarketBrowser.metaButton import MetaButton
|
||||
from gui.builtinMarketBrowser.marketTree import MarketTree
|
||||
from gui.builtinMarketBrowser.events import *
|
||||
|
||||
from logbook import Logger
|
||||
|
||||
@@ -99,4 +98,3 @@ class MarketBrowser(wx.Panel):
|
||||
|
||||
def jump(self, item):
|
||||
self.marketView.jump(item)
|
||||
|
||||
|
||||
@@ -197,7 +197,6 @@ class ImplantSetEditorDlg(wx.Dialog):
|
||||
try:
|
||||
sIS.importSets(text)
|
||||
self.stNotice.SetLabel("Patterns successfully imported from clipboard")
|
||||
self.showInput(False)
|
||||
except ImportError as e:
|
||||
pyfalog.error(e)
|
||||
self.stNotice.SetLabel(str(e))
|
||||
@@ -205,7 +204,7 @@ class ImplantSetEditorDlg(wx.Dialog):
|
||||
pyfalog.error(e)
|
||||
self.stNotice.SetLabel("Could not import from clipboard: unknown errors")
|
||||
finally:
|
||||
self.updateChoices()
|
||||
self.entityEditor.refreshEntityList()
|
||||
else:
|
||||
self.stNotice.SetLabel("Could not import from clipboard")
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from gui.builtinShipBrowser.shipItem import ShipItem
|
||||
from service.fit import Fit
|
||||
from service.market import Market
|
||||
|
||||
from gui.builtinShipBrowser.events import *
|
||||
import gui.builtinShipBrowser.events as events
|
||||
from gui.builtinShipBrowser.pfWidgetContainer import PFWidgetsContainer
|
||||
from gui.builtinShipBrowser.navigationPanel import NavigationPanel
|
||||
from gui.builtinShipBrowser.raceSelector import RaceSelector
|
||||
@@ -76,11 +76,11 @@ class ShipBrowser(wx.Panel):
|
||||
self.Show()
|
||||
|
||||
self.Bind(wx.EVT_SIZE, self.SizeRefreshList)
|
||||
self.Bind(EVT_SB_STAGE2_SEL, self.stage2)
|
||||
self.Bind(EVT_SB_STAGE1_SEL, self.stage1)
|
||||
self.Bind(EVT_SB_STAGE3_SEL, self.stage3)
|
||||
self.Bind(EVT_SB_SEARCH_SEL, self.searchStage)
|
||||
self.Bind(EVT_SB_IMPORT_SEL, self.importStage)
|
||||
self.Bind(events.EVT_SB_STAGE2_SEL, self.stage2)
|
||||
self.Bind(events.EVT_SB_STAGE1_SEL, self.stage1)
|
||||
self.Bind(events.EVT_SB_STAGE3_SEL, self.stage3)
|
||||
self.Bind(events.EVT_SB_SEARCH_SEL, self.searchStage)
|
||||
self.Bind(events.EVT_SB_IMPORT_SEL, self.importStage)
|
||||
|
||||
self.mainFrame.Bind(GE.FIT_CHANGED, self.RefreshList)
|
||||
|
||||
@@ -378,6 +378,10 @@ class ShipBrowser(wx.Panel):
|
||||
|
||||
for ID, name, shipID, shipName, booster, timestamp, notes in fitList:
|
||||
ship = sMkt.getItem(shipID)
|
||||
|
||||
if not sMkt.getPublicityByItem(ship):
|
||||
continue
|
||||
|
||||
shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits
|
||||
|
||||
self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID))
|
||||
@@ -440,7 +444,3 @@ class ShipBrowser(wx.Panel):
|
||||
if self.showRacesFilterInStage2Only:
|
||||
self.raceselect.Show(False)
|
||||
self.Layout()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -143,3 +143,9 @@ class StatsPane(wx.Panel):
|
||||
event.Skip()
|
||||
|
||||
return handler
|
||||
|
||||
@staticmethod
|
||||
def applyBinding(self, contentPanel):
|
||||
pyfalog.debug("Attempt applyBinding to children of {0}", contentPanel.viewName)
|
||||
for child in contentPanel.GetChildren():
|
||||
child.Bind(wx.EVT_RIGHT_DOWN, self.contextHandler(contentPanel))
|
||||
|
||||
@@ -79,5 +79,6 @@ from gui.builtinViewColumns import ( # noqa: E402, F401
|
||||
misc,
|
||||
price,
|
||||
propertyDisplay,
|
||||
state
|
||||
state,
|
||||
sideEffects
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user