Files
pyfa/gui/shipBrowser.py
2019-12-23 17:02:34 +03:00

450 lines
14 KiB
Python

# noinspection PyPackageRequirements
import wx
from logbook import Logger
import gui.globalEvents as GE
import gui.mainFrame
from gui.builtinShipBrowser.categoryItem import CategoryItem
from gui.builtinShipBrowser.fitItem import FitItem
from gui.builtinShipBrowser.shipItem import ShipItem
from service.fit import Fit
from service.market import Market
from gui.builtinShipBrowser.events import EVT_SB_IMPORT_SEL, EVT_SB_STAGE1_SEL, EVT_SB_STAGE2_SEL, EVT_SB_STAGE3_SEL, EVT_SB_SEARCH_SEL
from gui.builtinShipBrowser.pfWidgetContainer import PFWidgetsContainer
from gui.builtinShipBrowser.navigationPanel import NavigationPanel
from gui.builtinShipBrowser.raceSelector import RaceSelector
from gui.builtinShipBrowser.pfStaticText import PFStaticText
pyfalog = Logger(__name__)
class ShipBrowser(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, style=0)
self._lastWidth = 0
self._activeStage = 1
self._lastStage = 0
self.browseHist = []
self.lastStage = (0, 0)
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.categoryList = []
self.categoryFitCache = {}
self._stage1Data = -1
self._stage2Data = -1
self._stage3Data = -1
self._stage3ShipName = ""
self.fitIDMustEditName = -1
self.filterShipsWithNoFits = False
self.recentFits = False
self.racesFilter = {}
self.showRacesFilterInStage2Only = True
for race in self.RACE_ORDER:
if race:
self.racesFilter[race] = False
self.SetSizeHints(wx.DefaultSize, wx.DefaultSize)
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.navpanel = NavigationPanel(self)
mainSizer.Add(self.navpanel, 0, wx.EXPAND)
self.lpane = PFWidgetsContainer(self)
layout = wx.HORIZONTAL
self.raceselect = RaceSelector(self, layout=layout, animate=False)
container = wx.BoxSizer(wx.VERTICAL if layout == wx.HORIZONTAL else wx.HORIZONTAL)
if layout == wx.HORIZONTAL:
container.Add(self.lpane, 1, wx.EXPAND)
container.Add(self.raceselect, 0, wx.EXPAND)
else:
container.Add(self.raceselect, 0, wx.EXPAND)
container.Add(self.lpane, 1, wx.EXPAND)
mainSizer.Add(container, 1, wx.EXPAND)
self.SetSizer(mainSizer)
self.Layout()
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.mainFrame.Bind(GE.FIT_CHANGED, self.RefreshList)
self.stage1(None)
def GetBrowserContainer(self):
return self.lpane
def RefreshContent(self):
stage = self.GetActiveStage()
if stage == 1:
return
stageData = self.GetStageData(stage)
self.navpanel.gotoStage(stage, stageData)
def RefreshList(self, event):
event.Skip()
activeFitID = self.mainFrame.getActiveFit()
if activeFitID is not None and activeFitID not in event.fitIDs:
return
stage = self.GetActiveStage()
if stage in (3, 4, 5):
self.lpane.RefreshList(True)
def SizeRefreshList(self, event):
self.Layout()
self.lpane.Layout()
self.lpane.RefreshList(True)
event.Skip()
def __del__(self):
pass
def GetActiveStage(self):
return self._activeStage
def GetLastStage(self):
return self._lastStage
def GetStageData(self, stage):
if stage == 1:
return self._stage1Data
if stage == 2:
return self._stage2Data
if stage == 3:
return self._stage3Data
if stage == 4:
return self.navpanel.lastSearch
return -1
def GetStage3ShipName(self):
return self._stage3ShipName
def ToggleRacesFilter(self, race):
if self.racesFilter[race]:
self.racesFilter[race] = False
else:
self.racesFilter[race] = True
def GetRaceFilterState(self, race):
return self.racesFilter[race]
def stage1(self, event):
self.navpanel.ToggleRecentShips(False, False)
self._lastStage = self._activeStage
self._activeStage = 1
self.lastdata = 0
self.browseHist = [(1, 0)]
self.navpanel.ShowNewFitButton(False)
self.navpanel.ShowSwitchEmptyGroupsButton(False)
sMkt = Market.getInstance()
sFit = Fit.getInstance()
self.lpane.ShowLoading(False)
self.lpane.Freeze()
self.lpane.RemoveAllChildren()
pyfalog.debug("Populate ship category list.")
if len(self.categoryList) == 0:
# set cache of category list
self.categoryList = list(sMkt.getShipRoot())
self.categoryList.sort(key=lambda _ship: _ship.name)
counts = sFit.countAllFitsGroupedByShip()
# set map & cache of fittings per category
for cat in self.categoryList:
itemIDs = [x.ID for x in sMkt.getItemsByGroup(cat)]
self.categoryFitCache[cat.ID] = sum([count for shipID, count in counts if shipID in itemIDs]) > 0
for ship in self.categoryList:
if self.filterShipsWithNoFits and not self.categoryFitCache[ship.ID]:
continue
else:
self.lpane.AddWidget(CategoryItem(self.lpane, ship.ID, (ship.name, 0)))
self.navpanel.ShowSwitchEmptyGroupsButton(True)
self.lpane.RefreshList()
self.lpane.Thaw()
self.raceselect.RebuildRaces(self.RACE_ORDER)
if self.showRacesFilterInStage2Only:
self.raceselect.Show(False)
self.Layout()
RACE_ORDER = [
"amarr", "caldari", "gallente", "minmatar",
"sisters", "ore", "concord",
"serpentis", "angel", "blood", "sansha", "guristas", "mordu",
"jove", "upwell", "triglavian", None
]
def raceNameKey(self, ship):
return self.RACE_ORDER.index(ship.race), ship.name
def stage2Callback(self, data):
if self.GetActiveStage() != 2:
return
self.navpanel.ToggleRecentShips(False, False)
categoryID = self._stage2Data
ships = list(data[1])
sFit = Fit.getInstance()
ships.sort(key=self.raceNameKey)
racesList = []
subRacesFilter = {}
t_fits = 0 # total number of fits in this category
for ship in ships:
if ship.race:
if ship.race not in racesList:
racesList.append(ship.race)
for race, state in self.racesFilter.items():
if race in racesList:
subRacesFilter[race] = self.racesFilter[race]
override = True
for race, state in subRacesFilter.items():
if state:
override = False
break
for ship in ships:
fits = sFit.countFitsWithShip(ship.ID)
t_fits += fits
filter_ = subRacesFilter[ship.race] if ship.race else True
if override:
filter_ = True
shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits
if self.filterShipsWithNoFits:
if fits > 0:
if filter_:
self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race, ship.graphicID))
else:
if filter_:
self.lpane.AddWidget(ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, fits), ship.race, ship.graphicID))
self.raceselect.RebuildRaces(racesList)
# refresh category cache
if t_fits == 0:
self.categoryFitCache[categoryID] = False
else:
self.categoryFitCache[categoryID] = True
self.lpane.ShowLoading(False)
self.lpane.RefreshList()
if self.showRacesFilterInStage2Only:
self.raceselect.Show(True)
self.Layout()
def stage2(self, event):
# back = event.back
# if not back:
# self.browseHist.append( (1,0) )
self._lastStage = self._activeStage
self._activeStage = 2
categoryID = event.categoryID
self.lastdata = categoryID
self.lpane.ShowLoading()
self.lpane.RemoveAllChildren()
sMkt = Market.getInstance()
sMkt.getShipListDelayed(categoryID, self.stage2Callback)
self._stage2Data = categoryID
self.navpanel.ShowNewFitButton(False)
self.navpanel.ShowSwitchEmptyGroupsButton(True)
@staticmethod
def nameKey(info):
return info[1]
def stage3(self, event):
self.navpanel.ToggleRecentShips(False, False)
self.lpane.ShowLoading(False)
# If back is False, do not append to history. This could be us calling
# the stage from previous history, creating / copying fit, etc.
# We also have to use conditional for search stage since it's last data
# is kept elsewhere
if getattr(event, "back", False):
if self._activeStage == 4 and self.navpanel.lastSearch != "":
self.browseHist.append((4, self.navpanel.lastSearch))
else:
self.browseHist.append((self._activeStage, self.lastdata))
shipID = event.shipID
self.lastdata = shipID
self._lastStage = self._activeStage
self._activeStage = 3
sFit = Fit.getInstance()
sMkt = Market.getInstance()
ship = sMkt.getItem(shipID)
categoryID = ship.group.ID
self.lpane.Freeze()
self.lpane.RemoveAllChildren()
fitList = sFit.getFitsWithShip(shipID)
if len(fitList) == 0:
stage, data = self.browseHist.pop()
self.lpane.Thaw()
self.navpanel.gotoStage(stage, data)
return
self.categoryFitCache[categoryID] = True
self.navpanel.ShowNewFitButton(True)
self.navpanel.ShowSwitchEmptyGroupsButton(False)
if self.showRacesFilterInStage2Only:
self.raceselect.Show(False)
self.Layout()
fitList.sort(key=self.nameKey)
shipName = ship.name
self._stage3ShipName = shipName
self._stage3Data = shipID
shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits
for ID, name, booster, timestamp, notes, graphicID in fitList:
self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID, graphicID=graphicID))
self.lpane.RefreshList()
self.lpane.Thaw()
self.raceselect.RebuildRaces(self.RACE_ORDER)
def searchStage(self, event):
self.lpane.ShowLoading(False)
self.navpanel.ShowNewFitButton(False)
self.navpanel.ShowSwitchEmptyGroupsButton(False)
if not event.back:
if self._activeStage != 4:
if len(self.browseHist) > 0:
self.browseHist.append((self._activeStage, self.lastdata))
else:
self.browseHist.append((1, 0))
self._lastStage = self._activeStage
self._activeStage = 4
sMkt = Market.getInstance()
sFit = Fit.getInstance()
query = event.text
self.lpane.Freeze()
self.lpane.RemoveAllChildren()
if query:
ships = sMkt.searchShips(query)
fitList = sFit.searchFits(query)
for ship in ships:
shipTrait = ship.traits.traitText if (ship.traits is not None) else "" # empty string if no traits
self.lpane.AddWidget(
ShipItem(self.lpane, ship.ID, (ship.name, shipTrait, len(sFit.getFitsWithShip(ship.ID))),
ship.race, ship.graphicID))
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, graphicID=ship.graphicID))
if len(ships) == 0 and len(fitList) == 0:
self.lpane.AddWidget(PFStaticText(self.lpane, label="No matching results."))
self.lpane.RefreshList(doFocus=False)
self.lpane.Thaw()
self.raceselect.RebuildRaces(self.RACE_ORDER)
if self.showRacesFilterInStage2Only:
self.raceselect.Show(False)
self.Layout()
def importStage(self, event):
"""
The import stage handles both displaying fits after importing as well as displaying recent fits. todo: need to
reconcile these two better into a more uniform function, right now hacked together to get working
"""
self.lpane.ShowLoading(False)
self.navpanel.ShowNewFitButton(False)
self.navpanel.ShowSwitchEmptyGroupsButton(False)
if getattr(event, "back", False):
self.browseHist.append((self._activeStage, self.lastdata))
self._lastStage = self._activeStage
self._activeStage = 5
fits = event.fits
self.lastdata = fits
self.lpane.Freeze()
self.lpane.RemoveAllChildren()
if fits:
for fit in fits:
shipItem = fit[3]
shipTrait = shipItem.traits.traitText if (shipItem.traits is not None) else ""
self.lpane.AddWidget(FitItem(
self.lpane,
fit[0],
(
shipItem.name,
shipTrait,
fit[1],
False,
fit[2],
fit[4]
),
shipItem.ID,
graphicID=shipItem.graphicID
))
self.lpane.RefreshList(doFocus=False)
self.lpane.Thaw()
self.raceselect.RebuildRaces(self.RACE_ORDER)
if self.showRacesFilterInStage2Only:
self.raceselect.Show(False)
self.Layout()