From 68f45706ab7dc9c2e68a3f9d868b37b1ce56decc Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Sep 2016 15:07:59 -0700 Subject: [PATCH 01/26] Purging fleet bonuses from code base --- eos/db/saveddata/fleet.py | 48 +--- eos/db/saveddata/queries.py | 58 ----- eos/types.py | 1 - gui/additionsPane.py | 7 +- gui/builtinViews/fleetView.py | 141 +---------- gui/fleetBrowser.py | 457 +--------------------------------- gui/gangView.py | 394 +---------------------------- gui/mainFrame.py | 7 - service/__init__.py | 1 - service/fit.py | 13 +- service/fleet.py | 197 +-------------- 11 files changed, 7 insertions(+), 1317 deletions(-) diff --git a/eos/db/saveddata/fleet.py b/eos/db/saveddata/fleet.py index 8f0e6736b..0583b43b0 100644 --- a/eos/db/saveddata/fleet.py +++ b/eos/db/saveddata/fleet.py @@ -17,50 +17,4 @@ # along with eos. If not, see . #=============================================================================== -from sqlalchemy import Table, Column, Integer, ForeignKey, String -from sqlalchemy.orm import mapper, relation - -from eos.db import saveddata_meta -from eos.types import Fleet, Wing, Squad, Fit -from eos.db.saveddata.fit import fits_table - -gangs_table = Table("gangs", saveddata_meta, - Column("ID", Integer, primary_key = True), - Column("leaderID", ForeignKey("fits.ID")), - Column("boosterID", ForeignKey("fits.ID")), - Column("name", String)) - -wings_table = Table("wings", saveddata_meta, - Column("ID", Integer, primary_key = True), - Column("gangID", ForeignKey("gangs.ID")), - Column("boosterID", ForeignKey("fits.ID")), - Column("leaderID", ForeignKey("fits.ID"))) - -squads_table = Table("squads", saveddata_meta, - Column("ID", Integer, primary_key = True), - Column("wingID", ForeignKey("wings.ID")), - Column("leaderID", ForeignKey("fits.ID")), - Column("boosterID", ForeignKey("fits.ID"))) - -squadmembers_table = Table("squadmembers", saveddata_meta, - Column("squadID", ForeignKey("squads.ID"), primary_key = True), - Column("memberID", ForeignKey("fits.ID"), primary_key = True)) - -mapper(Fleet, gangs_table, - properties = {"wings" : relation(Wing, backref="gang"), - "leader" : relation(Fit, primaryjoin = gangs_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin = gangs_table.c.boosterID == fits_table.c.ID)}) - -mapper(Wing, wings_table, - properties = {"squads" : relation(Squad, backref="wing"), - "leader" : relation(Fit, primaryjoin = wings_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin = wings_table.c.boosterID == fits_table.c.ID)}) - -mapper(Squad, squads_table, - properties = {"leader" : relation(Fit, primaryjoin = squads_table.c.leaderID == fits_table.c.ID), - "booster" : relation(Fit, primaryjoin = squads_table.c.boosterID == fits_table.c.ID), - "members" : relation(Fit, - primaryjoin = squads_table.c.ID == squadmembers_table.c.squadID, - secondaryjoin = squadmembers_table.c.memberID == fits_table.c.ID, - secondary = squadmembers_table)}) - +#Purging fleet \ No newline at end of file diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 5a6dd1bfc..cb3033cc1 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -21,7 +21,6 @@ from eos.db.util import processEager, processWhere from eos.db import saveddata_session, sd_lock from eos.types import * -from eos.db.saveddata.fleet import squadmembers_table from eos.db.saveddata.fit import projectedFits_table from sqlalchemy.sql import and_ import eos.config @@ -194,48 +193,6 @@ def getFit(lookfor, eager=None): return fit -@cachedQuery(Fleet, 1, "fleetID") -def getFleet(fleetID, eager=None): - if isinstance(fleetID, int): - if eager is None: - with sd_lock: - fleet = saveddata_session.query(Fleet).get(fleetID) - else: - eager = processEager(eager) - with sd_lock: - fleet = saveddata_session.query(Fleet).options(*eager).filter(Fleet.ID == fleetID).first() - else: - raise TypeError("Need integer as argument") - return fleet - -@cachedQuery(Wing, 1, "wingID") -def getWing(wingID, eager=None): - if isinstance(wingID, int): - if eager is None: - with sd_lock: - wing = saveddata_session.query(Wing).get(wingID) - else: - eager = processEager(eager) - with sd_lock: - wing = saveddata_session.query(Wing).options(*eager).filter(Wing.ID == wingID).first() - else: - raise TypeError("Need integer as argument") - return wing - -@cachedQuery(Squad, 1, "squadID") -def getSquad(squadID, eager=None): - if isinstance(squadID, int): - if eager is None: - with sd_lock: - squad = saveddata_session.query(Squad).get(squadID) - else: - eager = processEager(eager) - with sd_lock: - squad = saveddata_session.query(Squad).options(*eager).filter(Fleet.ID == squadID).first() - else: - raise TypeError("Need integer as argument") - return squad - def getFitsWithShip(shipID, ownerID=None, where=None, eager=None): """ Get all the fits using a certain ship. @@ -308,12 +265,6 @@ def getFitList(eager=None): return fits -def getFleetList(eager=None): - eager = processEager(eager) - with sd_lock: - fleets = saveddata_session.query(Fleet).options(*eager).all() - return fleets - @cachedQuery(Price, 1, "typeID") def getPrice(typeID): if isinstance(typeID, int): @@ -423,15 +374,6 @@ def searchFits(nameLike, where=None, eager=None): return fits -def getSquadsIDsWithFitID(fitID): - if isinstance(fitID, int): - with sd_lock: - squads = saveddata_session.query(squadmembers_table.c.squadID).filter(squadmembers_table.c.memberID == fitID).all() - squads = tuple(entry[0] for entry in squads) - return squads - else: - raise TypeError("Need integer as argument") - def getProjectedFits(fitID): if isinstance(fitID, int): with sd_lock: diff --git a/eos/types.py b/eos/types.py index 12e7eb281..f2550ad31 100644 --- a/eos/types.py +++ b/eos/types.py @@ -38,7 +38,6 @@ from eos.saveddata.booster import SideEffect from eos.saveddata.booster import Booster from eos.saveddata.fit import Fit, ImplantLocation from eos.saveddata.mode import Mode -from eos.saveddata.fleet import Fleet, Wing, Squad from eos.saveddata.miscData import MiscData from eos.saveddata.override import Override diff --git a/gui/additionsPane.py b/gui/additionsPane.py index 8c855dc95..f1ca44be4 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -26,7 +26,6 @@ from gui.cargoView import CargoView from gui.implantView import ImplantView from gui.projectedView import ProjectedView from gui.pyfatogglepanel import TogglePanel -from gui.gangView import GangView from gui.bitmapLoader import BitmapLoader import gui.chromeTabs @@ -55,7 +54,6 @@ class AdditionsPane(TogglePanel): implantImg = BitmapLoader.getImage("implant_small", "gui") boosterImg = BitmapLoader.getImage("booster_small", "gui") projectedImg = BitmapLoader.getImage("projected_small", "gui") - gangImg = BitmapLoader.getImage("fleet_fc_small", "gui") cargoImg = BitmapLoader.getImage("cargo_small", "gui") self.drone = DroneView(self.notebook) @@ -76,12 +74,9 @@ class AdditionsPane(TogglePanel): self.projectedPage = ProjectedView(self.notebook) self.notebook.AddPage(self.projectedPage, "Projected", tabImage = projectedImg, showClose = False) - self.gangPage = GangView(self.notebook) - self.notebook.AddPage(self.gangPage, "Fleet", tabImage = gangImg, showClose = False) - self.notebook.SetSelection(0) - PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected", "Fleet"] + PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected"] def select(self, name): self.notebook.SetSelection(self.PANES.index(name)) diff --git a/gui/builtinViews/fleetView.py b/gui/builtinViews/fleetView.py index bc6eef9a2..9058bc35f 100644 --- a/gui/builtinViews/fleetView.py +++ b/gui/builtinViews/fleetView.py @@ -1,140 +1 @@ -import wx.gizmos -import gui.fleetBrowser -import service -from gui.bitmapLoader import BitmapLoader - -#Tab spawning handler -class FleetSpawner(gui.multiSwitch.TabSpawner): - def __init__(self, multiSwitch): - self.multiSwitch = multiSwitch - mainFrame = gui.mainFrame.MainFrame.getInstance() - mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_SELECTED, self.fleetSelected) - - def fleetSelected(self, event): - if self.multiSwitch.GetPageCount() == 0: - self.multiSwitch.AddPage(wx.Panel(self.multiSwitch, size = (0,0)), "Empty Tab") - - view = FleetView(self.multiSwitch) - self.multiSwitch.ReplaceActivePage(view) - view.populate(event.fleetID) - view.Show() - -FleetSpawner.register() - -class FleetView(wx.gizmos.TreeListCtrl): - def __init__(self, parent, size = (0,0)): - wx.gizmos.TreeListCtrl.__init__(self, parent, size = size) - - self.tabManager = parent - - self.fleetId = None - self.fleetImg = BitmapLoader.getImage("53_16", "icons") - - self.imageList = wx.ImageList(16, 16) - self.SetImageList(self.imageList) - - for col in ("", "Fit", "Shiptype", "Character", "Bonusses"): - self.AddColumn(col) - - self.SetMainColumn(1) - self.icons = {} - self.addImage = self.imageList.Add(BitmapLoader.getBitmap("add_small", "gui")) - for icon in ("fb", "fc", "sb", "sc", "wb", "wc"): - self.icons[icon] = self.imageList.Add(BitmapLoader.getBitmap("fleet_%s_small" % icon, "gui")) - - self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.checkNew) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_RENAMED, self.fleetRenamed) - self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_REMOVED, self.fleetRemoved) - - def Destroy(self): - self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_REMOVED, handler = self.fleetRemoved) - self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_RENAMED, handler = self.fleetRenamed) - wx.gizmos.TreeListCtrl.Destroy(self) - - def fleetRenamed(self, event): - if event.fleetID == self.fleetId: - sFleet = service.Fleet.getInstance() - f = sFleet.getFleetByID(event.fleetID) - self.UpdateTab(f.name, self.fleetImg) - - event.Skip() - - def fleetRemoved(self, event): - if event.fleetID == self.fleetId: - self.tabManager.DeletePage(self.tabManager.GetPageIndex(self)) - - event.Skip() - - def checkNew(self, event): - data = self.GetPyData(event.Item) - if data and isinstance(data, tuple) and data[0] == "add": - layer = data[1] - - - def UpdateTab(self, name, img): - self.tabManager.SetPageTextIcon(self.tabManager.GetSelection(), name, img) - - def populate(self, fleetID): - sFleet = service.Fleet.getInstance() - f = sFleet.getFleetByID(fleetID) - self.fleetId = fleetID - - self.UpdateTab( f.name, self.fleetImg) - self.fleet = f - self.DeleteAllItems() - root = self.AddRoot("") - - self.setEntry(root, f.leader, "fleet", f) - for wing in f.wings: - wingId = self.AppendItem(root, "") - self.setEntry(wingId, wing.leader, "wing", wing) - for squad in wing.squads: - for member in squad.members: - memberId = self.AppendItem(wingId, "") - self.setEntry(memberId, member, "squad", squad) - - self.addAdder(wingId, "squad") - - self.addAdder(root, "wing") - - self.ExpandAll(root) - self.SetColumnWidth(0, 16) - for i in xrange(1, 5): - self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) - headerWidth = self.GetColumnWidth(i) + 5 - self.SetColumnWidth(i, wx.LIST_AUTOSIZE) - baseWidth = self.GetColumnWidth(i) - if baseWidth < headerWidth: - self.SetColumnWidth(i, headerWidth) - else: - self.SetColumnWidth(i, baseWidth) - - - def addAdder(self, treeItemId, layer): - id = self.AppendItem(treeItemId, "Add new %s" % layer.capitalize()) - self.SetPyData(id, ("add", layer)) - self.SetItemImage(id, self.addImage, 1) - - def setEntry(self, treeItemId, fit, layer, info): - self.SetPyData(treeItemId, info) - if fit is None: - self.SetItemText(treeItemId, "%s Commander" % layer.capitalize(), 1) - else: - fleet = self.fleet - if fit == info.booster: - self.SetItemImage(treeItemId, self.icons["%sb" % layer[0]], 0) - elif fit == info.leader: - self.SetItemImage(treeItemId, self.icons["%sc" % layer[0]], 1) - - self.SetItemText(treeItemId, fit.name, 1) - self.SetItemText(treeItemId, fit.ship.item.name, 2) - self.SetItemText(treeItemId, fit.character.name, 3) - boosts = fleet.store.getBoosts(fit) - if boosts: - bonusses = [] - for name, info in boosts.iteritems(): - bonusses.append("%s: %.2g" % (name, info[0])) - - self.SetItemText(treeItemId, ", ".join(bonusses), 3) +#Purge fleet boosts \ No newline at end of file diff --git a/gui/fleetBrowser.py b/gui/fleetBrowser.py index 7476d7d7f..bb28be251 100644 --- a/gui/fleetBrowser.py +++ b/gui/fleetBrowser.py @@ -1,456 +1 @@ -import wx -import copy -from gui.bitmapLoader import BitmapLoader -import gui.mainFrame -from gui.PFListPane import PFListPane -import service.fleet -from gui.utils.drawUtils import GetPartialText - -from wx.lib.buttons import GenBitmapButton - -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils - -import gui.sfBrowserItem as SFItem - -FleetSelected, EVT_FLEET_SELECTED = wx.lib.newevent.NewEvent() -FleetRenamed, EVT_FLEET_RENAMED = wx.lib.newevent.NewEvent() -FleetRemoved, EVT_FLEET_REMOVED = wx.lib.newevent.NewEvent() - - -FleetItemSelect, EVT_FLEET_ITEM_SELECT = wx.lib.newevent.NewEvent() -FleetItemDelete, EVT_FLEET_ITEM_DELETE = wx.lib.newevent.NewEvent() -FleetItemNew, EVT_FLEET_ITEM_NEW = wx.lib.newevent.NewEvent() -FleetItemCopy, EVT_FLEET_ITEM_COPY = wx.lib.newevent.NewEvent() -FleetItemRename, EVT_FLEET_ITEM_RENAME = wx.lib.newevent.NewEvent() - - - -class FleetBrowser(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - - self.sFleet = service.fleet.Fleet.getInstance() - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - mainSizer = wx.BoxSizer(wx.VERTICAL) - - self.hpane = FleetBrowserHeader(self) - mainSizer.Add(self.hpane, 0, wx.EXPAND) - - self.m_sl2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_sl2, 0, wx.EXPAND, 0 ) - - self.fleetItemContainer = PFFleetItemContainer(self) - - mainSizer.Add(self.fleetItemContainer, 1, wx.EXPAND) - - self.SetSizer(mainSizer) - self.Layout() - - self.filter = "" - self.fleetIDMustEditName = -1 - - self.Bind(wx.EVT_SIZE, self.SizeRefreshList) - - self.Bind(EVT_FLEET_ITEM_NEW, self.AddNewFleetItem) - self.Bind(EVT_FLEET_ITEM_SELECT, self.SelectFleetItem) - self.Bind(EVT_FLEET_ITEM_DELETE, self.DeleteFleetItem) - self.Bind(EVT_FLEET_ITEM_COPY, self.CopyFleetItem) - self.Bind(EVT_FLEET_ITEM_RENAME, self.RenameFleetItem) - - self.PopulateFleetList() - - def AddNewFleetItem(self, event): - fleetName = event.fleetName - newFleet = self.sFleet.addFleet() - self.sFleet.renameFleet(newFleet, fleetName) - - self.fleetIDMustEditName = newFleet.ID - self.AddItem(newFleet.ID, newFleet.name, newFleet.count()) - - def SelectFleetItem(self, event): - fleetID = event.fleetID - self.fleetItemContainer.SelectWidgetByFleetID(fleetID) - wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleetID)) - - def CopyFleetItem(self, event): - fleetID = event.fleetID - fleet = self.sFleet.copyFleetByID(fleetID) - - fleetName = fleet.name + " Copy" - self.sFleet.renameFleet(fleet,fleetName) - - self.fleetIDMustEditName = fleet.ID - self.AddItem(fleet.ID, fleet.name, fleet.count()) - - self.fleetItemContainer.SelectWidgetByFleetID(fleet.ID) - wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleet.ID)) - - def RenameFleetItem(self, event): - fleetID = event.fleetID - fleet = self.sFleet.getFleetByID(fleetID) - - newFleetName = event.fleetName - - self.sFleet.renameFleet(fleet, newFleetName) - wx.PostEvent(self.mainFrame, FleetRenamed(fleetID = fleet.ID)) - - def DeleteFleetItem(self, event): - self.sFleet.deleteFleetByID(event.fleetID) - self.PopulateFleetList() - wx.PostEvent(self.mainFrame, FleetRemoved(fleetID = event.fleetID)) - - def AddItem (self, ID, name, count): - self.fleetItemContainer.AddWidget(FleetItem(self, ID, name, count)) - widget = self.fleetItemContainer.GetWidgetByFleetID(ID) - self.fleetItemContainer.RefreshList(True) - self.fleetItemContainer.ScrollChildIntoView(widget) - wx.PostEvent(self, FleetItemSelect(fleetID = ID)) - - def PopulateFleetList(self): - self.Freeze() - filter = self.filter - self.fleetItemContainer.RemoveAllChildren() - fleetList = self.sFleet.getFleetList() - for fleetID, fleetName, fleetCount in fleetList: - if fleetName.lower().find(filter.lower()) != -1: - self.fleetItemContainer.AddWidget(FleetItem(self, fleetID, fleetName, fleetCount)) - self.fleetItemContainer.RefreshList() - self.Thaw() - - def SetFilter(self, filter): - self.filter = filter - - def SizeRefreshList(self, event): - ewidth, eheight = event.GetSize() - self.Layout() - self.fleetItemContainer.Layout() - self.fleetItemContainer.RefreshList(True) - event.Skip() - - -class FleetBrowserHeader (wx.Panel): - def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 24), style=wx.TAB_TRAVERSAL) - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) - - self.newBmp = BitmapLoader.getBitmap("fit_add_small","gui") - bmpSize = (16,16) - - mainSizer = wx.BoxSizer(wx.HORIZONTAL) - - if 'wxMac' in wx.PlatformInfo: - bgcolour = wx.Colour(0, 0, 0, 0) - else: - bgcolour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) - - self.fbNewFleet = PFGenBitmapButton( self, wx.ID_ANY, self.newBmp, wx.DefaultPosition, bmpSize, wx.BORDER_NONE ) - mainSizer.Add(self.fbNewFleet, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL , 5) - self.fbNewFleet.SetBackgroundColour( bgcolour ) - - self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL ) - mainSizer.Add( self.sl1, 0, wx.EXPAND |wx.LEFT, 5 ) - - self.tcFilter = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.tcFilter, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 ) - - self.stStatus = wx.StaticText( self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stStatus.Wrap( -1 ) - mainSizer.Add( self.stStatus, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 ) - - self.SetSizer(mainSizer) - self.Layout() - - self.fbNewFleet.Bind(wx.EVT_ENTER_WINDOW, self.fbNewEnterWindow) - self.fbNewFleet.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) - self.fbNewFleet.Bind(wx.EVT_BUTTON, self.OnNewFleetItem) - - self.tcFilter.Bind(wx.EVT_TEXT, self.OnFilterText) - - self.tcFilter.Bind(wx.EVT_ENTER_WINDOW, self.fbFilterEnterWindow) - self.tcFilter.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) - - def OnFilterText(self, event): - filter = self.tcFilter.GetValue() - self.Parent.SetFilter(filter) - self.Parent.PopulateFleetList() - event.Skip() - - def OnNewFleetItem(self, event): - wx.PostEvent(self.Parent, FleetItemNew(fleetName = "New Fleet")) - - def fbNewEnterWindow(self, event): - self.stStatus.SetLabel("New fleet") - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) - event.Skip() - - def fbHItemLeaveWindow(self, event): - self.stStatus.SetLabel("") - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) - event.Skip() - - def fbFilterEnterWindow(self, event): - self.stStatus.SetLabel("Filter list") - event.Skip() - - - -class PFFleetItemContainer(PFListPane): - def __init__(self,parent): - PFListPane.__init__(self,parent) - self.selectedWidget = -1 - self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) - - def IsWidgetSelectedByContext(self, widget): - if self.GetWidgetList()[widget].IsSelected(): - return True - return False - - def GetWidgetIndex(self, widgetWnd): - return self.GetWidgetList().index(widgetWnd) - - def GetWidgetByFleetID(self, fleetID): - for widget in self.GetWidgetList(): - if widget.fleetID == fleetID: - return widget - return None - - def SelectWidget(self, widgetWnd): - wlist = self.GetWidgetList() - if self.selectedWidget != -1: - wlist[self.selectedWidget].SetSelected(False) - wlist[self.selectedWidget].Refresh() - windex = self.GetWidgetIndex(widgetWnd) - wlist[windex].SetSelected(True) - wlist[windex].Refresh() - self.selectedWidget = windex - - def SelectWidgetByFleetID(self, fleetID): - widgetWnd = self.GetWidgetByFleetID(fleetID) - if widgetWnd: - self.SelectWidget(widgetWnd) - - def RemoveWidget(self, child): - child.Destroy() - self.selectedWidget = -1 - self._wList.remove(child) - - - def RemoveAllChildren(self): - for widget in self._wList: - widget.Destroy() - - self.selectedWidget = -1 - self._wList = [] - - def OnLeftUp(self, event): - event.Skip() - -class FleetItem(SFItem.SFBrowserItem): - def __init__(self, parent, fleetID, fleetName, fleetCount, - id=wx.ID_ANY, pos=wx.DefaultPosition, - size=(0,40), style=0): - SFItem.SFBrowserItem.__init__(self, parent, size = size) - - self.fleetBrowser = self.Parent - self.fleetID = fleetID - self.fleetName = fleetName - self.fleetCount = fleetCount - - self.padding = 4 - - self.fontBig = wx.FontFromPixelSize((0,15),wx.SWISS, wx.NORMAL, wx.BOLD, False) - self.fontNormal = wx.FontFromPixelSize((0,14),wx.SWISS, wx.NORMAL, wx.NORMAL, False) - self.fontSmall = wx.FontFromPixelSize((0,12),wx.SWISS, wx.NORMAL, wx.NORMAL, False) - - self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") - self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") - self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small","gui") - self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") - self.fleetBmp = BitmapLoader.getBitmap("fleet_item_big", "gui") - - fleetImg = self.fleetBmp.ConvertToImage() - fleetImg = fleetImg.Blur(2) - - if not fleetImg.HasAlpha(): - fleetImg.InitAlpha() - - fleetImg = fleetImg.AdjustChannels(1, 1, 1, 0.5) - self.fleetEffBmp = wx.BitmapFromImage(fleetImg) - - self.toolbar.AddButton(self.copyBmp, "Copy", self.CopyFleetCB) - self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.RenameFleetCB) - self.toolbar.AddButton(self.deleteBmp, "Delete", self.DeleteFleetCB) - - self.editWidth = 150 - self.tcFleetName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fleetName, wx.DefaultPosition, (self.editWidth,-1), wx.TE_PROCESS_ENTER) - - if self.fleetBrowser.fleetIDMustEditName != self.fleetID: - self.tcFleetName.Show(False) - else: - self.tcFleetName.SetFocus() - self.tcFleetName.SelectAll() - self.fleetBrowser.fleetIDMustEditName = -1 - self.renameBtn.SetBitmap(self.acceptBmp) - self.selected = True - - self.tcFleetName.Bind(wx.EVT_KILL_FOCUS, self.OnEditLostFocus) - self.tcFleetName.Bind(wx.EVT_TEXT_ENTER, self.RenameFleet) - self.tcFleetName.Bind(wx.EVT_KEY_DOWN, self.EditCheckEsc) - - - self.animCount = 0 - - def MouseLeftUp(self, event): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - else: - wx.PostEvent(self.fleetBrowser, FleetItemSelect(fleetID = self.fleetID)) - - def CopyFleetCB(self): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - return - - wx.PostEvent(self.fleetBrowser, FleetItemCopy(fleetID = self.fleetID)) - - def RenameFleetCB(self): - - if self.tcFleetName.IsShown(): - - self.RenameFleet(None) - self.RestoreEditButton() - - else: - self.tcFleetName.SetValue(self.fleetName) - self.tcFleetName.Show() - - self.renameBtn.SetBitmap(self.acceptBmp) - self.Refresh() - - self.tcFleetName.SetFocus() - self.tcFleetName.SelectAll() - - self.Refresh() - - def RenameFleet(self, event): - - newFleetName = self.tcFleetName.GetValue() - self.fleetName = newFleetName - - self.tcFleetName.Show(False) - - wx.PostEvent(self.fleetBrowser, FleetItemRename(fleetID = self.fleetID, fleetName = self.fleetName)) - self.Refresh() - - def DeleteFleetCB(self): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - return - wx.PostEvent(self.fleetBrowser, FleetItemDelete(fleetID = self.fleetID)) - - def RestoreEditButton(self): - self.tcFleetName.Show(False) - self.renameBtn.SetBitmap(self.renameBmp) - self.Refresh() - - def OnEditLostFocus(self, event): - self.RestoreEditButton() - self.Refresh() - - def EditCheckEsc(self, event): - if event.GetKeyCode() == wx.WXK_ESCAPE: - self.RestoreEditButton() - else: - event.Skip() - - def IsSelected(self): - return self.selected - - def UpdateElementsPos(self, mdc): - rect = self.GetRect() - - self.toolbarx = rect.width - self.toolbar.GetWidth() - self.padding - self.toolbary = (rect.height - self.toolbar.GetHeight()) / 2 - - self.toolbarx = self.toolbarx + self.animCount - - self.fleetBmpx = self.padding + (rect.height - self.fleetBmp.GetWidth()) / 2 - self.fleetBmpy = (rect.height - self.fleetBmp.GetHeight()) / 2 - - self.fleetBmpx -= self.animCount - - self.textStartx = self.fleetBmpx + self.fleetBmp.GetWidth() + self.padding - - self.fleetNamey = (rect.height - self.fleetBmp.GetHeight()) / 2 - - mdc.SetFont(self.fontBig) - wtext, htext = mdc.GetTextExtent(self.fleetName) - - self.fleetCounty = self.fleetNamey + htext - - mdc.SetFont(self.fontSmall) - - wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) - - self.thoverx = self.toolbarx - self.padding - wlabel - self.thovery = (rect.height - hlabel)/2 - self.thoverw = wlabel - - def DrawItem(self, mdc): - rect = self.GetRect() - - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) - - mdc.SetTextForeground(textColor) - - self.UpdateElementsPos(mdc) - - self.toolbar.SetPosition((self.toolbarx, self.toolbary)) - mdc.DrawBitmap(self.fleetEffBmp, self.fleetBmpx + 3, self.fleetBmpy + 2) - mdc.DrawBitmap(self.fleetBmp, self.fleetBmpx,self.fleetBmpy) - - mdc.SetFont(self.fontNormal) - - suffix = "%d ships" % self.fleetCount if self.fleetCount >1 else "%d ship" % self.fleetCount if self.fleetCount == 1 else "No ships" - fleetCount = "Fleet size: %s" % suffix - fleetCount = drawUtils.GetPartialText(mdc, fleetCount, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) - - mdc.DrawText(fleetCount, self.textStartx, self.fleetCounty) - - mdc.SetFont(self.fontSmall) - mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) - - mdc.SetFont(self.fontBig) - - pfname = drawUtils.GetPartialText(mdc, self.fleetName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) - mdc.DrawText(pfname, self.textStartx, self.fleetNamey) - - if self.tcFleetName.IsShown(): - self.AdjustControlSizePos(self.tcFleetName, self.textStartx, self.toolbarx - self.editWidth - self.padding) - - def AdjustControlSizePos(self, editCtl, start, end): - fnEditSize = editCtl.GetSize() - wSize = self.GetSize() - fnEditPosX = end - fnEditPosY = (wSize.height - fnEditSize.height)/2 - if fnEditPosX < start: - editCtl.SetSize((self.editWidth + fnEditPosX - start,-1)) - editCtl.SetPosition((start,fnEditPosY)) - else: - editCtl.SetSize((self.editWidth,-1)) - editCtl.SetPosition((fnEditPosX,fnEditPosY)) - - -class PFGenBitmapButton(GenBitmapButton): - def __init__(self, parent, id, bitmap, pos, size, style): - GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style) - self.bgcolor = wx.Brush(wx.WHITE) - - def SetBackgroundColour(self, color): - self.bgcolor = wx.Brush(color) - - def GetBackgroundBrush(self, dc): - return self.bgcolor +# purging fleet \ No newline at end of file diff --git a/gui/gangView.py b/gui/gangView.py index 106f14f98..d5f8e43cb 100644 --- a/gui/gangView.py +++ b/gui/gangView.py @@ -16,397 +16,5 @@ # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . #=============================================================================== -import wx -from wx.lib.scrolledpanel import ScrolledPanel -import service -import gui.mainFrame -import gui.shipBrowser -import gui.globalEvents as GE - -from gui import characterEditor as CharEditor - -class GangView ( ScrolledPanel ): - - def __init__( self, parent ): - ScrolledPanel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 100,20 ), style = wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL ) - mainSizer = wx.BoxSizer( wx.VERTICAL ) - - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - self.draggedFitID = None - - help = '''Set fit as booster to display in dropdown, or drag fitting from\nship browser to this window, or right click fit and select booster role.''' - helpSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.helpText = wx.StaticText( self, wx.ID_ANY, help, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) - helpSizer.Add( self.helpText, 1, wx.ALL, 5 ) - - self.options = ["Fleet", "Wing", "Squad"] - - self.fleet = {} - for id, option in enumerate(self.options): - - # set content for each commander - self.fleet[id] = {} - self.fleet[id]['stLabel'] = wx.StaticText( self, wx.ID_ANY, self.options[id]+':', wx.DefaultPosition, wx.DefaultSize, 0 ) - self.fleet[id]['stText'] = wx.StaticText( self, wx.ID_ANY, 'None', wx.DefaultPosition, wx.DefaultSize, 0 ) - self.fleet[id]['chFit'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] ) - self.fleet[id]['chChar'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] ) - self.fleet[id]['fitSizer'] = wx.BoxSizer( wx.VERTICAL ) - - self.FitDNDPopupMenu = self.buildBoostermenu() - - contentFGSizer = wx.FlexGridSizer( 5, 3, 0, 0 ) - contentFGSizer.AddGrowableCol( 1 ) - contentFGSizer.SetFlexibleDirection( wx.BOTH ) - contentFGSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) - - ### Header - self.stBooster = wx.StaticText( self, wx.ID_ANY, u"Booster", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stBooster.Wrap( -1 ) - self.stBooster.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - contentFGSizer.Add( self.stBooster, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 5 ) - - self.stFits = wx.StaticText( self, wx.ID_ANY, u"Fits", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stFits.Wrap( -1 ) - self.stFits.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - contentFGSizer.Add( self.stFits, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) - - self.stCharacters = wx.StaticText( self, wx.ID_ANY, u"Characters", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stCharacters.Wrap( -1 ) - self.stCharacters.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - contentFGSizer.Add( self.stCharacters, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) - - self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - contentFGSizer.Add( self.m_staticline2, 0, wx.EXPAND, 5 ) - - self.m_staticline3 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - contentFGSizer.Add( self.m_staticline3, 0, wx.EXPAND, 5 ) - - self.m_staticline4 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - contentFGSizer.Add( self.m_staticline4, 0, wx.EXPAND, 5 ) - - ### Content - for id in self.fleet: - # set various properties - self.fleet[id]['stLabel'].Wrap( -1 ) - self.fleet[id]['stLabel'].SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - self.fleet[id]['stText'].Wrap( -1 ) - - # bind text and choice events - self.fleet[id]['stText'].Bind(wx.EVT_LEFT_DCLICK, self.RemoveBooster) - self.fleet[id]['stText'].Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow) - self.fleet[id]['stText'].Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - self.fleet[id]['stText'].SetToolTip(wx.ToolTip("Double click to remove booster")) - self.fleet[id]['chChar'].Bind(wx.EVT_CHOICE, self.CharChanged) - self.fleet[id]['chFit'].Bind(wx.EVT_CHOICE, self.OnFitChoiceSelected) - - # add fit text and choice to the fit sizer - self.fleet[id]['fitSizer'].Add( self.fleet[id]['stText'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) - self.fleet[id]['fitSizer'].Add( self.fleet[id]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 1 ) - - # add everything to the content sizer - contentFGSizer.Add( self.fleet[id]['stLabel'], 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) - contentFGSizer.Add( self.fleet[id]['fitSizer'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 ) - contentFGSizer.Add( self.fleet[id]['chChar'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) - - mainSizer.Add( contentFGSizer, 1, wx.EXPAND, 0 ) - mainSizer.Add( helpSizer, 0, wx.EXPAND, 0 ) - - self.SetSizer( mainSizer ) - self.SetAutoLayout(True) - self.SetupScrolling() - - self.mainFrame.Bind(GE.CHAR_LIST_UPDATED, self.RefreshCharacterList) - self.mainFrame.Bind(GE.FIT_CHANGED, self.fitSelected) - self.mainFrame.Bind(gui.shipBrowser.EVT_FIT_RENAMED, self.fitRenamed) - self.mainFrame.Bind(gui.shipBrowser.BOOSTER_LIST_UPDATED, self.RefreshBoosterFits) - - self.RefreshBoosterFits() - self.RefreshCharacterList() - - def buildBoostermenu(self): - menu = wx.Menu() - - for id, option in enumerate(self.options): - item = menu.Append(-1, option) - # We bind it to the mainFrame because it may be called from either this class or from FitItem via shipBrowser - self.mainFrame.Bind(wx.EVT_MENU, self.OnPopupItemSelected, item) - return menu - - def OnEnterWindow(self, event): - obj = event.GetEventObject() - obj.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) - event.Skip() - - def OnLeaveWindow(self, event): - obj = event.GetEventObject() - obj.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) - event.Skip() - - def CharChanged(self, event): - ''' Change booster character ''' - chBooster = event.GetEventObject() - - type = -1 - for id in self.fleet: - if chBooster == self.fleet[id]['chChar']: type = id - - if type == -1: - event.Skip() - return - - sFit = service.Fit.getInstance() - - fleetSrv = service.Fleet.getInstance() - - activeFitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(activeFitID) - - sChar = service.Character.getInstance() - charList = sChar.getCharacterList() - - if activeFitID: - commanders = fleetSrv.loadLinearFleet(fit) - if commanders is None: - fleetCom, wingCom, squadCom = (None, None, None) - else: - fleetCom, wingCom, squadCom = commanders - - if type == 0: - if fleetCom: - charID = chBooster.GetClientData(chBooster.GetSelection()) - sFit.changeChar(fleetCom.ID, charID) - else: - chBooster.SetSelection(0) - - if type == 1: - if wingCom: - charID = chBooster.GetClientData(chBooster.GetSelection()) - sFit.changeChar(wingCom.ID, charID) - else: - chBooster.SetSelection(0) - - if type == 2: - if squadCom: - charID = chBooster.GetClientData(chBooster.GetSelection()) - sFit.changeChar(squadCom.ID, charID) - else: - chBooster.SetSelection(0) - - sFit.recalc(fit, withBoosters=True) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - - else: - chBooster.SetSelection(0) - - def RemoveBooster(self, event): - activeFitID = self.mainFrame.getActiveFit() - if not activeFitID: - return - - location = event.GetEventObject() - - for id in self.fleet: - if location == self.fleet[id]['stText']: type = id - - sFit = service.Fit.getInstance() - boostee = sFit.getFit(activeFitID) - booster = None - - fleetSrv = service.Fleet.getInstance() - - if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) - if type == 1: fleetSrv.setLinearWingCom(boostee, booster) - if type == 2: fleetSrv.setLinearSquadCom(boostee, booster) - - # Hide stText and, default fit selection, and enable it - location.Hide() - choice = self.fleet[type]['chFit'] - choice.SetSelection(0) - choice.Show() - - sFit.recalc(boostee, withBoosters=True) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - - def fitRenamed(self, event): - fleetSrv = service.Fleet.getInstance() - activeFitID = self.mainFrame.getActiveFit() - - if activeFitID: - ev = event - ev.fitID = activeFitID - self.fitSelected(ev) - - def fitSelected(self, event): - ''' Fires when active fit is selected and when booster is saved to fit. Update the UI to reflect changes ''' - fleetSrv = service.Fleet.getInstance() - - activeFitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() - fit = sFit.getFit(event.fitID or activeFitID) - - self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - - commanders = (None, None, None) - - if activeFitID: - commanders = fleetSrv.loadLinearFleet(fit) - - for id in self.fleet: - # try...except here as we're trying 2 different criteria and want to fall back on the same code - try: - commander = commanders[id] - - if not activeFitID or commander is None: - raise Exception() - - self.fleet[id]['stText'].SetLabel(commander.ship.item.name + ": " + commander.name) - self.fleet[id]['chChar'].SetStringSelection(commander.character.name if commander.character is not None else "All 0") - self.fleet[id]['chChar'].Enable() - self.fleet[id]['chFit'].Hide() - self.fleet[id]['stText'].Show() - except: - #set defaults, disable char selection, and enable fit selection - self.fleet[id]['stText'].SetLabel("None") - self.fleet[id]['chChar'].SetStringSelection("All 0") - self.fleet[id]['chChar'].Disable() - self.fleet[id]['chFit'].SetSelection(0) - self.fleet[id]['chFit'].Show() - self.fleet[id]['stText'].Hide() - - if activeFitID: - self.Enable() - else: - self.Disable() - - self.Layout() - self.SendSizeEvent() - - def AddCommander(self, fitID, type = None): - ''' Adds booster to a fit, then recalculates active fit ''' - if type is None: - return - - activeFitID = self.mainFrame.getActiveFit() - if activeFitID: - sFit = service.Fit.getInstance() - - boostee = sFit.getFit(activeFitID) - booster = sFit.getFit(fitID) - - fleetSrv = service.Fleet.getInstance() - - if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) - if type == 1: fleetSrv.setLinearWingCom(boostee, booster) - if type == 2: fleetSrv.setLinearSquadCom(boostee, booster) - - sFit.recalc(boostee) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - - def RefreshBoosterFits(self, event = None): - sFit = service.Fit.getInstance() - sMkt = service.Market.getInstance() - fitList = sFit.getBoosterFits() - - for id in self.fleet: - choice = self.fleet[id]['chFit'] - chCurrSelection = choice.GetSelection() - chCurrData = -1 - if chCurrSelection != -1: - chCurrData = choice.GetClientData(chCurrSelection) - chCurrSelString = choice.GetString(chCurrSelection) - choice.Clear() - currSelFound = False - choice.Append("None", -1) - for fit in fitList: - id,name,type = fit - ship = sMkt.getItem(type) - choice.Append(ship.name+': '+name, id) - if chCurrData == id: - currSelFound = True - - if chCurrSelection == -1: - choice.SetSelection(0) - else: - if currSelFound: - choice.SetStringSelection(chCurrSelString) - else: - choice.SetSelection(0) - - def RefreshCharacterList(self, event = None): - sChar = service.Character.getInstance() - charList = sChar.getCharacterList() - for id in self.fleet: - choice = self.fleet[id]['chChar'] - chCurrSelection = choice.GetSelection() - chCurrData = -1 - if chCurrSelection != -1: - chCurrData = choice.GetClientData(chCurrSelection) - chCurrSelString = choice.GetString(chCurrSelection) - choice.Clear() - currSelFound = False - for char in charList: - choice.Append(char.name, char.ID) - if chCurrData == char.ID: - currSelFound = True - - if chCurrSelection == -1: - choice.SetSelection(1) - else: - if currSelFound: - choice.SetStringSelection(chCurrSelString) - else: - choice.SetSelection(1) - - def handleDrag(self, type, fitID): - ''' Handle dragging of fit to fleet interface ''' - #Those are drags coming from pyfa sources, NOT builtin wx drags - self.draggedFitID = None - if type == "fit": - sFit = service.Fit.getInstance() - fit = sFit.getFit(self.mainFrame.getActiveFit()) - - if fit and not fit.isStructure: - self.draggedFitID = fitID - - pos = wx.GetMousePosition() - pos = self.ScreenToClient(pos) - - self.PopupMenu(self.FitDNDPopupMenu, pos) - - - def OnPopupItemSelected(self, event): - ''' Fired when booster popup item is selected ''' - # Get menu selection ID via self.options - menuItem = event.EventObject.FindItemById(event.GetId()) - type = self.options.index(menuItem.GetText()) - - if self.draggedFitID: - sFit = service.Fit.getInstance() - draggedFit = sFit.getFit(self.draggedFitID) - - self.AddCommander(draggedFit.ID, type) - self.mainFrame.additionsPane.select("Fleet") - - def OnFitChoiceSelected(self, event): - ''' Fired when booster choice is selected ''' - sFit = service.Fit.getInstance() - - # set type via choice box used - chFit = event.GetEventObject() - fitID = chFit.GetClientData(chFit.GetSelection()) - - type = -1 - for id in self.fleet: - if chFit == self.fleet[id]['chFit']: type = id - - if type == -1 or fitID == -1: - event.Skip() - return - - fit = sFit.getFit(fitID) - - self.AddCommander(fit.ID, type) - self.mainFrame.additionsPane.select("Fleet") +#Purging fleet boosts \ No newline at end of file diff --git a/gui/mainFrame.py b/gui/mainFrame.py index c209a0422..220a8f0ab 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -55,7 +55,6 @@ from gui.preferenceDialog import PreferenceDialog from gui.graphFrame import GraphFrame from gui.copySelectDialog import CopySelectDialog from gui.utils.clipboard import toClipboard, fromClipboard -from gui.fleetBrowser import FleetBrowser from gui.updateDialog import UpdateDialog from gui.builtinViews import * @@ -158,12 +157,6 @@ class MainFrame(wx.Frame): self.shipBrowser = ShipBrowser(self.notebookBrowsers) self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage = shipBrowserImg, showClose = False) - #======================================================================= - # DISABLED FOR RC2 RELEASE - #self.fleetBrowser = FleetBrowser(self.notebookBrowsers) - #self.notebookBrowsers.AddPage(self.fleetBrowser, "Fleets", showClose = False) - #======================================================================= - self.notebookBrowsers.SetSelection(1) self.browser_fitting_split.SplitVertically(self.notebookBrowsers, self.fitting_additions_split) diff --git a/service/__init__.py b/service/__init__.py index b19c6db17..91ed07d82 100644 --- a/service/__init__.py +++ b/service/__init__.py @@ -5,7 +5,6 @@ from service.character import Character from service.damagePattern import DamagePattern from service.targetResists import TargetResists from service.settings import SettingsProvider -from service.fleet import Fleet from service.update import Update from service.price import Price from service.network import Network diff --git a/service/fit.py b/service/fit.py index 455e76463..d05865514 100644 --- a/service/fit.py +++ b/service/fit.py @@ -34,7 +34,6 @@ from eos.types import State, Slot from service.market import Market from service.damagePattern import DamagePattern from service.character import Character -from service.fleet import Fleet from service.settings import SettingsProvider from service.port import Port @@ -184,8 +183,6 @@ class Fit(object): def deleteFit(self, fitID): fit = eos.db.getFit(fitID) - sFleet = Fleet.getInstance() - sFleet.removeAssociatedFleetData(fit) eos.db.remove(fit) @@ -236,7 +233,7 @@ class Fit(object): self.recalc(fit, withBoosters=True) def getFit(self, fitID, projected=False, basic=False): - ''' Gets fit from database, and populates fleet data. + ''' Gets fit from database. Projected is a recursion flag that is set to reduce recursions into projected fits Basic is a flag to simply return the fit without any other processing @@ -251,14 +248,6 @@ class Fit(object): inited = getattr(fit, "inited", None) if inited is None or inited is False: - sFleet = Fleet.getInstance() - f = sFleet.getLinearFleet(fit) - if f is None: - sFleet.removeAssociatedFleetData(fit) - fit.fleet = None - else: - fit.fleet = f - if not projected: for fitP in fit.projectedFits: self.getFit(fitP.ID, projected=True) diff --git a/service/fleet.py b/service/fleet.py index cfad7f04b..3a158cb2d 100644 --- a/service/fleet.py +++ b/service/fleet.py @@ -17,199 +17,4 @@ # along with pyfa. If not, see . #=============================================================================== -import eos.db -from eos.types import Fleet as Fleet_, Wing, Squad -import copy - -class Fleet(object): - instance = None - @classmethod - def getInstance(cls): - if cls.instance is None: - cls.instance = Fleet() - - return cls.instance - - def __init__(self): - pass - - def getFleetList(self): - fleetList = [] - fleets = eos.db.getFleetList() - for fleet in fleets: - fleetList.append((fleet.ID, fleet.name, fleet.count())) - - return fleetList - - def getFleetByID(self, ID): - f = eos.db.getFleet(ID) - return f - - def addFleet(self): - f = Fleet_() - eos.db.save(f) - return f - - def renameFleet(self, fleet, newName): - fleet.name = newName - eos.db.commit() - - def copyFleet(self, fleet): - newFleet = copy.deepcopy(fleet) - eos.db.save(newFleet) - return newFleet - - def copyFleetByID(self, ID): - fleet = self.getFleetByID(ID) - return self.copyFleet(fleet) - - def deleteFleet(self, fleet): - eos.db.remove(fleet) - - def deleteFleetByID(self, ID): - fleet = self.getFleetByID(ID) - self.deleteFleet(fleet) - - def makeLinearFleet(self, fit): - f = Fleet_() - w = Wing() - f.wings.append(w) - s = Squad() - w.squads.append(s) - s.members.append(fit) - fit.fleet = f - eos.db.save(f) - - def setLinearFleetCom(self, boostee, booster): - #if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.wing.gang.leader is not None and booster is None: - try: - squad.wing.gang.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.wing.gang.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - def setLinearWingCom(self, boostee, booster): - #if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.wing.leader is not None and booster is None: - try: - squad.wing.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.wing.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - def setLinearSquadCom(self, boostee, booster): - #if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.leader is not None and booster is None: - try: - squad.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - - def getLinearFleet(self, fit): - sqIDs = eos.db.getSquadsIDsWithFitID(fit.ID) - if len(sqIDs) != 1: - return None - s = eos.db.getSquad(sqIDs[0]) - if len(s.members) != 1: - return None - w = s.wing - if len(w.squads) != 1: - return None - f = w.gang - if len(f.wings) != 1: - return None - return f - - def removeAssociatedFleetData(self, fit): - squadIDs = set(eos.db.getSquadsIDsWithFitID(fit.ID)) - if len(squadIDs) == 0: - return - squads = list(eos.db.getSquad(sqID) for sqID in squadIDs) - wingIDs = set(squad.wing.ID for squad in squads) - fleetIDs = set(squad.wing.gang.ID for squad in squads) - for fleetID in fleetIDs: - fleet = eos.db.getFleet(fleetID) - for wing in fleet.wings: - wingIDs.add(wing.ID) - for wingID in wingIDs: - wing = eos.db.getWing(wingID) - for squad in wing.squads: - squadIDs.add(squad.ID) - for squadID in squadIDs: - squad = eos.db.getSquad(squadID) - if squad.leader is not None: - try: - squad.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(squad) - for wingID in wingIDs: - wing = eos.db.getWing(wingID) - if wing.leader is not None: - try: - wing.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(wing) - for fleetID in fleetIDs: - fleet = eos.db.getFleet(fleetID) - if fleet.leader is not None: - try: - fleet.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(fleet) - fit.fleet = None - return - - def anyBoosters(self, squad): - wing = squad.wing - fleet = wing.gang - if squad.leader is None and wing.leader is None and fleet.leader is None: - return False - return True - - def loadLinearFleet(self, fit): - if self.getLinearFleet(fit) is None: - return None - squadID = eos.db.getSquadsIDsWithFitID(fit.ID)[0] - s = eos.db.getSquad(squadID) - w = s.wing - f = w.gang - return (f.leader, w.leader, s.leader) +#Purge fleet boosts From 6e54d6788cd4784c5d6b76c26a2ec243e4c87cad Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Sep 2016 15:27:10 -0700 Subject: [PATCH 02/26] Revert "Purging fleet bonuses from code base" This reverts commit 68f45706ab7dc9c2e68a3f9d868b37b1ce56decc. --- eos/db/saveddata/fleet.py | 48 +++- eos/db/saveddata/queries.py | 58 +++++ eos/types.py | 1 + gui/additionsPane.py | 7 +- gui/builtinViews/fleetView.py | 141 ++++++++++- gui/fleetBrowser.py | 457 +++++++++++++++++++++++++++++++++- gui/gangView.py | 394 ++++++++++++++++++++++++++++- gui/mainFrame.py | 7 + service/__init__.py | 1 + service/fit.py | 13 +- service/fleet.py | 197 ++++++++++++++- 11 files changed, 1317 insertions(+), 7 deletions(-) diff --git a/eos/db/saveddata/fleet.py b/eos/db/saveddata/fleet.py index 0583b43b0..8f0e6736b 100644 --- a/eos/db/saveddata/fleet.py +++ b/eos/db/saveddata/fleet.py @@ -17,4 +17,50 @@ # along with eos. If not, see . #=============================================================================== -#Purging fleet \ No newline at end of file +from sqlalchemy import Table, Column, Integer, ForeignKey, String +from sqlalchemy.orm import mapper, relation + +from eos.db import saveddata_meta +from eos.types import Fleet, Wing, Squad, Fit +from eos.db.saveddata.fit import fits_table + +gangs_table = Table("gangs", saveddata_meta, + Column("ID", Integer, primary_key = True), + Column("leaderID", ForeignKey("fits.ID")), + Column("boosterID", ForeignKey("fits.ID")), + Column("name", String)) + +wings_table = Table("wings", saveddata_meta, + Column("ID", Integer, primary_key = True), + Column("gangID", ForeignKey("gangs.ID")), + Column("boosterID", ForeignKey("fits.ID")), + Column("leaderID", ForeignKey("fits.ID"))) + +squads_table = Table("squads", saveddata_meta, + Column("ID", Integer, primary_key = True), + Column("wingID", ForeignKey("wings.ID")), + Column("leaderID", ForeignKey("fits.ID")), + Column("boosterID", ForeignKey("fits.ID"))) + +squadmembers_table = Table("squadmembers", saveddata_meta, + Column("squadID", ForeignKey("squads.ID"), primary_key = True), + Column("memberID", ForeignKey("fits.ID"), primary_key = True)) + +mapper(Fleet, gangs_table, + properties = {"wings" : relation(Wing, backref="gang"), + "leader" : relation(Fit, primaryjoin = gangs_table.c.leaderID == fits_table.c.ID), + "booster": relation(Fit, primaryjoin = gangs_table.c.boosterID == fits_table.c.ID)}) + +mapper(Wing, wings_table, + properties = {"squads" : relation(Squad, backref="wing"), + "leader" : relation(Fit, primaryjoin = wings_table.c.leaderID == fits_table.c.ID), + "booster": relation(Fit, primaryjoin = wings_table.c.boosterID == fits_table.c.ID)}) + +mapper(Squad, squads_table, + properties = {"leader" : relation(Fit, primaryjoin = squads_table.c.leaderID == fits_table.c.ID), + "booster" : relation(Fit, primaryjoin = squads_table.c.boosterID == fits_table.c.ID), + "members" : relation(Fit, + primaryjoin = squads_table.c.ID == squadmembers_table.c.squadID, + secondaryjoin = squadmembers_table.c.memberID == fits_table.c.ID, + secondary = squadmembers_table)}) + diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index cb3033cc1..5a6dd1bfc 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -21,6 +21,7 @@ from eos.db.util import processEager, processWhere from eos.db import saveddata_session, sd_lock from eos.types import * +from eos.db.saveddata.fleet import squadmembers_table from eos.db.saveddata.fit import projectedFits_table from sqlalchemy.sql import and_ import eos.config @@ -193,6 +194,48 @@ def getFit(lookfor, eager=None): return fit +@cachedQuery(Fleet, 1, "fleetID") +def getFleet(fleetID, eager=None): + if isinstance(fleetID, int): + if eager is None: + with sd_lock: + fleet = saveddata_session.query(Fleet).get(fleetID) + else: + eager = processEager(eager) + with sd_lock: + fleet = saveddata_session.query(Fleet).options(*eager).filter(Fleet.ID == fleetID).first() + else: + raise TypeError("Need integer as argument") + return fleet + +@cachedQuery(Wing, 1, "wingID") +def getWing(wingID, eager=None): + if isinstance(wingID, int): + if eager is None: + with sd_lock: + wing = saveddata_session.query(Wing).get(wingID) + else: + eager = processEager(eager) + with sd_lock: + wing = saveddata_session.query(Wing).options(*eager).filter(Wing.ID == wingID).first() + else: + raise TypeError("Need integer as argument") + return wing + +@cachedQuery(Squad, 1, "squadID") +def getSquad(squadID, eager=None): + if isinstance(squadID, int): + if eager is None: + with sd_lock: + squad = saveddata_session.query(Squad).get(squadID) + else: + eager = processEager(eager) + with sd_lock: + squad = saveddata_session.query(Squad).options(*eager).filter(Fleet.ID == squadID).first() + else: + raise TypeError("Need integer as argument") + return squad + def getFitsWithShip(shipID, ownerID=None, where=None, eager=None): """ Get all the fits using a certain ship. @@ -265,6 +308,12 @@ def getFitList(eager=None): return fits +def getFleetList(eager=None): + eager = processEager(eager) + with sd_lock: + fleets = saveddata_session.query(Fleet).options(*eager).all() + return fleets + @cachedQuery(Price, 1, "typeID") def getPrice(typeID): if isinstance(typeID, int): @@ -374,6 +423,15 @@ def searchFits(nameLike, where=None, eager=None): return fits +def getSquadsIDsWithFitID(fitID): + if isinstance(fitID, int): + with sd_lock: + squads = saveddata_session.query(squadmembers_table.c.squadID).filter(squadmembers_table.c.memberID == fitID).all() + squads = tuple(entry[0] for entry in squads) + return squads + else: + raise TypeError("Need integer as argument") + def getProjectedFits(fitID): if isinstance(fitID, int): with sd_lock: diff --git a/eos/types.py b/eos/types.py index f2550ad31..12e7eb281 100644 --- a/eos/types.py +++ b/eos/types.py @@ -38,6 +38,7 @@ from eos.saveddata.booster import SideEffect from eos.saveddata.booster import Booster from eos.saveddata.fit import Fit, ImplantLocation from eos.saveddata.mode import Mode +from eos.saveddata.fleet import Fleet, Wing, Squad from eos.saveddata.miscData import MiscData from eos.saveddata.override import Override diff --git a/gui/additionsPane.py b/gui/additionsPane.py index f1ca44be4..8c855dc95 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -26,6 +26,7 @@ from gui.cargoView import CargoView from gui.implantView import ImplantView from gui.projectedView import ProjectedView from gui.pyfatogglepanel import TogglePanel +from gui.gangView import GangView from gui.bitmapLoader import BitmapLoader import gui.chromeTabs @@ -54,6 +55,7 @@ class AdditionsPane(TogglePanel): implantImg = BitmapLoader.getImage("implant_small", "gui") boosterImg = BitmapLoader.getImage("booster_small", "gui") projectedImg = BitmapLoader.getImage("projected_small", "gui") + gangImg = BitmapLoader.getImage("fleet_fc_small", "gui") cargoImg = BitmapLoader.getImage("cargo_small", "gui") self.drone = DroneView(self.notebook) @@ -74,9 +76,12 @@ class AdditionsPane(TogglePanel): self.projectedPage = ProjectedView(self.notebook) self.notebook.AddPage(self.projectedPage, "Projected", tabImage = projectedImg, showClose = False) + self.gangPage = GangView(self.notebook) + self.notebook.AddPage(self.gangPage, "Fleet", tabImage = gangImg, showClose = False) + self.notebook.SetSelection(0) - PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected"] + PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected", "Fleet"] def select(self, name): self.notebook.SetSelection(self.PANES.index(name)) diff --git a/gui/builtinViews/fleetView.py b/gui/builtinViews/fleetView.py index 9058bc35f..bc6eef9a2 100644 --- a/gui/builtinViews/fleetView.py +++ b/gui/builtinViews/fleetView.py @@ -1 +1,140 @@ -#Purge fleet boosts \ No newline at end of file +import wx.gizmos +import gui.fleetBrowser +import service +from gui.bitmapLoader import BitmapLoader + +#Tab spawning handler +class FleetSpawner(gui.multiSwitch.TabSpawner): + def __init__(self, multiSwitch): + self.multiSwitch = multiSwitch + mainFrame = gui.mainFrame.MainFrame.getInstance() + mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_SELECTED, self.fleetSelected) + + def fleetSelected(self, event): + if self.multiSwitch.GetPageCount() == 0: + self.multiSwitch.AddPage(wx.Panel(self.multiSwitch, size = (0,0)), "Empty Tab") + + view = FleetView(self.multiSwitch) + self.multiSwitch.ReplaceActivePage(view) + view.populate(event.fleetID) + view.Show() + +FleetSpawner.register() + +class FleetView(wx.gizmos.TreeListCtrl): + def __init__(self, parent, size = (0,0)): + wx.gizmos.TreeListCtrl.__init__(self, parent, size = size) + + self.tabManager = parent + + self.fleetId = None + self.fleetImg = BitmapLoader.getImage("53_16", "icons") + + self.imageList = wx.ImageList(16, 16) + self.SetImageList(self.imageList) + + for col in ("", "Fit", "Shiptype", "Character", "Bonusses"): + self.AddColumn(col) + + self.SetMainColumn(1) + self.icons = {} + self.addImage = self.imageList.Add(BitmapLoader.getBitmap("add_small", "gui")) + for icon in ("fb", "fc", "sb", "sc", "wb", "wc"): + self.icons[icon] = self.imageList.Add(BitmapLoader.getBitmap("fleet_%s_small" % icon, "gui")) + + self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.checkNew) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_RENAMED, self.fleetRenamed) + self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_REMOVED, self.fleetRemoved) + + def Destroy(self): + self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_REMOVED, handler = self.fleetRemoved) + self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_RENAMED, handler = self.fleetRenamed) + wx.gizmos.TreeListCtrl.Destroy(self) + + def fleetRenamed(self, event): + if event.fleetID == self.fleetId: + sFleet = service.Fleet.getInstance() + f = sFleet.getFleetByID(event.fleetID) + self.UpdateTab(f.name, self.fleetImg) + + event.Skip() + + def fleetRemoved(self, event): + if event.fleetID == self.fleetId: + self.tabManager.DeletePage(self.tabManager.GetPageIndex(self)) + + event.Skip() + + def checkNew(self, event): + data = self.GetPyData(event.Item) + if data and isinstance(data, tuple) and data[0] == "add": + layer = data[1] + + + def UpdateTab(self, name, img): + self.tabManager.SetPageTextIcon(self.tabManager.GetSelection(), name, img) + + def populate(self, fleetID): + sFleet = service.Fleet.getInstance() + f = sFleet.getFleetByID(fleetID) + self.fleetId = fleetID + + self.UpdateTab( f.name, self.fleetImg) + self.fleet = f + self.DeleteAllItems() + root = self.AddRoot("") + + self.setEntry(root, f.leader, "fleet", f) + for wing in f.wings: + wingId = self.AppendItem(root, "") + self.setEntry(wingId, wing.leader, "wing", wing) + for squad in wing.squads: + for member in squad.members: + memberId = self.AppendItem(wingId, "") + self.setEntry(memberId, member, "squad", squad) + + self.addAdder(wingId, "squad") + + self.addAdder(root, "wing") + + self.ExpandAll(root) + self.SetColumnWidth(0, 16) + for i in xrange(1, 5): + self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) + headerWidth = self.GetColumnWidth(i) + 5 + self.SetColumnWidth(i, wx.LIST_AUTOSIZE) + baseWidth = self.GetColumnWidth(i) + if baseWidth < headerWidth: + self.SetColumnWidth(i, headerWidth) + else: + self.SetColumnWidth(i, baseWidth) + + + def addAdder(self, treeItemId, layer): + id = self.AppendItem(treeItemId, "Add new %s" % layer.capitalize()) + self.SetPyData(id, ("add", layer)) + self.SetItemImage(id, self.addImage, 1) + + def setEntry(self, treeItemId, fit, layer, info): + self.SetPyData(treeItemId, info) + if fit is None: + self.SetItemText(treeItemId, "%s Commander" % layer.capitalize(), 1) + else: + fleet = self.fleet + if fit == info.booster: + self.SetItemImage(treeItemId, self.icons["%sb" % layer[0]], 0) + elif fit == info.leader: + self.SetItemImage(treeItemId, self.icons["%sc" % layer[0]], 1) + + self.SetItemText(treeItemId, fit.name, 1) + self.SetItemText(treeItemId, fit.ship.item.name, 2) + self.SetItemText(treeItemId, fit.character.name, 3) + boosts = fleet.store.getBoosts(fit) + if boosts: + bonusses = [] + for name, info in boosts.iteritems(): + bonusses.append("%s: %.2g" % (name, info[0])) + + self.SetItemText(treeItemId, ", ".join(bonusses), 3) diff --git a/gui/fleetBrowser.py b/gui/fleetBrowser.py index bb28be251..7476d7d7f 100644 --- a/gui/fleetBrowser.py +++ b/gui/fleetBrowser.py @@ -1 +1,456 @@ -# purging fleet \ No newline at end of file +import wx +import copy +from gui.bitmapLoader import BitmapLoader +import gui.mainFrame +from gui.PFListPane import PFListPane +import service.fleet +from gui.utils.drawUtils import GetPartialText + +from wx.lib.buttons import GenBitmapButton + +import gui.utils.colorUtils as colorUtils +import gui.utils.drawUtils as drawUtils + +import gui.sfBrowserItem as SFItem + +FleetSelected, EVT_FLEET_SELECTED = wx.lib.newevent.NewEvent() +FleetRenamed, EVT_FLEET_RENAMED = wx.lib.newevent.NewEvent() +FleetRemoved, EVT_FLEET_REMOVED = wx.lib.newevent.NewEvent() + + +FleetItemSelect, EVT_FLEET_ITEM_SELECT = wx.lib.newevent.NewEvent() +FleetItemDelete, EVT_FLEET_ITEM_DELETE = wx.lib.newevent.NewEvent() +FleetItemNew, EVT_FLEET_ITEM_NEW = wx.lib.newevent.NewEvent() +FleetItemCopy, EVT_FLEET_ITEM_COPY = wx.lib.newevent.NewEvent() +FleetItemRename, EVT_FLEET_ITEM_RENAME = wx.lib.newevent.NewEvent() + + + +class FleetBrowser(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent) + + self.sFleet = service.fleet.Fleet.getInstance() + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + mainSizer = wx.BoxSizer(wx.VERTICAL) + + self.hpane = FleetBrowserHeader(self) + mainSizer.Add(self.hpane, 0, wx.EXPAND) + + self.m_sl2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + mainSizer.Add( self.m_sl2, 0, wx.EXPAND, 0 ) + + self.fleetItemContainer = PFFleetItemContainer(self) + + mainSizer.Add(self.fleetItemContainer, 1, wx.EXPAND) + + self.SetSizer(mainSizer) + self.Layout() + + self.filter = "" + self.fleetIDMustEditName = -1 + + self.Bind(wx.EVT_SIZE, self.SizeRefreshList) + + self.Bind(EVT_FLEET_ITEM_NEW, self.AddNewFleetItem) + self.Bind(EVT_FLEET_ITEM_SELECT, self.SelectFleetItem) + self.Bind(EVT_FLEET_ITEM_DELETE, self.DeleteFleetItem) + self.Bind(EVT_FLEET_ITEM_COPY, self.CopyFleetItem) + self.Bind(EVT_FLEET_ITEM_RENAME, self.RenameFleetItem) + + self.PopulateFleetList() + + def AddNewFleetItem(self, event): + fleetName = event.fleetName + newFleet = self.sFleet.addFleet() + self.sFleet.renameFleet(newFleet, fleetName) + + self.fleetIDMustEditName = newFleet.ID + self.AddItem(newFleet.ID, newFleet.name, newFleet.count()) + + def SelectFleetItem(self, event): + fleetID = event.fleetID + self.fleetItemContainer.SelectWidgetByFleetID(fleetID) + wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleetID)) + + def CopyFleetItem(self, event): + fleetID = event.fleetID + fleet = self.sFleet.copyFleetByID(fleetID) + + fleetName = fleet.name + " Copy" + self.sFleet.renameFleet(fleet,fleetName) + + self.fleetIDMustEditName = fleet.ID + self.AddItem(fleet.ID, fleet.name, fleet.count()) + + self.fleetItemContainer.SelectWidgetByFleetID(fleet.ID) + wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleet.ID)) + + def RenameFleetItem(self, event): + fleetID = event.fleetID + fleet = self.sFleet.getFleetByID(fleetID) + + newFleetName = event.fleetName + + self.sFleet.renameFleet(fleet, newFleetName) + wx.PostEvent(self.mainFrame, FleetRenamed(fleetID = fleet.ID)) + + def DeleteFleetItem(self, event): + self.sFleet.deleteFleetByID(event.fleetID) + self.PopulateFleetList() + wx.PostEvent(self.mainFrame, FleetRemoved(fleetID = event.fleetID)) + + def AddItem (self, ID, name, count): + self.fleetItemContainer.AddWidget(FleetItem(self, ID, name, count)) + widget = self.fleetItemContainer.GetWidgetByFleetID(ID) + self.fleetItemContainer.RefreshList(True) + self.fleetItemContainer.ScrollChildIntoView(widget) + wx.PostEvent(self, FleetItemSelect(fleetID = ID)) + + def PopulateFleetList(self): + self.Freeze() + filter = self.filter + self.fleetItemContainer.RemoveAllChildren() + fleetList = self.sFleet.getFleetList() + for fleetID, fleetName, fleetCount in fleetList: + if fleetName.lower().find(filter.lower()) != -1: + self.fleetItemContainer.AddWidget(FleetItem(self, fleetID, fleetName, fleetCount)) + self.fleetItemContainer.RefreshList() + self.Thaw() + + def SetFilter(self, filter): + self.filter = filter + + def SizeRefreshList(self, event): + ewidth, eheight = event.GetSize() + self.Layout() + self.fleetItemContainer.Layout() + self.fleetItemContainer.RefreshList(True) + event.Skip() + + +class FleetBrowserHeader (wx.Panel): + def __init__(self, parent): + wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 24), style=wx.TAB_TRAVERSAL) + self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) + + self.newBmp = BitmapLoader.getBitmap("fit_add_small","gui") + bmpSize = (16,16) + + mainSizer = wx.BoxSizer(wx.HORIZONTAL) + + if 'wxMac' in wx.PlatformInfo: + bgcolour = wx.Colour(0, 0, 0, 0) + else: + bgcolour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) + + self.fbNewFleet = PFGenBitmapButton( self, wx.ID_ANY, self.newBmp, wx.DefaultPosition, bmpSize, wx.BORDER_NONE ) + mainSizer.Add(self.fbNewFleet, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL , 5) + self.fbNewFleet.SetBackgroundColour( bgcolour ) + + self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL ) + mainSizer.Add( self.sl1, 0, wx.EXPAND |wx.LEFT, 5 ) + + self.tcFilter = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + mainSizer.Add( self.tcFilter, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 ) + + self.stStatus = wx.StaticText( self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stStatus.Wrap( -1 ) + mainSizer.Add( self.stStatus, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 ) + + self.SetSizer(mainSizer) + self.Layout() + + self.fbNewFleet.Bind(wx.EVT_ENTER_WINDOW, self.fbNewEnterWindow) + self.fbNewFleet.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) + self.fbNewFleet.Bind(wx.EVT_BUTTON, self.OnNewFleetItem) + + self.tcFilter.Bind(wx.EVT_TEXT, self.OnFilterText) + + self.tcFilter.Bind(wx.EVT_ENTER_WINDOW, self.fbFilterEnterWindow) + self.tcFilter.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) + + def OnFilterText(self, event): + filter = self.tcFilter.GetValue() + self.Parent.SetFilter(filter) + self.Parent.PopulateFleetList() + event.Skip() + + def OnNewFleetItem(self, event): + wx.PostEvent(self.Parent, FleetItemNew(fleetName = "New Fleet")) + + def fbNewEnterWindow(self, event): + self.stStatus.SetLabel("New fleet") + self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) + event.Skip() + + def fbHItemLeaveWindow(self, event): + self.stStatus.SetLabel("") + self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + event.Skip() + + def fbFilterEnterWindow(self, event): + self.stStatus.SetLabel("Filter list") + event.Skip() + + + +class PFFleetItemContainer(PFListPane): + def __init__(self,parent): + PFListPane.__init__(self,parent) + self.selectedWidget = -1 + self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) + + def IsWidgetSelectedByContext(self, widget): + if self.GetWidgetList()[widget].IsSelected(): + return True + return False + + def GetWidgetIndex(self, widgetWnd): + return self.GetWidgetList().index(widgetWnd) + + def GetWidgetByFleetID(self, fleetID): + for widget in self.GetWidgetList(): + if widget.fleetID == fleetID: + return widget + return None + + def SelectWidget(self, widgetWnd): + wlist = self.GetWidgetList() + if self.selectedWidget != -1: + wlist[self.selectedWidget].SetSelected(False) + wlist[self.selectedWidget].Refresh() + windex = self.GetWidgetIndex(widgetWnd) + wlist[windex].SetSelected(True) + wlist[windex].Refresh() + self.selectedWidget = windex + + def SelectWidgetByFleetID(self, fleetID): + widgetWnd = self.GetWidgetByFleetID(fleetID) + if widgetWnd: + self.SelectWidget(widgetWnd) + + def RemoveWidget(self, child): + child.Destroy() + self.selectedWidget = -1 + self._wList.remove(child) + + + def RemoveAllChildren(self): + for widget in self._wList: + widget.Destroy() + + self.selectedWidget = -1 + self._wList = [] + + def OnLeftUp(self, event): + event.Skip() + +class FleetItem(SFItem.SFBrowserItem): + def __init__(self, parent, fleetID, fleetName, fleetCount, + id=wx.ID_ANY, pos=wx.DefaultPosition, + size=(0,40), style=0): + SFItem.SFBrowserItem.__init__(self, parent, size = size) + + self.fleetBrowser = self.Parent + self.fleetID = fleetID + self.fleetName = fleetName + self.fleetCount = fleetCount + + self.padding = 4 + + self.fontBig = wx.FontFromPixelSize((0,15),wx.SWISS, wx.NORMAL, wx.BOLD, False) + self.fontNormal = wx.FontFromPixelSize((0,14),wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.fontSmall = wx.FontFromPixelSize((0,12),wx.SWISS, wx.NORMAL, wx.NORMAL, False) + + self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") + self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") + self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small","gui") + self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") + self.fleetBmp = BitmapLoader.getBitmap("fleet_item_big", "gui") + + fleetImg = self.fleetBmp.ConvertToImage() + fleetImg = fleetImg.Blur(2) + + if not fleetImg.HasAlpha(): + fleetImg.InitAlpha() + + fleetImg = fleetImg.AdjustChannels(1, 1, 1, 0.5) + self.fleetEffBmp = wx.BitmapFromImage(fleetImg) + + self.toolbar.AddButton(self.copyBmp, "Copy", self.CopyFleetCB) + self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.RenameFleetCB) + self.toolbar.AddButton(self.deleteBmp, "Delete", self.DeleteFleetCB) + + self.editWidth = 150 + self.tcFleetName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fleetName, wx.DefaultPosition, (self.editWidth,-1), wx.TE_PROCESS_ENTER) + + if self.fleetBrowser.fleetIDMustEditName != self.fleetID: + self.tcFleetName.Show(False) + else: + self.tcFleetName.SetFocus() + self.tcFleetName.SelectAll() + self.fleetBrowser.fleetIDMustEditName = -1 + self.renameBtn.SetBitmap(self.acceptBmp) + self.selected = True + + self.tcFleetName.Bind(wx.EVT_KILL_FOCUS, self.OnEditLostFocus) + self.tcFleetName.Bind(wx.EVT_TEXT_ENTER, self.RenameFleet) + self.tcFleetName.Bind(wx.EVT_KEY_DOWN, self.EditCheckEsc) + + + self.animCount = 0 + + def MouseLeftUp(self, event): + if self.tcFleetName.IsShown(): + self.RestoreEditButton() + else: + wx.PostEvent(self.fleetBrowser, FleetItemSelect(fleetID = self.fleetID)) + + def CopyFleetCB(self): + if self.tcFleetName.IsShown(): + self.RestoreEditButton() + return + + wx.PostEvent(self.fleetBrowser, FleetItemCopy(fleetID = self.fleetID)) + + def RenameFleetCB(self): + + if self.tcFleetName.IsShown(): + + self.RenameFleet(None) + self.RestoreEditButton() + + else: + self.tcFleetName.SetValue(self.fleetName) + self.tcFleetName.Show() + + self.renameBtn.SetBitmap(self.acceptBmp) + self.Refresh() + + self.tcFleetName.SetFocus() + self.tcFleetName.SelectAll() + + self.Refresh() + + def RenameFleet(self, event): + + newFleetName = self.tcFleetName.GetValue() + self.fleetName = newFleetName + + self.tcFleetName.Show(False) + + wx.PostEvent(self.fleetBrowser, FleetItemRename(fleetID = self.fleetID, fleetName = self.fleetName)) + self.Refresh() + + def DeleteFleetCB(self): + if self.tcFleetName.IsShown(): + self.RestoreEditButton() + return + wx.PostEvent(self.fleetBrowser, FleetItemDelete(fleetID = self.fleetID)) + + def RestoreEditButton(self): + self.tcFleetName.Show(False) + self.renameBtn.SetBitmap(self.renameBmp) + self.Refresh() + + def OnEditLostFocus(self, event): + self.RestoreEditButton() + self.Refresh() + + def EditCheckEsc(self, event): + if event.GetKeyCode() == wx.WXK_ESCAPE: + self.RestoreEditButton() + else: + event.Skip() + + def IsSelected(self): + return self.selected + + def UpdateElementsPos(self, mdc): + rect = self.GetRect() + + self.toolbarx = rect.width - self.toolbar.GetWidth() - self.padding + self.toolbary = (rect.height - self.toolbar.GetHeight()) / 2 + + self.toolbarx = self.toolbarx + self.animCount + + self.fleetBmpx = self.padding + (rect.height - self.fleetBmp.GetWidth()) / 2 + self.fleetBmpy = (rect.height - self.fleetBmp.GetHeight()) / 2 + + self.fleetBmpx -= self.animCount + + self.textStartx = self.fleetBmpx + self.fleetBmp.GetWidth() + self.padding + + self.fleetNamey = (rect.height - self.fleetBmp.GetHeight()) / 2 + + mdc.SetFont(self.fontBig) + wtext, htext = mdc.GetTextExtent(self.fleetName) + + self.fleetCounty = self.fleetNamey + htext + + mdc.SetFont(self.fontSmall) + + wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) + + self.thoverx = self.toolbarx - self.padding - wlabel + self.thovery = (rect.height - hlabel)/2 + self.thoverw = wlabel + + def DrawItem(self, mdc): + rect = self.GetRect() + + windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + textColor = colorUtils.GetSuitableColor(windowColor, 1) + + mdc.SetTextForeground(textColor) + + self.UpdateElementsPos(mdc) + + self.toolbar.SetPosition((self.toolbarx, self.toolbary)) + mdc.DrawBitmap(self.fleetEffBmp, self.fleetBmpx + 3, self.fleetBmpy + 2) + mdc.DrawBitmap(self.fleetBmp, self.fleetBmpx,self.fleetBmpy) + + mdc.SetFont(self.fontNormal) + + suffix = "%d ships" % self.fleetCount if self.fleetCount >1 else "%d ship" % self.fleetCount if self.fleetCount == 1 else "No ships" + fleetCount = "Fleet size: %s" % suffix + fleetCount = drawUtils.GetPartialText(mdc, fleetCount, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + + mdc.DrawText(fleetCount, self.textStartx, self.fleetCounty) + + mdc.SetFont(self.fontSmall) + mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) + + mdc.SetFont(self.fontBig) + + pfname = drawUtils.GetPartialText(mdc, self.fleetName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) + mdc.DrawText(pfname, self.textStartx, self.fleetNamey) + + if self.tcFleetName.IsShown(): + self.AdjustControlSizePos(self.tcFleetName, self.textStartx, self.toolbarx - self.editWidth - self.padding) + + def AdjustControlSizePos(self, editCtl, start, end): + fnEditSize = editCtl.GetSize() + wSize = self.GetSize() + fnEditPosX = end + fnEditPosY = (wSize.height - fnEditSize.height)/2 + if fnEditPosX < start: + editCtl.SetSize((self.editWidth + fnEditPosX - start,-1)) + editCtl.SetPosition((start,fnEditPosY)) + else: + editCtl.SetSize((self.editWidth,-1)) + editCtl.SetPosition((fnEditPosX,fnEditPosY)) + + +class PFGenBitmapButton(GenBitmapButton): + def __init__(self, parent, id, bitmap, pos, size, style): + GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style) + self.bgcolor = wx.Brush(wx.WHITE) + + def SetBackgroundColour(self, color): + self.bgcolor = wx.Brush(color) + + def GetBackgroundBrush(self, dc): + return self.bgcolor diff --git a/gui/gangView.py b/gui/gangView.py index d5f8e43cb..106f14f98 100644 --- a/gui/gangView.py +++ b/gui/gangView.py @@ -16,5 +16,397 @@ # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . #=============================================================================== +import wx +from wx.lib.scrolledpanel import ScrolledPanel -#Purging fleet boosts \ No newline at end of file +import service +import gui.mainFrame +import gui.shipBrowser +import gui.globalEvents as GE + +from gui import characterEditor as CharEditor + +class GangView ( ScrolledPanel ): + + def __init__( self, parent ): + ScrolledPanel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 100,20 ), style = wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL ) + mainSizer = wx.BoxSizer( wx.VERTICAL ) + + self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + self.draggedFitID = None + + help = '''Set fit as booster to display in dropdown, or drag fitting from\nship browser to this window, or right click fit and select booster role.''' + helpSizer = wx.BoxSizer( wx.HORIZONTAL ) + self.helpText = wx.StaticText( self, wx.ID_ANY, help, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) + helpSizer.Add( self.helpText, 1, wx.ALL, 5 ) + + self.options = ["Fleet", "Wing", "Squad"] + + self.fleet = {} + for id, option in enumerate(self.options): + + # set content for each commander + self.fleet[id] = {} + self.fleet[id]['stLabel'] = wx.StaticText( self, wx.ID_ANY, self.options[id]+':', wx.DefaultPosition, wx.DefaultSize, 0 ) + self.fleet[id]['stText'] = wx.StaticText( self, wx.ID_ANY, 'None', wx.DefaultPosition, wx.DefaultSize, 0 ) + self.fleet[id]['chFit'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] ) + self.fleet[id]['chChar'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] ) + self.fleet[id]['fitSizer'] = wx.BoxSizer( wx.VERTICAL ) + + self.FitDNDPopupMenu = self.buildBoostermenu() + + contentFGSizer = wx.FlexGridSizer( 5, 3, 0, 0 ) + contentFGSizer.AddGrowableCol( 1 ) + contentFGSizer.SetFlexibleDirection( wx.BOTH ) + contentFGSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + + ### Header + self.stBooster = wx.StaticText( self, wx.ID_ANY, u"Booster", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stBooster.Wrap( -1 ) + self.stBooster.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) + contentFGSizer.Add( self.stBooster, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 5 ) + + self.stFits = wx.StaticText( self, wx.ID_ANY, u"Fits", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stFits.Wrap( -1 ) + self.stFits.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) + contentFGSizer.Add( self.stFits, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) + + self.stCharacters = wx.StaticText( self, wx.ID_ANY, u"Characters", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.stCharacters.Wrap( -1 ) + self.stCharacters.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) + contentFGSizer.Add( self.stCharacters, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) + + self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + contentFGSizer.Add( self.m_staticline2, 0, wx.EXPAND, 5 ) + + self.m_staticline3 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + contentFGSizer.Add( self.m_staticline3, 0, wx.EXPAND, 5 ) + + self.m_staticline4 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + contentFGSizer.Add( self.m_staticline4, 0, wx.EXPAND, 5 ) + + ### Content + for id in self.fleet: + # set various properties + self.fleet[id]['stLabel'].Wrap( -1 ) + self.fleet[id]['stLabel'].SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) + self.fleet[id]['stText'].Wrap( -1 ) + + # bind text and choice events + self.fleet[id]['stText'].Bind(wx.EVT_LEFT_DCLICK, self.RemoveBooster) + self.fleet[id]['stText'].Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow) + self.fleet[id]['stText'].Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) + self.fleet[id]['stText'].SetToolTip(wx.ToolTip("Double click to remove booster")) + self.fleet[id]['chChar'].Bind(wx.EVT_CHOICE, self.CharChanged) + self.fleet[id]['chFit'].Bind(wx.EVT_CHOICE, self.OnFitChoiceSelected) + + # add fit text and choice to the fit sizer + self.fleet[id]['fitSizer'].Add( self.fleet[id]['stText'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + self.fleet[id]['fitSizer'].Add( self.fleet[id]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 1 ) + + # add everything to the content sizer + contentFGSizer.Add( self.fleet[id]['stLabel'], 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + contentFGSizer.Add( self.fleet[id]['fitSizer'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 ) + contentFGSizer.Add( self.fleet[id]['chChar'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + + mainSizer.Add( contentFGSizer, 1, wx.EXPAND, 0 ) + mainSizer.Add( helpSizer, 0, wx.EXPAND, 0 ) + + self.SetSizer( mainSizer ) + self.SetAutoLayout(True) + self.SetupScrolling() + + self.mainFrame.Bind(GE.CHAR_LIST_UPDATED, self.RefreshCharacterList) + self.mainFrame.Bind(GE.FIT_CHANGED, self.fitSelected) + self.mainFrame.Bind(gui.shipBrowser.EVT_FIT_RENAMED, self.fitRenamed) + self.mainFrame.Bind(gui.shipBrowser.BOOSTER_LIST_UPDATED, self.RefreshBoosterFits) + + self.RefreshBoosterFits() + self.RefreshCharacterList() + + def buildBoostermenu(self): + menu = wx.Menu() + + for id, option in enumerate(self.options): + item = menu.Append(-1, option) + # We bind it to the mainFrame because it may be called from either this class or from FitItem via shipBrowser + self.mainFrame.Bind(wx.EVT_MENU, self.OnPopupItemSelected, item) + return menu + + def OnEnterWindow(self, event): + obj = event.GetEventObject() + obj.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) + event.Skip() + + def OnLeaveWindow(self, event): + obj = event.GetEventObject() + obj.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) + event.Skip() + + def CharChanged(self, event): + ''' Change booster character ''' + chBooster = event.GetEventObject() + + type = -1 + for id in self.fleet: + if chBooster == self.fleet[id]['chChar']: type = id + + if type == -1: + event.Skip() + return + + sFit = service.Fit.getInstance() + + fleetSrv = service.Fleet.getInstance() + + activeFitID = self.mainFrame.getActiveFit() + fit = sFit.getFit(activeFitID) + + sChar = service.Character.getInstance() + charList = sChar.getCharacterList() + + if activeFitID: + commanders = fleetSrv.loadLinearFleet(fit) + if commanders is None: + fleetCom, wingCom, squadCom = (None, None, None) + else: + fleetCom, wingCom, squadCom = commanders + + if type == 0: + if fleetCom: + charID = chBooster.GetClientData(chBooster.GetSelection()) + sFit.changeChar(fleetCom.ID, charID) + else: + chBooster.SetSelection(0) + + if type == 1: + if wingCom: + charID = chBooster.GetClientData(chBooster.GetSelection()) + sFit.changeChar(wingCom.ID, charID) + else: + chBooster.SetSelection(0) + + if type == 2: + if squadCom: + charID = chBooster.GetClientData(chBooster.GetSelection()) + sFit.changeChar(squadCom.ID, charID) + else: + chBooster.SetSelection(0) + + sFit.recalc(fit, withBoosters=True) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) + + else: + chBooster.SetSelection(0) + + def RemoveBooster(self, event): + activeFitID = self.mainFrame.getActiveFit() + if not activeFitID: + return + + location = event.GetEventObject() + + for id in self.fleet: + if location == self.fleet[id]['stText']: type = id + + sFit = service.Fit.getInstance() + boostee = sFit.getFit(activeFitID) + booster = None + + fleetSrv = service.Fleet.getInstance() + + if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) + if type == 1: fleetSrv.setLinearWingCom(boostee, booster) + if type == 2: fleetSrv.setLinearSquadCom(boostee, booster) + + # Hide stText and, default fit selection, and enable it + location.Hide() + choice = self.fleet[type]['chFit'] + choice.SetSelection(0) + choice.Show() + + sFit.recalc(boostee, withBoosters=True) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) + + def fitRenamed(self, event): + fleetSrv = service.Fleet.getInstance() + activeFitID = self.mainFrame.getActiveFit() + + if activeFitID: + ev = event + ev.fitID = activeFitID + self.fitSelected(ev) + + def fitSelected(self, event): + ''' Fires when active fit is selected and when booster is saved to fit. Update the UI to reflect changes ''' + fleetSrv = service.Fleet.getInstance() + + activeFitID = self.mainFrame.getActiveFit() + sFit = service.Fit.getInstance() + fit = sFit.getFit(event.fitID or activeFitID) + + self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) + + commanders = (None, None, None) + + if activeFitID: + commanders = fleetSrv.loadLinearFleet(fit) + + for id in self.fleet: + # try...except here as we're trying 2 different criteria and want to fall back on the same code + try: + commander = commanders[id] + + if not activeFitID or commander is None: + raise Exception() + + self.fleet[id]['stText'].SetLabel(commander.ship.item.name + ": " + commander.name) + self.fleet[id]['chChar'].SetStringSelection(commander.character.name if commander.character is not None else "All 0") + self.fleet[id]['chChar'].Enable() + self.fleet[id]['chFit'].Hide() + self.fleet[id]['stText'].Show() + except: + #set defaults, disable char selection, and enable fit selection + self.fleet[id]['stText'].SetLabel("None") + self.fleet[id]['chChar'].SetStringSelection("All 0") + self.fleet[id]['chChar'].Disable() + self.fleet[id]['chFit'].SetSelection(0) + self.fleet[id]['chFit'].Show() + self.fleet[id]['stText'].Hide() + + if activeFitID: + self.Enable() + else: + self.Disable() + + self.Layout() + self.SendSizeEvent() + + def AddCommander(self, fitID, type = None): + ''' Adds booster to a fit, then recalculates active fit ''' + if type is None: + return + + activeFitID = self.mainFrame.getActiveFit() + if activeFitID: + sFit = service.Fit.getInstance() + + boostee = sFit.getFit(activeFitID) + booster = sFit.getFit(fitID) + + fleetSrv = service.Fleet.getInstance() + + if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) + if type == 1: fleetSrv.setLinearWingCom(boostee, booster) + if type == 2: fleetSrv.setLinearSquadCom(boostee, booster) + + sFit.recalc(boostee) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) + + def RefreshBoosterFits(self, event = None): + sFit = service.Fit.getInstance() + sMkt = service.Market.getInstance() + fitList = sFit.getBoosterFits() + + for id in self.fleet: + choice = self.fleet[id]['chFit'] + chCurrSelection = choice.GetSelection() + chCurrData = -1 + if chCurrSelection != -1: + chCurrData = choice.GetClientData(chCurrSelection) + chCurrSelString = choice.GetString(chCurrSelection) + choice.Clear() + currSelFound = False + choice.Append("None", -1) + for fit in fitList: + id,name,type = fit + ship = sMkt.getItem(type) + choice.Append(ship.name+': '+name, id) + if chCurrData == id: + currSelFound = True + + if chCurrSelection == -1: + choice.SetSelection(0) + else: + if currSelFound: + choice.SetStringSelection(chCurrSelString) + else: + choice.SetSelection(0) + + def RefreshCharacterList(self, event = None): + sChar = service.Character.getInstance() + charList = sChar.getCharacterList() + for id in self.fleet: + choice = self.fleet[id]['chChar'] + chCurrSelection = choice.GetSelection() + chCurrData = -1 + if chCurrSelection != -1: + chCurrData = choice.GetClientData(chCurrSelection) + chCurrSelString = choice.GetString(chCurrSelection) + choice.Clear() + currSelFound = False + for char in charList: + choice.Append(char.name, char.ID) + if chCurrData == char.ID: + currSelFound = True + + if chCurrSelection == -1: + choice.SetSelection(1) + else: + if currSelFound: + choice.SetStringSelection(chCurrSelString) + else: + choice.SetSelection(1) + + def handleDrag(self, type, fitID): + ''' Handle dragging of fit to fleet interface ''' + #Those are drags coming from pyfa sources, NOT builtin wx drags + self.draggedFitID = None + if type == "fit": + sFit = service.Fit.getInstance() + fit = sFit.getFit(self.mainFrame.getActiveFit()) + + if fit and not fit.isStructure: + self.draggedFitID = fitID + + pos = wx.GetMousePosition() + pos = self.ScreenToClient(pos) + + self.PopupMenu(self.FitDNDPopupMenu, pos) + + + def OnPopupItemSelected(self, event): + ''' Fired when booster popup item is selected ''' + # Get menu selection ID via self.options + menuItem = event.EventObject.FindItemById(event.GetId()) + type = self.options.index(menuItem.GetText()) + + if self.draggedFitID: + sFit = service.Fit.getInstance() + draggedFit = sFit.getFit(self.draggedFitID) + + self.AddCommander(draggedFit.ID, type) + self.mainFrame.additionsPane.select("Fleet") + + def OnFitChoiceSelected(self, event): + ''' Fired when booster choice is selected ''' + sFit = service.Fit.getInstance() + + # set type via choice box used + chFit = event.GetEventObject() + fitID = chFit.GetClientData(chFit.GetSelection()) + + type = -1 + for id in self.fleet: + if chFit == self.fleet[id]['chFit']: type = id + + if type == -1 or fitID == -1: + event.Skip() + return + + fit = sFit.getFit(fitID) + + self.AddCommander(fit.ID, type) + self.mainFrame.additionsPane.select("Fleet") diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 220a8f0ab..c209a0422 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -55,6 +55,7 @@ from gui.preferenceDialog import PreferenceDialog from gui.graphFrame import GraphFrame from gui.copySelectDialog import CopySelectDialog from gui.utils.clipboard import toClipboard, fromClipboard +from gui.fleetBrowser import FleetBrowser from gui.updateDialog import UpdateDialog from gui.builtinViews import * @@ -157,6 +158,12 @@ class MainFrame(wx.Frame): self.shipBrowser = ShipBrowser(self.notebookBrowsers) self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage = shipBrowserImg, showClose = False) + #======================================================================= + # DISABLED FOR RC2 RELEASE + #self.fleetBrowser = FleetBrowser(self.notebookBrowsers) + #self.notebookBrowsers.AddPage(self.fleetBrowser, "Fleets", showClose = False) + #======================================================================= + self.notebookBrowsers.SetSelection(1) self.browser_fitting_split.SplitVertically(self.notebookBrowsers, self.fitting_additions_split) diff --git a/service/__init__.py b/service/__init__.py index 91ed07d82..b19c6db17 100644 --- a/service/__init__.py +++ b/service/__init__.py @@ -5,6 +5,7 @@ from service.character import Character from service.damagePattern import DamagePattern from service.targetResists import TargetResists from service.settings import SettingsProvider +from service.fleet import Fleet from service.update import Update from service.price import Price from service.network import Network diff --git a/service/fit.py b/service/fit.py index d05865514..455e76463 100644 --- a/service/fit.py +++ b/service/fit.py @@ -34,6 +34,7 @@ from eos.types import State, Slot from service.market import Market from service.damagePattern import DamagePattern from service.character import Character +from service.fleet import Fleet from service.settings import SettingsProvider from service.port import Port @@ -183,6 +184,8 @@ class Fit(object): def deleteFit(self, fitID): fit = eos.db.getFit(fitID) + sFleet = Fleet.getInstance() + sFleet.removeAssociatedFleetData(fit) eos.db.remove(fit) @@ -233,7 +236,7 @@ class Fit(object): self.recalc(fit, withBoosters=True) def getFit(self, fitID, projected=False, basic=False): - ''' Gets fit from database. + ''' Gets fit from database, and populates fleet data. Projected is a recursion flag that is set to reduce recursions into projected fits Basic is a flag to simply return the fit without any other processing @@ -248,6 +251,14 @@ class Fit(object): inited = getattr(fit, "inited", None) if inited is None or inited is False: + sFleet = Fleet.getInstance() + f = sFleet.getLinearFleet(fit) + if f is None: + sFleet.removeAssociatedFleetData(fit) + fit.fleet = None + else: + fit.fleet = f + if not projected: for fitP in fit.projectedFits: self.getFit(fitP.ID, projected=True) diff --git a/service/fleet.py b/service/fleet.py index 3a158cb2d..cfad7f04b 100644 --- a/service/fleet.py +++ b/service/fleet.py @@ -17,4 +17,199 @@ # along with pyfa. If not, see . #=============================================================================== -#Purge fleet boosts +import eos.db +from eos.types import Fleet as Fleet_, Wing, Squad +import copy + +class Fleet(object): + instance = None + @classmethod + def getInstance(cls): + if cls.instance is None: + cls.instance = Fleet() + + return cls.instance + + def __init__(self): + pass + + def getFleetList(self): + fleetList = [] + fleets = eos.db.getFleetList() + for fleet in fleets: + fleetList.append((fleet.ID, fleet.name, fleet.count())) + + return fleetList + + def getFleetByID(self, ID): + f = eos.db.getFleet(ID) + return f + + def addFleet(self): + f = Fleet_() + eos.db.save(f) + return f + + def renameFleet(self, fleet, newName): + fleet.name = newName + eos.db.commit() + + def copyFleet(self, fleet): + newFleet = copy.deepcopy(fleet) + eos.db.save(newFleet) + return newFleet + + def copyFleetByID(self, ID): + fleet = self.getFleetByID(ID) + return self.copyFleet(fleet) + + def deleteFleet(self, fleet): + eos.db.remove(fleet) + + def deleteFleetByID(self, ID): + fleet = self.getFleetByID(ID) + self.deleteFleet(fleet) + + def makeLinearFleet(self, fit): + f = Fleet_() + w = Wing() + f.wings.append(w) + s = Squad() + w.squads.append(s) + s.members.append(fit) + fit.fleet = f + eos.db.save(f) + + def setLinearFleetCom(self, boostee, booster): + #if boostee == booster: + # return + if self.getLinearFleet(boostee) is None: + self.removeAssociatedFleetData(boostee) + self.makeLinearFleet(boostee) + squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) + squad = eos.db.getSquad(squadIDs.pop()) + if squad.wing.gang.leader is not None and booster is None: + try: + squad.wing.gang.leader.boostsFits.remove(boostee.ID) + except KeyError: + pass + squad.wing.gang.leader = booster + if self.anyBoosters(squad) is False: + self.removeAssociatedFleetData(boostee) + from service.fit import Fit + sFit = Fit.getInstance() + sFit.recalc(boostee, withBoosters=True) + + def setLinearWingCom(self, boostee, booster): + #if boostee == booster: + # return + if self.getLinearFleet(boostee) is None: + self.removeAssociatedFleetData(boostee) + self.makeLinearFleet(boostee) + squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) + squad = eos.db.getSquad(squadIDs.pop()) + if squad.wing.leader is not None and booster is None: + try: + squad.wing.leader.boostsFits.remove(boostee.ID) + except KeyError: + pass + squad.wing.leader = booster + if self.anyBoosters(squad) is False: + self.removeAssociatedFleetData(boostee) + from service.fit import Fit + sFit = Fit.getInstance() + sFit.recalc(boostee, withBoosters=True) + + def setLinearSquadCom(self, boostee, booster): + #if boostee == booster: + # return + if self.getLinearFleet(boostee) is None: + self.removeAssociatedFleetData(boostee) + self.makeLinearFleet(boostee) + squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) + squad = eos.db.getSquad(squadIDs.pop()) + if squad.leader is not None and booster is None: + try: + squad.leader.boostsFits.remove(boostee.ID) + except KeyError: + pass + squad.leader = booster + if self.anyBoosters(squad) is False: + self.removeAssociatedFleetData(boostee) + from service.fit import Fit + sFit = Fit.getInstance() + sFit.recalc(boostee, withBoosters=True) + + + def getLinearFleet(self, fit): + sqIDs = eos.db.getSquadsIDsWithFitID(fit.ID) + if len(sqIDs) != 1: + return None + s = eos.db.getSquad(sqIDs[0]) + if len(s.members) != 1: + return None + w = s.wing + if len(w.squads) != 1: + return None + f = w.gang + if len(f.wings) != 1: + return None + return f + + def removeAssociatedFleetData(self, fit): + squadIDs = set(eos.db.getSquadsIDsWithFitID(fit.ID)) + if len(squadIDs) == 0: + return + squads = list(eos.db.getSquad(sqID) for sqID in squadIDs) + wingIDs = set(squad.wing.ID for squad in squads) + fleetIDs = set(squad.wing.gang.ID for squad in squads) + for fleetID in fleetIDs: + fleet = eos.db.getFleet(fleetID) + for wing in fleet.wings: + wingIDs.add(wing.ID) + for wingID in wingIDs: + wing = eos.db.getWing(wingID) + for squad in wing.squads: + squadIDs.add(squad.ID) + for squadID in squadIDs: + squad = eos.db.getSquad(squadID) + if squad.leader is not None: + try: + squad.leader.boostsFits.remove(fit.ID) + except KeyError: + pass + eos.db.remove(squad) + for wingID in wingIDs: + wing = eos.db.getWing(wingID) + if wing.leader is not None: + try: + wing.leader.boostsFits.remove(fit.ID) + except KeyError: + pass + eos.db.remove(wing) + for fleetID in fleetIDs: + fleet = eos.db.getFleet(fleetID) + if fleet.leader is not None: + try: + fleet.leader.boostsFits.remove(fit.ID) + except KeyError: + pass + eos.db.remove(fleet) + fit.fleet = None + return + + def anyBoosters(self, squad): + wing = squad.wing + fleet = wing.gang + if squad.leader is None and wing.leader is None and fleet.leader is None: + return False + return True + + def loadLinearFleet(self, fit): + if self.getLinearFleet(fit) is None: + return None + squadID = eos.db.getSquadsIDsWithFitID(fit.ID)[0] + s = eos.db.getSquad(squadID) + w = s.wing + f = w.gang + return (f.leader, w.leader, s.leader) From c30190f63e29f315cd26251ecd40931d46a6de57 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Thu, 15 Sep 2016 15:27:30 -0700 Subject: [PATCH 03/26] Revert "Revert "Purging fleet bonuses from code base"" This reverts commit 6e54d6788cd4784c5d6b76c26a2ec243e4c87cad. --- eos/db/saveddata/fleet.py | 48 +--- eos/db/saveddata/queries.py | 58 ----- eos/types.py | 1 - gui/additionsPane.py | 7 +- gui/builtinViews/fleetView.py | 141 +---------- gui/fleetBrowser.py | 457 +--------------------------------- gui/gangView.py | 394 +---------------------------- gui/mainFrame.py | 7 - service/__init__.py | 1 - service/fit.py | 13 +- service/fleet.py | 197 +-------------- 11 files changed, 7 insertions(+), 1317 deletions(-) diff --git a/eos/db/saveddata/fleet.py b/eos/db/saveddata/fleet.py index 8f0e6736b..0583b43b0 100644 --- a/eos/db/saveddata/fleet.py +++ b/eos/db/saveddata/fleet.py @@ -17,50 +17,4 @@ # along with eos. If not, see . #=============================================================================== -from sqlalchemy import Table, Column, Integer, ForeignKey, String -from sqlalchemy.orm import mapper, relation - -from eos.db import saveddata_meta -from eos.types import Fleet, Wing, Squad, Fit -from eos.db.saveddata.fit import fits_table - -gangs_table = Table("gangs", saveddata_meta, - Column("ID", Integer, primary_key = True), - Column("leaderID", ForeignKey("fits.ID")), - Column("boosterID", ForeignKey("fits.ID")), - Column("name", String)) - -wings_table = Table("wings", saveddata_meta, - Column("ID", Integer, primary_key = True), - Column("gangID", ForeignKey("gangs.ID")), - Column("boosterID", ForeignKey("fits.ID")), - Column("leaderID", ForeignKey("fits.ID"))) - -squads_table = Table("squads", saveddata_meta, - Column("ID", Integer, primary_key = True), - Column("wingID", ForeignKey("wings.ID")), - Column("leaderID", ForeignKey("fits.ID")), - Column("boosterID", ForeignKey("fits.ID"))) - -squadmembers_table = Table("squadmembers", saveddata_meta, - Column("squadID", ForeignKey("squads.ID"), primary_key = True), - Column("memberID", ForeignKey("fits.ID"), primary_key = True)) - -mapper(Fleet, gangs_table, - properties = {"wings" : relation(Wing, backref="gang"), - "leader" : relation(Fit, primaryjoin = gangs_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin = gangs_table.c.boosterID == fits_table.c.ID)}) - -mapper(Wing, wings_table, - properties = {"squads" : relation(Squad, backref="wing"), - "leader" : relation(Fit, primaryjoin = wings_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin = wings_table.c.boosterID == fits_table.c.ID)}) - -mapper(Squad, squads_table, - properties = {"leader" : relation(Fit, primaryjoin = squads_table.c.leaderID == fits_table.c.ID), - "booster" : relation(Fit, primaryjoin = squads_table.c.boosterID == fits_table.c.ID), - "members" : relation(Fit, - primaryjoin = squads_table.c.ID == squadmembers_table.c.squadID, - secondaryjoin = squadmembers_table.c.memberID == fits_table.c.ID, - secondary = squadmembers_table)}) - +#Purging fleet \ No newline at end of file diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 5a6dd1bfc..cb3033cc1 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -21,7 +21,6 @@ from eos.db.util import processEager, processWhere from eos.db import saveddata_session, sd_lock from eos.types import * -from eos.db.saveddata.fleet import squadmembers_table from eos.db.saveddata.fit import projectedFits_table from sqlalchemy.sql import and_ import eos.config @@ -194,48 +193,6 @@ def getFit(lookfor, eager=None): return fit -@cachedQuery(Fleet, 1, "fleetID") -def getFleet(fleetID, eager=None): - if isinstance(fleetID, int): - if eager is None: - with sd_lock: - fleet = saveddata_session.query(Fleet).get(fleetID) - else: - eager = processEager(eager) - with sd_lock: - fleet = saveddata_session.query(Fleet).options(*eager).filter(Fleet.ID == fleetID).first() - else: - raise TypeError("Need integer as argument") - return fleet - -@cachedQuery(Wing, 1, "wingID") -def getWing(wingID, eager=None): - if isinstance(wingID, int): - if eager is None: - with sd_lock: - wing = saveddata_session.query(Wing).get(wingID) - else: - eager = processEager(eager) - with sd_lock: - wing = saveddata_session.query(Wing).options(*eager).filter(Wing.ID == wingID).first() - else: - raise TypeError("Need integer as argument") - return wing - -@cachedQuery(Squad, 1, "squadID") -def getSquad(squadID, eager=None): - if isinstance(squadID, int): - if eager is None: - with sd_lock: - squad = saveddata_session.query(Squad).get(squadID) - else: - eager = processEager(eager) - with sd_lock: - squad = saveddata_session.query(Squad).options(*eager).filter(Fleet.ID == squadID).first() - else: - raise TypeError("Need integer as argument") - return squad - def getFitsWithShip(shipID, ownerID=None, where=None, eager=None): """ Get all the fits using a certain ship. @@ -308,12 +265,6 @@ def getFitList(eager=None): return fits -def getFleetList(eager=None): - eager = processEager(eager) - with sd_lock: - fleets = saveddata_session.query(Fleet).options(*eager).all() - return fleets - @cachedQuery(Price, 1, "typeID") def getPrice(typeID): if isinstance(typeID, int): @@ -423,15 +374,6 @@ def searchFits(nameLike, where=None, eager=None): return fits -def getSquadsIDsWithFitID(fitID): - if isinstance(fitID, int): - with sd_lock: - squads = saveddata_session.query(squadmembers_table.c.squadID).filter(squadmembers_table.c.memberID == fitID).all() - squads = tuple(entry[0] for entry in squads) - return squads - else: - raise TypeError("Need integer as argument") - def getProjectedFits(fitID): if isinstance(fitID, int): with sd_lock: diff --git a/eos/types.py b/eos/types.py index 12e7eb281..f2550ad31 100644 --- a/eos/types.py +++ b/eos/types.py @@ -38,7 +38,6 @@ from eos.saveddata.booster import SideEffect from eos.saveddata.booster import Booster from eos.saveddata.fit import Fit, ImplantLocation from eos.saveddata.mode import Mode -from eos.saveddata.fleet import Fleet, Wing, Squad from eos.saveddata.miscData import MiscData from eos.saveddata.override import Override diff --git a/gui/additionsPane.py b/gui/additionsPane.py index 8c855dc95..f1ca44be4 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -26,7 +26,6 @@ from gui.cargoView import CargoView from gui.implantView import ImplantView from gui.projectedView import ProjectedView from gui.pyfatogglepanel import TogglePanel -from gui.gangView import GangView from gui.bitmapLoader import BitmapLoader import gui.chromeTabs @@ -55,7 +54,6 @@ class AdditionsPane(TogglePanel): implantImg = BitmapLoader.getImage("implant_small", "gui") boosterImg = BitmapLoader.getImage("booster_small", "gui") projectedImg = BitmapLoader.getImage("projected_small", "gui") - gangImg = BitmapLoader.getImage("fleet_fc_small", "gui") cargoImg = BitmapLoader.getImage("cargo_small", "gui") self.drone = DroneView(self.notebook) @@ -76,12 +74,9 @@ class AdditionsPane(TogglePanel): self.projectedPage = ProjectedView(self.notebook) self.notebook.AddPage(self.projectedPage, "Projected", tabImage = projectedImg, showClose = False) - self.gangPage = GangView(self.notebook) - self.notebook.AddPage(self.gangPage, "Fleet", tabImage = gangImg, showClose = False) - self.notebook.SetSelection(0) - PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected", "Fleet"] + PANES = ["Drones", "Fighters", "Cargo", "Implants", "Boosters", "Projected"] def select(self, name): self.notebook.SetSelection(self.PANES.index(name)) diff --git a/gui/builtinViews/fleetView.py b/gui/builtinViews/fleetView.py index bc6eef9a2..9058bc35f 100644 --- a/gui/builtinViews/fleetView.py +++ b/gui/builtinViews/fleetView.py @@ -1,140 +1 @@ -import wx.gizmos -import gui.fleetBrowser -import service -from gui.bitmapLoader import BitmapLoader - -#Tab spawning handler -class FleetSpawner(gui.multiSwitch.TabSpawner): - def __init__(self, multiSwitch): - self.multiSwitch = multiSwitch - mainFrame = gui.mainFrame.MainFrame.getInstance() - mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_SELECTED, self.fleetSelected) - - def fleetSelected(self, event): - if self.multiSwitch.GetPageCount() == 0: - self.multiSwitch.AddPage(wx.Panel(self.multiSwitch, size = (0,0)), "Empty Tab") - - view = FleetView(self.multiSwitch) - self.multiSwitch.ReplaceActivePage(view) - view.populate(event.fleetID) - view.Show() - -FleetSpawner.register() - -class FleetView(wx.gizmos.TreeListCtrl): - def __init__(self, parent, size = (0,0)): - wx.gizmos.TreeListCtrl.__init__(self, parent, size = size) - - self.tabManager = parent - - self.fleetId = None - self.fleetImg = BitmapLoader.getImage("53_16", "icons") - - self.imageList = wx.ImageList(16, 16) - self.SetImageList(self.imageList) - - for col in ("", "Fit", "Shiptype", "Character", "Bonusses"): - self.AddColumn(col) - - self.SetMainColumn(1) - self.icons = {} - self.addImage = self.imageList.Add(BitmapLoader.getBitmap("add_small", "gui")) - for icon in ("fb", "fc", "sb", "sc", "wb", "wc"): - self.icons[icon] = self.imageList.Add(BitmapLoader.getBitmap("fleet_%s_small" % icon, "gui")) - - self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.checkNew) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_RENAMED, self.fleetRenamed) - self.mainFrame.Bind(gui.fleetBrowser.EVT_FLEET_REMOVED, self.fleetRemoved) - - def Destroy(self): - self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_REMOVED, handler = self.fleetRemoved) - self.mainFrame.Unbind(gui.fleetBrowser.EVT_FLEET_RENAMED, handler = self.fleetRenamed) - wx.gizmos.TreeListCtrl.Destroy(self) - - def fleetRenamed(self, event): - if event.fleetID == self.fleetId: - sFleet = service.Fleet.getInstance() - f = sFleet.getFleetByID(event.fleetID) - self.UpdateTab(f.name, self.fleetImg) - - event.Skip() - - def fleetRemoved(self, event): - if event.fleetID == self.fleetId: - self.tabManager.DeletePage(self.tabManager.GetPageIndex(self)) - - event.Skip() - - def checkNew(self, event): - data = self.GetPyData(event.Item) - if data and isinstance(data, tuple) and data[0] == "add": - layer = data[1] - - - def UpdateTab(self, name, img): - self.tabManager.SetPageTextIcon(self.tabManager.GetSelection(), name, img) - - def populate(self, fleetID): - sFleet = service.Fleet.getInstance() - f = sFleet.getFleetByID(fleetID) - self.fleetId = fleetID - - self.UpdateTab( f.name, self.fleetImg) - self.fleet = f - self.DeleteAllItems() - root = self.AddRoot("") - - self.setEntry(root, f.leader, "fleet", f) - for wing in f.wings: - wingId = self.AppendItem(root, "") - self.setEntry(wingId, wing.leader, "wing", wing) - for squad in wing.squads: - for member in squad.members: - memberId = self.AppendItem(wingId, "") - self.setEntry(memberId, member, "squad", squad) - - self.addAdder(wingId, "squad") - - self.addAdder(root, "wing") - - self.ExpandAll(root) - self.SetColumnWidth(0, 16) - for i in xrange(1, 5): - self.SetColumnWidth(i, wx.LIST_AUTOSIZE_USEHEADER) - headerWidth = self.GetColumnWidth(i) + 5 - self.SetColumnWidth(i, wx.LIST_AUTOSIZE) - baseWidth = self.GetColumnWidth(i) - if baseWidth < headerWidth: - self.SetColumnWidth(i, headerWidth) - else: - self.SetColumnWidth(i, baseWidth) - - - def addAdder(self, treeItemId, layer): - id = self.AppendItem(treeItemId, "Add new %s" % layer.capitalize()) - self.SetPyData(id, ("add", layer)) - self.SetItemImage(id, self.addImage, 1) - - def setEntry(self, treeItemId, fit, layer, info): - self.SetPyData(treeItemId, info) - if fit is None: - self.SetItemText(treeItemId, "%s Commander" % layer.capitalize(), 1) - else: - fleet = self.fleet - if fit == info.booster: - self.SetItemImage(treeItemId, self.icons["%sb" % layer[0]], 0) - elif fit == info.leader: - self.SetItemImage(treeItemId, self.icons["%sc" % layer[0]], 1) - - self.SetItemText(treeItemId, fit.name, 1) - self.SetItemText(treeItemId, fit.ship.item.name, 2) - self.SetItemText(treeItemId, fit.character.name, 3) - boosts = fleet.store.getBoosts(fit) - if boosts: - bonusses = [] - for name, info in boosts.iteritems(): - bonusses.append("%s: %.2g" % (name, info[0])) - - self.SetItemText(treeItemId, ", ".join(bonusses), 3) +#Purge fleet boosts \ No newline at end of file diff --git a/gui/fleetBrowser.py b/gui/fleetBrowser.py index 7476d7d7f..bb28be251 100644 --- a/gui/fleetBrowser.py +++ b/gui/fleetBrowser.py @@ -1,456 +1 @@ -import wx -import copy -from gui.bitmapLoader import BitmapLoader -import gui.mainFrame -from gui.PFListPane import PFListPane -import service.fleet -from gui.utils.drawUtils import GetPartialText - -from wx.lib.buttons import GenBitmapButton - -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils - -import gui.sfBrowserItem as SFItem - -FleetSelected, EVT_FLEET_SELECTED = wx.lib.newevent.NewEvent() -FleetRenamed, EVT_FLEET_RENAMED = wx.lib.newevent.NewEvent() -FleetRemoved, EVT_FLEET_REMOVED = wx.lib.newevent.NewEvent() - - -FleetItemSelect, EVT_FLEET_ITEM_SELECT = wx.lib.newevent.NewEvent() -FleetItemDelete, EVT_FLEET_ITEM_DELETE = wx.lib.newevent.NewEvent() -FleetItemNew, EVT_FLEET_ITEM_NEW = wx.lib.newevent.NewEvent() -FleetItemCopy, EVT_FLEET_ITEM_COPY = wx.lib.newevent.NewEvent() -FleetItemRename, EVT_FLEET_ITEM_RENAME = wx.lib.newevent.NewEvent() - - - -class FleetBrowser(wx.Panel): - def __init__(self, parent): - wx.Panel.__init__(self, parent) - - self.sFleet = service.fleet.Fleet.getInstance() - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - mainSizer = wx.BoxSizer(wx.VERTICAL) - - self.hpane = FleetBrowserHeader(self) - mainSizer.Add(self.hpane, 0, wx.EXPAND) - - self.m_sl2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - mainSizer.Add( self.m_sl2, 0, wx.EXPAND, 0 ) - - self.fleetItemContainer = PFFleetItemContainer(self) - - mainSizer.Add(self.fleetItemContainer, 1, wx.EXPAND) - - self.SetSizer(mainSizer) - self.Layout() - - self.filter = "" - self.fleetIDMustEditName = -1 - - self.Bind(wx.EVT_SIZE, self.SizeRefreshList) - - self.Bind(EVT_FLEET_ITEM_NEW, self.AddNewFleetItem) - self.Bind(EVT_FLEET_ITEM_SELECT, self.SelectFleetItem) - self.Bind(EVT_FLEET_ITEM_DELETE, self.DeleteFleetItem) - self.Bind(EVT_FLEET_ITEM_COPY, self.CopyFleetItem) - self.Bind(EVT_FLEET_ITEM_RENAME, self.RenameFleetItem) - - self.PopulateFleetList() - - def AddNewFleetItem(self, event): - fleetName = event.fleetName - newFleet = self.sFleet.addFleet() - self.sFleet.renameFleet(newFleet, fleetName) - - self.fleetIDMustEditName = newFleet.ID - self.AddItem(newFleet.ID, newFleet.name, newFleet.count()) - - def SelectFleetItem(self, event): - fleetID = event.fleetID - self.fleetItemContainer.SelectWidgetByFleetID(fleetID) - wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleetID)) - - def CopyFleetItem(self, event): - fleetID = event.fleetID - fleet = self.sFleet.copyFleetByID(fleetID) - - fleetName = fleet.name + " Copy" - self.sFleet.renameFleet(fleet,fleetName) - - self.fleetIDMustEditName = fleet.ID - self.AddItem(fleet.ID, fleet.name, fleet.count()) - - self.fleetItemContainer.SelectWidgetByFleetID(fleet.ID) - wx.PostEvent(self.mainFrame, FleetSelected(fleetID=fleet.ID)) - - def RenameFleetItem(self, event): - fleetID = event.fleetID - fleet = self.sFleet.getFleetByID(fleetID) - - newFleetName = event.fleetName - - self.sFleet.renameFleet(fleet, newFleetName) - wx.PostEvent(self.mainFrame, FleetRenamed(fleetID = fleet.ID)) - - def DeleteFleetItem(self, event): - self.sFleet.deleteFleetByID(event.fleetID) - self.PopulateFleetList() - wx.PostEvent(self.mainFrame, FleetRemoved(fleetID = event.fleetID)) - - def AddItem (self, ID, name, count): - self.fleetItemContainer.AddWidget(FleetItem(self, ID, name, count)) - widget = self.fleetItemContainer.GetWidgetByFleetID(ID) - self.fleetItemContainer.RefreshList(True) - self.fleetItemContainer.ScrollChildIntoView(widget) - wx.PostEvent(self, FleetItemSelect(fleetID = ID)) - - def PopulateFleetList(self): - self.Freeze() - filter = self.filter - self.fleetItemContainer.RemoveAllChildren() - fleetList = self.sFleet.getFleetList() - for fleetID, fleetName, fleetCount in fleetList: - if fleetName.lower().find(filter.lower()) != -1: - self.fleetItemContainer.AddWidget(FleetItem(self, fleetID, fleetName, fleetCount)) - self.fleetItemContainer.RefreshList() - self.Thaw() - - def SetFilter(self, filter): - self.filter = filter - - def SizeRefreshList(self, event): - ewidth, eheight = event.GetSize() - self.Layout() - self.fleetItemContainer.Layout() - self.fleetItemContainer.RefreshList(True) - event.Skip() - - -class FleetBrowserHeader (wx.Panel): - def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 24), style=wx.TAB_TRAVERSAL) - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) - - self.newBmp = BitmapLoader.getBitmap("fit_add_small","gui") - bmpSize = (16,16) - - mainSizer = wx.BoxSizer(wx.HORIZONTAL) - - if 'wxMac' in wx.PlatformInfo: - bgcolour = wx.Colour(0, 0, 0, 0) - else: - bgcolour = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) - - self.fbNewFleet = PFGenBitmapButton( self, wx.ID_ANY, self.newBmp, wx.DefaultPosition, bmpSize, wx.BORDER_NONE ) - mainSizer.Add(self.fbNewFleet, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_VERTICAL , 5) - self.fbNewFleet.SetBackgroundColour( bgcolour ) - - self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_VERTICAL ) - mainSizer.Add( self.sl1, 0, wx.EXPAND |wx.LEFT, 5 ) - - self.tcFilter = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.tcFilter, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 ) - - self.stStatus = wx.StaticText( self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stStatus.Wrap( -1 ) - mainSizer.Add( self.stStatus, 1, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5 ) - - self.SetSizer(mainSizer) - self.Layout() - - self.fbNewFleet.Bind(wx.EVT_ENTER_WINDOW, self.fbNewEnterWindow) - self.fbNewFleet.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) - self.fbNewFleet.Bind(wx.EVT_BUTTON, self.OnNewFleetItem) - - self.tcFilter.Bind(wx.EVT_TEXT, self.OnFilterText) - - self.tcFilter.Bind(wx.EVT_ENTER_WINDOW, self.fbFilterEnterWindow) - self.tcFilter.Bind(wx.EVT_LEAVE_WINDOW, self.fbHItemLeaveWindow) - - def OnFilterText(self, event): - filter = self.tcFilter.GetValue() - self.Parent.SetFilter(filter) - self.Parent.PopulateFleetList() - event.Skip() - - def OnNewFleetItem(self, event): - wx.PostEvent(self.Parent, FleetItemNew(fleetName = "New Fleet")) - - def fbNewEnterWindow(self, event): - self.stStatus.SetLabel("New fleet") - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) - event.Skip() - - def fbHItemLeaveWindow(self, event): - self.stStatus.SetLabel("") - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) - event.Skip() - - def fbFilterEnterWindow(self, event): - self.stStatus.SetLabel("Filter list") - event.Skip() - - - -class PFFleetItemContainer(PFListPane): - def __init__(self,parent): - PFListPane.__init__(self,parent) - self.selectedWidget = -1 - self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) - - def IsWidgetSelectedByContext(self, widget): - if self.GetWidgetList()[widget].IsSelected(): - return True - return False - - def GetWidgetIndex(self, widgetWnd): - return self.GetWidgetList().index(widgetWnd) - - def GetWidgetByFleetID(self, fleetID): - for widget in self.GetWidgetList(): - if widget.fleetID == fleetID: - return widget - return None - - def SelectWidget(self, widgetWnd): - wlist = self.GetWidgetList() - if self.selectedWidget != -1: - wlist[self.selectedWidget].SetSelected(False) - wlist[self.selectedWidget].Refresh() - windex = self.GetWidgetIndex(widgetWnd) - wlist[windex].SetSelected(True) - wlist[windex].Refresh() - self.selectedWidget = windex - - def SelectWidgetByFleetID(self, fleetID): - widgetWnd = self.GetWidgetByFleetID(fleetID) - if widgetWnd: - self.SelectWidget(widgetWnd) - - def RemoveWidget(self, child): - child.Destroy() - self.selectedWidget = -1 - self._wList.remove(child) - - - def RemoveAllChildren(self): - for widget in self._wList: - widget.Destroy() - - self.selectedWidget = -1 - self._wList = [] - - def OnLeftUp(self, event): - event.Skip() - -class FleetItem(SFItem.SFBrowserItem): - def __init__(self, parent, fleetID, fleetName, fleetCount, - id=wx.ID_ANY, pos=wx.DefaultPosition, - size=(0,40), style=0): - SFItem.SFBrowserItem.__init__(self, parent, size = size) - - self.fleetBrowser = self.Parent - self.fleetID = fleetID - self.fleetName = fleetName - self.fleetCount = fleetCount - - self.padding = 4 - - self.fontBig = wx.FontFromPixelSize((0,15),wx.SWISS, wx.NORMAL, wx.BOLD, False) - self.fontNormal = wx.FontFromPixelSize((0,14),wx.SWISS, wx.NORMAL, wx.NORMAL, False) - self.fontSmall = wx.FontFromPixelSize((0,12),wx.SWISS, wx.NORMAL, wx.NORMAL, False) - - self.copyBmp = BitmapLoader.getBitmap("fit_add_small", "gui") - self.renameBmp = BitmapLoader.getBitmap("fit_rename_small", "gui") - self.deleteBmp = BitmapLoader.getBitmap("fit_delete_small","gui") - self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") - self.fleetBmp = BitmapLoader.getBitmap("fleet_item_big", "gui") - - fleetImg = self.fleetBmp.ConvertToImage() - fleetImg = fleetImg.Blur(2) - - if not fleetImg.HasAlpha(): - fleetImg.InitAlpha() - - fleetImg = fleetImg.AdjustChannels(1, 1, 1, 0.5) - self.fleetEffBmp = wx.BitmapFromImage(fleetImg) - - self.toolbar.AddButton(self.copyBmp, "Copy", self.CopyFleetCB) - self.renameBtn = self.toolbar.AddButton(self.renameBmp, "Rename", self.RenameFleetCB) - self.toolbar.AddButton(self.deleteBmp, "Delete", self.DeleteFleetCB) - - self.editWidth = 150 - self.tcFleetName = wx.TextCtrl(self, wx.ID_ANY, "%s" % self.fleetName, wx.DefaultPosition, (self.editWidth,-1), wx.TE_PROCESS_ENTER) - - if self.fleetBrowser.fleetIDMustEditName != self.fleetID: - self.tcFleetName.Show(False) - else: - self.tcFleetName.SetFocus() - self.tcFleetName.SelectAll() - self.fleetBrowser.fleetIDMustEditName = -1 - self.renameBtn.SetBitmap(self.acceptBmp) - self.selected = True - - self.tcFleetName.Bind(wx.EVT_KILL_FOCUS, self.OnEditLostFocus) - self.tcFleetName.Bind(wx.EVT_TEXT_ENTER, self.RenameFleet) - self.tcFleetName.Bind(wx.EVT_KEY_DOWN, self.EditCheckEsc) - - - self.animCount = 0 - - def MouseLeftUp(self, event): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - else: - wx.PostEvent(self.fleetBrowser, FleetItemSelect(fleetID = self.fleetID)) - - def CopyFleetCB(self): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - return - - wx.PostEvent(self.fleetBrowser, FleetItemCopy(fleetID = self.fleetID)) - - def RenameFleetCB(self): - - if self.tcFleetName.IsShown(): - - self.RenameFleet(None) - self.RestoreEditButton() - - else: - self.tcFleetName.SetValue(self.fleetName) - self.tcFleetName.Show() - - self.renameBtn.SetBitmap(self.acceptBmp) - self.Refresh() - - self.tcFleetName.SetFocus() - self.tcFleetName.SelectAll() - - self.Refresh() - - def RenameFleet(self, event): - - newFleetName = self.tcFleetName.GetValue() - self.fleetName = newFleetName - - self.tcFleetName.Show(False) - - wx.PostEvent(self.fleetBrowser, FleetItemRename(fleetID = self.fleetID, fleetName = self.fleetName)) - self.Refresh() - - def DeleteFleetCB(self): - if self.tcFleetName.IsShown(): - self.RestoreEditButton() - return - wx.PostEvent(self.fleetBrowser, FleetItemDelete(fleetID = self.fleetID)) - - def RestoreEditButton(self): - self.tcFleetName.Show(False) - self.renameBtn.SetBitmap(self.renameBmp) - self.Refresh() - - def OnEditLostFocus(self, event): - self.RestoreEditButton() - self.Refresh() - - def EditCheckEsc(self, event): - if event.GetKeyCode() == wx.WXK_ESCAPE: - self.RestoreEditButton() - else: - event.Skip() - - def IsSelected(self): - return self.selected - - def UpdateElementsPos(self, mdc): - rect = self.GetRect() - - self.toolbarx = rect.width - self.toolbar.GetWidth() - self.padding - self.toolbary = (rect.height - self.toolbar.GetHeight()) / 2 - - self.toolbarx = self.toolbarx + self.animCount - - self.fleetBmpx = self.padding + (rect.height - self.fleetBmp.GetWidth()) / 2 - self.fleetBmpy = (rect.height - self.fleetBmp.GetHeight()) / 2 - - self.fleetBmpx -= self.animCount - - self.textStartx = self.fleetBmpx + self.fleetBmp.GetWidth() + self.padding - - self.fleetNamey = (rect.height - self.fleetBmp.GetHeight()) / 2 - - mdc.SetFont(self.fontBig) - wtext, htext = mdc.GetTextExtent(self.fleetName) - - self.fleetCounty = self.fleetNamey + htext - - mdc.SetFont(self.fontSmall) - - wlabel,hlabel = mdc.GetTextExtent(self.toolbar.hoverLabel) - - self.thoverx = self.toolbarx - self.padding - wlabel - self.thovery = (rect.height - hlabel)/2 - self.thoverw = wlabel - - def DrawItem(self, mdc): - rect = self.GetRect() - - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) - - mdc.SetTextForeground(textColor) - - self.UpdateElementsPos(mdc) - - self.toolbar.SetPosition((self.toolbarx, self.toolbary)) - mdc.DrawBitmap(self.fleetEffBmp, self.fleetBmpx + 3, self.fleetBmpy + 2) - mdc.DrawBitmap(self.fleetBmp, self.fleetBmpx,self.fleetBmpy) - - mdc.SetFont(self.fontNormal) - - suffix = "%d ships" % self.fleetCount if self.fleetCount >1 else "%d ship" % self.fleetCount if self.fleetCount == 1 else "No ships" - fleetCount = "Fleet size: %s" % suffix - fleetCount = drawUtils.GetPartialText(mdc, fleetCount, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) - - mdc.DrawText(fleetCount, self.textStartx, self.fleetCounty) - - mdc.SetFont(self.fontSmall) - mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery) - - mdc.SetFont(self.fontBig) - - pfname = drawUtils.GetPartialText(mdc, self.fleetName, self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) - mdc.DrawText(pfname, self.textStartx, self.fleetNamey) - - if self.tcFleetName.IsShown(): - self.AdjustControlSizePos(self.tcFleetName, self.textStartx, self.toolbarx - self.editWidth - self.padding) - - def AdjustControlSizePos(self, editCtl, start, end): - fnEditSize = editCtl.GetSize() - wSize = self.GetSize() - fnEditPosX = end - fnEditPosY = (wSize.height - fnEditSize.height)/2 - if fnEditPosX < start: - editCtl.SetSize((self.editWidth + fnEditPosX - start,-1)) - editCtl.SetPosition((start,fnEditPosY)) - else: - editCtl.SetSize((self.editWidth,-1)) - editCtl.SetPosition((fnEditPosX,fnEditPosY)) - - -class PFGenBitmapButton(GenBitmapButton): - def __init__(self, parent, id, bitmap, pos, size, style): - GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style) - self.bgcolor = wx.Brush(wx.WHITE) - - def SetBackgroundColour(self, color): - self.bgcolor = wx.Brush(color) - - def GetBackgroundBrush(self, dc): - return self.bgcolor +# purging fleet \ No newline at end of file diff --git a/gui/gangView.py b/gui/gangView.py index 106f14f98..d5f8e43cb 100644 --- a/gui/gangView.py +++ b/gui/gangView.py @@ -16,397 +16,5 @@ # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . #=============================================================================== -import wx -from wx.lib.scrolledpanel import ScrolledPanel -import service -import gui.mainFrame -import gui.shipBrowser -import gui.globalEvents as GE - -from gui import characterEditor as CharEditor - -class GangView ( ScrolledPanel ): - - def __init__( self, parent ): - ScrolledPanel.__init__ ( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 100,20 ), style = wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL ) - mainSizer = wx.BoxSizer( wx.VERTICAL ) - - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - - self.draggedFitID = None - - help = '''Set fit as booster to display in dropdown, or drag fitting from\nship browser to this window, or right click fit and select booster role.''' - helpSizer = wx.BoxSizer( wx.HORIZONTAL ) - self.helpText = wx.StaticText( self, wx.ID_ANY, help, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE ) - helpSizer.Add( self.helpText, 1, wx.ALL, 5 ) - - self.options = ["Fleet", "Wing", "Squad"] - - self.fleet = {} - for id, option in enumerate(self.options): - - # set content for each commander - self.fleet[id] = {} - self.fleet[id]['stLabel'] = wx.StaticText( self, wx.ID_ANY, self.options[id]+':', wx.DefaultPosition, wx.DefaultSize, 0 ) - self.fleet[id]['stText'] = wx.StaticText( self, wx.ID_ANY, 'None', wx.DefaultPosition, wx.DefaultSize, 0 ) - self.fleet[id]['chFit'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] ) - self.fleet[id]['chChar'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] ) - self.fleet[id]['fitSizer'] = wx.BoxSizer( wx.VERTICAL ) - - self.FitDNDPopupMenu = self.buildBoostermenu() - - contentFGSizer = wx.FlexGridSizer( 5, 3, 0, 0 ) - contentFGSizer.AddGrowableCol( 1 ) - contentFGSizer.SetFlexibleDirection( wx.BOTH ) - contentFGSizer.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) - - ### Header - self.stBooster = wx.StaticText( self, wx.ID_ANY, u"Booster", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stBooster.Wrap( -1 ) - self.stBooster.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - contentFGSizer.Add( self.stBooster, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 5 ) - - self.stFits = wx.StaticText( self, wx.ID_ANY, u"Fits", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stFits.Wrap( -1 ) - self.stFits.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - contentFGSizer.Add( self.stFits, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) - - self.stCharacters = wx.StaticText( self, wx.ID_ANY, u"Characters", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stCharacters.Wrap( -1 ) - self.stCharacters.SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - contentFGSizer.Add( self.stCharacters, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) - - self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - contentFGSizer.Add( self.m_staticline2, 0, wx.EXPAND, 5 ) - - self.m_staticline3 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - contentFGSizer.Add( self.m_staticline3, 0, wx.EXPAND, 5 ) - - self.m_staticline4 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - contentFGSizer.Add( self.m_staticline4, 0, wx.EXPAND, 5 ) - - ### Content - for id in self.fleet: - # set various properties - self.fleet[id]['stLabel'].Wrap( -1 ) - self.fleet[id]['stLabel'].SetFont( wx.Font( wx.NORMAL_FONT.GetPointSize(), 70, 90, 92, False, wx.EmptyString ) ) - self.fleet[id]['stText'].Wrap( -1 ) - - # bind text and choice events - self.fleet[id]['stText'].Bind(wx.EVT_LEFT_DCLICK, self.RemoveBooster) - self.fleet[id]['stText'].Bind(wx.EVT_ENTER_WINDOW, self.OnEnterWindow) - self.fleet[id]['stText'].Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - self.fleet[id]['stText'].SetToolTip(wx.ToolTip("Double click to remove booster")) - self.fleet[id]['chChar'].Bind(wx.EVT_CHOICE, self.CharChanged) - self.fleet[id]['chFit'].Bind(wx.EVT_CHOICE, self.OnFitChoiceSelected) - - # add fit text and choice to the fit sizer - self.fleet[id]['fitSizer'].Add( self.fleet[id]['stText'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) - self.fleet[id]['fitSizer'].Add( self.fleet[id]['chFit'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 1 ) - - # add everything to the content sizer - contentFGSizer.Add( self.fleet[id]['stLabel'], 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) - contentFGSizer.Add( self.fleet[id]['fitSizer'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 ) - contentFGSizer.Add( self.fleet[id]['chChar'], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) - - mainSizer.Add( contentFGSizer, 1, wx.EXPAND, 0 ) - mainSizer.Add( helpSizer, 0, wx.EXPAND, 0 ) - - self.SetSizer( mainSizer ) - self.SetAutoLayout(True) - self.SetupScrolling() - - self.mainFrame.Bind(GE.CHAR_LIST_UPDATED, self.RefreshCharacterList) - self.mainFrame.Bind(GE.FIT_CHANGED, self.fitSelected) - self.mainFrame.Bind(gui.shipBrowser.EVT_FIT_RENAMED, self.fitRenamed) - self.mainFrame.Bind(gui.shipBrowser.BOOSTER_LIST_UPDATED, self.RefreshBoosterFits) - - self.RefreshBoosterFits() - self.RefreshCharacterList() - - def buildBoostermenu(self): - menu = wx.Menu() - - for id, option in enumerate(self.options): - item = menu.Append(-1, option) - # We bind it to the mainFrame because it may be called from either this class or from FitItem via shipBrowser - self.mainFrame.Bind(wx.EVT_MENU, self.OnPopupItemSelected, item) - return menu - - def OnEnterWindow(self, event): - obj = event.GetEventObject() - obj.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) - event.Skip() - - def OnLeaveWindow(self, event): - obj = event.GetEventObject() - obj.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) - event.Skip() - - def CharChanged(self, event): - ''' Change booster character ''' - chBooster = event.GetEventObject() - - type = -1 - for id in self.fleet: - if chBooster == self.fleet[id]['chChar']: type = id - - if type == -1: - event.Skip() - return - - sFit = service.Fit.getInstance() - - fleetSrv = service.Fleet.getInstance() - - activeFitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(activeFitID) - - sChar = service.Character.getInstance() - charList = sChar.getCharacterList() - - if activeFitID: - commanders = fleetSrv.loadLinearFleet(fit) - if commanders is None: - fleetCom, wingCom, squadCom = (None, None, None) - else: - fleetCom, wingCom, squadCom = commanders - - if type == 0: - if fleetCom: - charID = chBooster.GetClientData(chBooster.GetSelection()) - sFit.changeChar(fleetCom.ID, charID) - else: - chBooster.SetSelection(0) - - if type == 1: - if wingCom: - charID = chBooster.GetClientData(chBooster.GetSelection()) - sFit.changeChar(wingCom.ID, charID) - else: - chBooster.SetSelection(0) - - if type == 2: - if squadCom: - charID = chBooster.GetClientData(chBooster.GetSelection()) - sFit.changeChar(squadCom.ID, charID) - else: - chBooster.SetSelection(0) - - sFit.recalc(fit, withBoosters=True) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - - else: - chBooster.SetSelection(0) - - def RemoveBooster(self, event): - activeFitID = self.mainFrame.getActiveFit() - if not activeFitID: - return - - location = event.GetEventObject() - - for id in self.fleet: - if location == self.fleet[id]['stText']: type = id - - sFit = service.Fit.getInstance() - boostee = sFit.getFit(activeFitID) - booster = None - - fleetSrv = service.Fleet.getInstance() - - if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) - if type == 1: fleetSrv.setLinearWingCom(boostee, booster) - if type == 2: fleetSrv.setLinearSquadCom(boostee, booster) - - # Hide stText and, default fit selection, and enable it - location.Hide() - choice = self.fleet[type]['chFit'] - choice.SetSelection(0) - choice.Show() - - sFit.recalc(boostee, withBoosters=True) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - - def fitRenamed(self, event): - fleetSrv = service.Fleet.getInstance() - activeFitID = self.mainFrame.getActiveFit() - - if activeFitID: - ev = event - ev.fitID = activeFitID - self.fitSelected(ev) - - def fitSelected(self, event): - ''' Fires when active fit is selected and when booster is saved to fit. Update the UI to reflect changes ''' - fleetSrv = service.Fleet.getInstance() - - activeFitID = self.mainFrame.getActiveFit() - sFit = service.Fit.getInstance() - fit = sFit.getFit(event.fitID or activeFitID) - - self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) - - commanders = (None, None, None) - - if activeFitID: - commanders = fleetSrv.loadLinearFleet(fit) - - for id in self.fleet: - # try...except here as we're trying 2 different criteria and want to fall back on the same code - try: - commander = commanders[id] - - if not activeFitID or commander is None: - raise Exception() - - self.fleet[id]['stText'].SetLabel(commander.ship.item.name + ": " + commander.name) - self.fleet[id]['chChar'].SetStringSelection(commander.character.name if commander.character is not None else "All 0") - self.fleet[id]['chChar'].Enable() - self.fleet[id]['chFit'].Hide() - self.fleet[id]['stText'].Show() - except: - #set defaults, disable char selection, and enable fit selection - self.fleet[id]['stText'].SetLabel("None") - self.fleet[id]['chChar'].SetStringSelection("All 0") - self.fleet[id]['chChar'].Disable() - self.fleet[id]['chFit'].SetSelection(0) - self.fleet[id]['chFit'].Show() - self.fleet[id]['stText'].Hide() - - if activeFitID: - self.Enable() - else: - self.Disable() - - self.Layout() - self.SendSizeEvent() - - def AddCommander(self, fitID, type = None): - ''' Adds booster to a fit, then recalculates active fit ''' - if type is None: - return - - activeFitID = self.mainFrame.getActiveFit() - if activeFitID: - sFit = service.Fit.getInstance() - - boostee = sFit.getFit(activeFitID) - booster = sFit.getFit(fitID) - - fleetSrv = service.Fleet.getInstance() - - if type == 0: fleetSrv.setLinearFleetCom(boostee, booster) - if type == 1: fleetSrv.setLinearWingCom(boostee, booster) - if type == 2: fleetSrv.setLinearSquadCom(boostee, booster) - - sFit.recalc(boostee) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) - - def RefreshBoosterFits(self, event = None): - sFit = service.Fit.getInstance() - sMkt = service.Market.getInstance() - fitList = sFit.getBoosterFits() - - for id in self.fleet: - choice = self.fleet[id]['chFit'] - chCurrSelection = choice.GetSelection() - chCurrData = -1 - if chCurrSelection != -1: - chCurrData = choice.GetClientData(chCurrSelection) - chCurrSelString = choice.GetString(chCurrSelection) - choice.Clear() - currSelFound = False - choice.Append("None", -1) - for fit in fitList: - id,name,type = fit - ship = sMkt.getItem(type) - choice.Append(ship.name+': '+name, id) - if chCurrData == id: - currSelFound = True - - if chCurrSelection == -1: - choice.SetSelection(0) - else: - if currSelFound: - choice.SetStringSelection(chCurrSelString) - else: - choice.SetSelection(0) - - def RefreshCharacterList(self, event = None): - sChar = service.Character.getInstance() - charList = sChar.getCharacterList() - for id in self.fleet: - choice = self.fleet[id]['chChar'] - chCurrSelection = choice.GetSelection() - chCurrData = -1 - if chCurrSelection != -1: - chCurrData = choice.GetClientData(chCurrSelection) - chCurrSelString = choice.GetString(chCurrSelection) - choice.Clear() - currSelFound = False - for char in charList: - choice.Append(char.name, char.ID) - if chCurrData == char.ID: - currSelFound = True - - if chCurrSelection == -1: - choice.SetSelection(1) - else: - if currSelFound: - choice.SetStringSelection(chCurrSelString) - else: - choice.SetSelection(1) - - def handleDrag(self, type, fitID): - ''' Handle dragging of fit to fleet interface ''' - #Those are drags coming from pyfa sources, NOT builtin wx drags - self.draggedFitID = None - if type == "fit": - sFit = service.Fit.getInstance() - fit = sFit.getFit(self.mainFrame.getActiveFit()) - - if fit and not fit.isStructure: - self.draggedFitID = fitID - - pos = wx.GetMousePosition() - pos = self.ScreenToClient(pos) - - self.PopupMenu(self.FitDNDPopupMenu, pos) - - - def OnPopupItemSelected(self, event): - ''' Fired when booster popup item is selected ''' - # Get menu selection ID via self.options - menuItem = event.EventObject.FindItemById(event.GetId()) - type = self.options.index(menuItem.GetText()) - - if self.draggedFitID: - sFit = service.Fit.getInstance() - draggedFit = sFit.getFit(self.draggedFitID) - - self.AddCommander(draggedFit.ID, type) - self.mainFrame.additionsPane.select("Fleet") - - def OnFitChoiceSelected(self, event): - ''' Fired when booster choice is selected ''' - sFit = service.Fit.getInstance() - - # set type via choice box used - chFit = event.GetEventObject() - fitID = chFit.GetClientData(chFit.GetSelection()) - - type = -1 - for id in self.fleet: - if chFit == self.fleet[id]['chFit']: type = id - - if type == -1 or fitID == -1: - event.Skip() - return - - fit = sFit.getFit(fitID) - - self.AddCommander(fit.ID, type) - self.mainFrame.additionsPane.select("Fleet") +#Purging fleet boosts \ No newline at end of file diff --git a/gui/mainFrame.py b/gui/mainFrame.py index c209a0422..220a8f0ab 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -55,7 +55,6 @@ from gui.preferenceDialog import PreferenceDialog from gui.graphFrame import GraphFrame from gui.copySelectDialog import CopySelectDialog from gui.utils.clipboard import toClipboard, fromClipboard -from gui.fleetBrowser import FleetBrowser from gui.updateDialog import UpdateDialog from gui.builtinViews import * @@ -158,12 +157,6 @@ class MainFrame(wx.Frame): self.shipBrowser = ShipBrowser(self.notebookBrowsers) self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage = shipBrowserImg, showClose = False) - #======================================================================= - # DISABLED FOR RC2 RELEASE - #self.fleetBrowser = FleetBrowser(self.notebookBrowsers) - #self.notebookBrowsers.AddPage(self.fleetBrowser, "Fleets", showClose = False) - #======================================================================= - self.notebookBrowsers.SetSelection(1) self.browser_fitting_split.SplitVertically(self.notebookBrowsers, self.fitting_additions_split) diff --git a/service/__init__.py b/service/__init__.py index b19c6db17..91ed07d82 100644 --- a/service/__init__.py +++ b/service/__init__.py @@ -5,7 +5,6 @@ from service.character import Character from service.damagePattern import DamagePattern from service.targetResists import TargetResists from service.settings import SettingsProvider -from service.fleet import Fleet from service.update import Update from service.price import Price from service.network import Network diff --git a/service/fit.py b/service/fit.py index 455e76463..d05865514 100644 --- a/service/fit.py +++ b/service/fit.py @@ -34,7 +34,6 @@ from eos.types import State, Slot from service.market import Market from service.damagePattern import DamagePattern from service.character import Character -from service.fleet import Fleet from service.settings import SettingsProvider from service.port import Port @@ -184,8 +183,6 @@ class Fit(object): def deleteFit(self, fitID): fit = eos.db.getFit(fitID) - sFleet = Fleet.getInstance() - sFleet.removeAssociatedFleetData(fit) eos.db.remove(fit) @@ -236,7 +233,7 @@ class Fit(object): self.recalc(fit, withBoosters=True) def getFit(self, fitID, projected=False, basic=False): - ''' Gets fit from database, and populates fleet data. + ''' Gets fit from database. Projected is a recursion flag that is set to reduce recursions into projected fits Basic is a flag to simply return the fit without any other processing @@ -251,14 +248,6 @@ class Fit(object): inited = getattr(fit, "inited", None) if inited is None or inited is False: - sFleet = Fleet.getInstance() - f = sFleet.getLinearFleet(fit) - if f is None: - sFleet.removeAssociatedFleetData(fit) - fit.fleet = None - else: - fit.fleet = f - if not projected: for fitP in fit.projectedFits: self.getFit(fitP.ID, projected=True) diff --git a/service/fleet.py b/service/fleet.py index cfad7f04b..3a158cb2d 100644 --- a/service/fleet.py +++ b/service/fleet.py @@ -17,199 +17,4 @@ # along with pyfa. If not, see . #=============================================================================== -import eos.db -from eos.types import Fleet as Fleet_, Wing, Squad -import copy - -class Fleet(object): - instance = None - @classmethod - def getInstance(cls): - if cls.instance is None: - cls.instance = Fleet() - - return cls.instance - - def __init__(self): - pass - - def getFleetList(self): - fleetList = [] - fleets = eos.db.getFleetList() - for fleet in fleets: - fleetList.append((fleet.ID, fleet.name, fleet.count())) - - return fleetList - - def getFleetByID(self, ID): - f = eos.db.getFleet(ID) - return f - - def addFleet(self): - f = Fleet_() - eos.db.save(f) - return f - - def renameFleet(self, fleet, newName): - fleet.name = newName - eos.db.commit() - - def copyFleet(self, fleet): - newFleet = copy.deepcopy(fleet) - eos.db.save(newFleet) - return newFleet - - def copyFleetByID(self, ID): - fleet = self.getFleetByID(ID) - return self.copyFleet(fleet) - - def deleteFleet(self, fleet): - eos.db.remove(fleet) - - def deleteFleetByID(self, ID): - fleet = self.getFleetByID(ID) - self.deleteFleet(fleet) - - def makeLinearFleet(self, fit): - f = Fleet_() - w = Wing() - f.wings.append(w) - s = Squad() - w.squads.append(s) - s.members.append(fit) - fit.fleet = f - eos.db.save(f) - - def setLinearFleetCom(self, boostee, booster): - #if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.wing.gang.leader is not None and booster is None: - try: - squad.wing.gang.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.wing.gang.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - def setLinearWingCom(self, boostee, booster): - #if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.wing.leader is not None and booster is None: - try: - squad.wing.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.wing.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - def setLinearSquadCom(self, boostee, booster): - #if boostee == booster: - # return - if self.getLinearFleet(boostee) is None: - self.removeAssociatedFleetData(boostee) - self.makeLinearFleet(boostee) - squadIDs = set(eos.db.getSquadsIDsWithFitID(boostee.ID)) - squad = eos.db.getSquad(squadIDs.pop()) - if squad.leader is not None and booster is None: - try: - squad.leader.boostsFits.remove(boostee.ID) - except KeyError: - pass - squad.leader = booster - if self.anyBoosters(squad) is False: - self.removeAssociatedFleetData(boostee) - from service.fit import Fit - sFit = Fit.getInstance() - sFit.recalc(boostee, withBoosters=True) - - - def getLinearFleet(self, fit): - sqIDs = eos.db.getSquadsIDsWithFitID(fit.ID) - if len(sqIDs) != 1: - return None - s = eos.db.getSquad(sqIDs[0]) - if len(s.members) != 1: - return None - w = s.wing - if len(w.squads) != 1: - return None - f = w.gang - if len(f.wings) != 1: - return None - return f - - def removeAssociatedFleetData(self, fit): - squadIDs = set(eos.db.getSquadsIDsWithFitID(fit.ID)) - if len(squadIDs) == 0: - return - squads = list(eos.db.getSquad(sqID) for sqID in squadIDs) - wingIDs = set(squad.wing.ID for squad in squads) - fleetIDs = set(squad.wing.gang.ID for squad in squads) - for fleetID in fleetIDs: - fleet = eos.db.getFleet(fleetID) - for wing in fleet.wings: - wingIDs.add(wing.ID) - for wingID in wingIDs: - wing = eos.db.getWing(wingID) - for squad in wing.squads: - squadIDs.add(squad.ID) - for squadID in squadIDs: - squad = eos.db.getSquad(squadID) - if squad.leader is not None: - try: - squad.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(squad) - for wingID in wingIDs: - wing = eos.db.getWing(wingID) - if wing.leader is not None: - try: - wing.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(wing) - for fleetID in fleetIDs: - fleet = eos.db.getFleet(fleetID) - if fleet.leader is not None: - try: - fleet.leader.boostsFits.remove(fit.ID) - except KeyError: - pass - eos.db.remove(fleet) - fit.fleet = None - return - - def anyBoosters(self, squad): - wing = squad.wing - fleet = wing.gang - if squad.leader is None and wing.leader is None and fleet.leader is None: - return False - return True - - def loadLinearFleet(self, fit): - if self.getLinearFleet(fit) is None: - return None - squadID = eos.db.getSquadsIDsWithFitID(fit.ID)[0] - s = eos.db.getSquad(squadID) - w = s.wing - f = w.gang - return (f.leader, w.leader, s.leader) +#Purge fleet boosts From e5d65b97cee041b10c598590358762658a0f8658 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sun, 6 Nov 2016 16:33:28 -0800 Subject: [PATCH 04/26] Merged changes from Pyfa.org\Master to branch --- service/fit.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/service/fit.py b/service/fit.py index d05865514..1ef142266 100644 --- a/service/fit.py +++ b/service/fit.py @@ -105,7 +105,9 @@ class Fit(object): "showTooltip": True, "showMarketShortcuts": False, "enableGaugeAnimation": True, - "exportCharges": True} + "exportCharges": True, + "openFitInNew":False + } self.serviceFittingOptions = SettingsProvider.getInstance().getSettings( "pyfaServiceFittingOptions", serviceFittingDefaultOptions) @@ -233,8 +235,7 @@ class Fit(object): self.recalc(fit, withBoosters=True) def getFit(self, fitID, projected=False, basic=False): - ''' Gets fit from database. - + ''' Gets fit from database, and populates fleet data. Projected is a recursion flag that is set to reduce recursions into projected fits Basic is a flag to simply return the fit without any other processing ''' @@ -501,7 +502,6 @@ class Fit(object): """ Moves cargo to fitting window. Can either do a copy, move, or swap with current module If we try to copy/move into a spot with a non-empty module, we swap instead. - To avoid redundancy in converting Cargo item, this function does the sanity checks as opposed to the GUI View. This is different than how the normal .swapModules() does things, which is mostly a blind swap. @@ -567,7 +567,6 @@ class Fit(object): def cloneModule(self, fitID, src, dst): """ Clone a module from src to dst - This will overwrite dst! Checking for empty module must be done at a higher level """ @@ -914,6 +913,10 @@ class Fit(object): fits = map(lambda fitID: eos.db.getFit(fitID), fitIDs) return Port.exportXml(callback, *fits) + def exportMultiBuy(self, fitID): + fit = eos.db.getFit(fitID) + return Port.exportMultiBuy(fit) + def backupFits(self, path, callback): thread = FitBackupThread(path, callback) thread.start() @@ -927,7 +930,6 @@ class Fit(object): Imports fits from file(s). First processes all provided paths and stores assembled fits into a list. This allows us to call back to the GUI as fits are processed as well as when fits are being saved. - returns """ defcodepage = locale.getpreferredencoding() From b6c5183d400d45d1c778175a9127dbe7b5b15436 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sun, 6 Nov 2016 16:38:21 -0800 Subject: [PATCH 05/26] Removed actual fleet files. Cleaned up __init__.py that had references. --- eos/db/saveddata/__init__.py | 1 - eos/db/saveddata/fleet.py | 20 -------------------- gui/builtinViews/__init__.py | 2 +- gui/builtinViews/fleetView.py | 1 - gui/fleetBrowser.py | 1 - gui/gangView.py | 20 -------------------- service/fleet.py | 20 -------------------- 7 files changed, 1 insertion(+), 64 deletions(-) delete mode 100644 eos/db/saveddata/fleet.py delete mode 100644 gui/builtinViews/fleetView.py delete mode 100644 gui/fleetBrowser.py delete mode 100644 gui/gangView.py delete mode 100644 service/fleet.py diff --git a/eos/db/saveddata/__init__.py b/eos/db/saveddata/__init__.py index c97707f68..ad6b69ed7 100644 --- a/eos/db/saveddata/__init__.py +++ b/eos/db/saveddata/__init__.py @@ -8,7 +8,6 @@ __all__ = [ "booster", "drone", "implant", - "fleet", "damagePattern", "miscData", "targetResists", diff --git a/eos/db/saveddata/fleet.py b/eos/db/saveddata/fleet.py deleted file mode 100644 index 0583b43b0..000000000 --- a/eos/db/saveddata/fleet.py +++ /dev/null @@ -1,20 +0,0 @@ -#=============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -#=============================================================================== - -#Purging fleet \ No newline at end of file diff --git a/gui/builtinViews/__init__.py b/gui/builtinViews/__init__.py index 46a500f7b..d51fdbad6 100644 --- a/gui/builtinViews/__init__.py +++ b/gui/builtinViews/__init__.py @@ -1 +1 @@ -__all__ = ["fittingView", "fleetView", "implantEditor"] +__all__ = ["fittingView", "implantEditor"] diff --git a/gui/builtinViews/fleetView.py b/gui/builtinViews/fleetView.py deleted file mode 100644 index 9058bc35f..000000000 --- a/gui/builtinViews/fleetView.py +++ /dev/null @@ -1 +0,0 @@ -#Purge fleet boosts \ No newline at end of file diff --git a/gui/fleetBrowser.py b/gui/fleetBrowser.py deleted file mode 100644 index bb28be251..000000000 --- a/gui/fleetBrowser.py +++ /dev/null @@ -1 +0,0 @@ -# purging fleet \ No newline at end of file diff --git a/gui/gangView.py b/gui/gangView.py deleted file mode 100644 index d5f8e43cb..000000000 --- a/gui/gangView.py +++ /dev/null @@ -1,20 +0,0 @@ -#=============================================================================== -# 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 . -#=============================================================================== - -#Purging fleet boosts \ No newline at end of file diff --git a/service/fleet.py b/service/fleet.py deleted file mode 100644 index 3a158cb2d..000000000 --- a/service/fleet.py +++ /dev/null @@ -1,20 +0,0 @@ -#=============================================================================== -# 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 . -#=============================================================================== - -#Purge fleet boosts From ea3e5e273f10943af581623fbb76aebc605a6262 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 7 Nov 2016 10:27:51 -0800 Subject: [PATCH 06/26] Handle Lockbreaker, Void, and Focused Void Bombs --- eos/effects/usemissiles.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/eos/effects/usemissiles.py b/eos/effects/usemissiles.py index f21a7b778..91f608e91 100644 --- a/eos/effects/usemissiles.py +++ b/eos/effects/usemissiles.py @@ -4,7 +4,27 @@ # Modules from group: Missile Launcher Heavy (12 of 12) # Modules from group: Missile Launcher Rocket (15 of 15) # Modules named like: Launcher (151 of 151) -type = 'active' -def handler(fit, module, context): +type = 'active', "projected" + + +def handler(fit, src, context): # Set reload time to 10 seconds - module.reloadTime = 10000 + src.reloadTime = 10000 + + if "projected" in context: + if src.item.group.name == unicode("Missile Launcher Bomb"): + # Bomb Launcher Cooldown Timer + moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay") + + # Void and Focused Void Bombs + neutAmount = src.getModifiedChargeAttr("energyNeutralizerAmount") + + if moduleReactivationDelay and neutAmount: + fit.addDrain(src, moduleReactivationDelay, neutAmount, 0) + + # Lockbreaker Bombs + ecmStrengthBonus = src.getModifiedChargeAttr("scan{0}StrengthBonus".format(fit.scanType)) + + if ecmStrengthBonus: + strModifier = 1 - ecmStrengthBonus / fit.scanStrength + fit.ecmProjectedStr *= strModifier From ab32a3afb05c373f968a8ac89dd41ea52bc8a0e0 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 7 Nov 2016 12:09:20 -0800 Subject: [PATCH 07/26] Change to shorthand unicode --- eos/effects/usemissiles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/effects/usemissiles.py b/eos/effects/usemissiles.py index 91f608e91..ff551fadd 100644 --- a/eos/effects/usemissiles.py +++ b/eos/effects/usemissiles.py @@ -12,7 +12,7 @@ def handler(fit, src, context): src.reloadTime = 10000 if "projected" in context: - if src.item.group.name == unicode("Missile Launcher Bomb"): + if src.item.group.name == u'Missile Launcher Bomb': # Bomb Launcher Cooldown Timer moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay") From 7fe7056c12558c03e5f4785b06b1840a21bb80d5 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 7 Nov 2016 12:18:36 -0800 Subject: [PATCH 08/26] Drop unicode --- eos/effects/usemissiles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eos/effects/usemissiles.py b/eos/effects/usemissiles.py index ff551fadd..49ef3c5a0 100644 --- a/eos/effects/usemissiles.py +++ b/eos/effects/usemissiles.py @@ -12,7 +12,7 @@ def handler(fit, src, context): src.reloadTime = 10000 if "projected" in context: - if src.item.group.name == u'Missile Launcher Bomb': + if src.item.group.name == 'Missile Launcher Bomb': # Bomb Launcher Cooldown Timer moduleReactivationDelay = src.getModifiedItemAttr("moduleReactivationDelay") From cfa57a70dc17a8f80bff2e354498372fa42534cd Mon Sep 17 00:00:00 2001 From: Resin Neublem Date: Sun, 20 Nov 2016 16:26:09 -0500 Subject: [PATCH 09/26] Remembers user's meta filtering choices There is a lot of logic that overwrites the user's choices of meta filtering on every search. That's a little too clever. Especially if the user is looking for different variations of a certain meta. If a user wants to include the other meta buttons they can re-add them after filtering. closes: https://github.com/pyfa-org/Pyfa/issues/818 --- gui/marketBrowser.py | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index 14dea666c..7659c897f 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -63,6 +63,7 @@ class MarketBrowser(wx.Panel): self.metaButtons = [] for name in self.sMkt.META_MAP.keys(): btn = wx.ToggleButton(p, wx.ID_ANY, name.capitalize(), style=wx.BU_EXACTFIT) + btn.SetValue(True) setattr(self, name, btn) box.Add(btn, 1, wx.ALIGN_CENTER) btn.Bind(wx.EVT_TOGGLEBUTTON, self.toggleMetaButton) @@ -175,7 +176,6 @@ class MarketTree(wx.TreeCtrl): self.marketBrowser.searchMode = False sMkt = self.sMkt mg = sMkt.getMarketGroupByItem(item) - metaId = sMkt.getMetaGroupIdByItem(item) jumpList = [] while mg is not None: @@ -197,7 +197,7 @@ class MarketTree(wx.TreeCtrl): self.Expand(item) self.SelectItem(item) - self.marketBrowser.itemView.selectionMade(forcedMetaSelect=metaId) + self.marketBrowser.itemView.selectionMade() class ItemView(d.Display): DEFAULT_COLS = ["Base Icon", @@ -269,7 +269,7 @@ class ItemView(d.Display): self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].append(itemID) - def selectionMade(self, event=None, forcedMetaSelect=None): + def selectionMade(self, event=None): self.marketBrowser.searchMode = False # Grab the threeview selection and check if it's fine sel = self.marketView.GetSelection() @@ -299,7 +299,7 @@ class ItemView(d.Display): # Set toggle buttons / use search mode flag if recently used modules category is selected (in order to have all modules listed and not filtered) if seldata is not RECENTLY_USED_MODULES: - self.setToggles(forcedMetaSelect=forcedMetaSelect) + self.setToggles() else: self.marketBrowser.searchMode = True self.setToggles() @@ -319,7 +319,7 @@ class ItemView(d.Display): self.filteredStore = sMkt.filterItemsByMeta(self.unfilteredStore, selectedMetas) self.update(list(self.filteredStore)) - def setToggles(self, forcedMetaSelect=None): + def setToggles(self): metaIDs = set() sMkt = self.sMkt for item in self.unfilteredStore: @@ -329,23 +329,8 @@ class ItemView(d.Display): btnMetas = sMkt.META_MAP[btn.metaName] if len(metaIDs.intersection(btnMetas)) > 0: btn.Enable(True) - # Select all available buttons if we're searching - if self.marketBrowser.searchMode is True: - btn.SetValue(True) - # Select explicitly requested button - if forcedMetaSelect is not None: - btn.SetValue(True if forcedMetaSelect in btnMetas else False) else: btn.Enable(False) - btn.SetValue(False) - if btn.GetValue(): - anySelection = True - # If no buttons are pressed, press first active - if anySelection is False: - for btn in self.marketBrowser.metaButtons: - if btn.Enabled: - btn.SetValue(True) - break def scheduleSearch(self, event=None): search = self.marketBrowser.search.GetLineText(0) From 0d4f24ade9aec5e3d263bf4f78817b612467658b Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sat, 26 Nov 2016 22:57:53 -0800 Subject: [PATCH 10/26] Handle unicode, utf8, and windows-1252 --- config.py | 109 +++++++++++----- gui/bitmapLoader.py | 22 ++-- gui/graphFrame.py | 67 ++++++---- gui/itemStats.py | 284 +++++++++++++++++++++-------------------- service/pycrest/eve.py | 31 +++-- service/settings.py | 63 +++++---- 6 files changed, 333 insertions(+), 243 deletions(-) diff --git a/config.py b/config.py index 9e3dc3d36..a251be1cb 100644 --- a/config.py +++ b/config.py @@ -31,18 +31,20 @@ gameDB = None class StreamToLogger(object): - """ - Fake file-like stream object that redirects writes to a logger instance. - From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ - """ - def __init__(self, logger, log_level=logging.INFO): - self.logger = logger - self.log_level = log_level - self.linebuf = '' + """ + Fake file-like stream object that redirects writes to a logger instance. + From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ + """ + + def __init__(self, logger, log_level=logging.INFO): + self.logger = logger + self.log_level = log_level + self.linebuf = '' + + def write(self, buf): + for line in buf.rstrip().splitlines(): + self.logger.log(self.log_level, line.rstrip()) - def write(self, buf): - for line in buf.rstrip().splitlines(): - self.logger.log(self.log_level, line.rstrip()) def isFrozen(): if hasattr(sys, 'frozen'): @@ -50,16 +52,12 @@ def isFrozen(): else: return False -def getPyfaRoot(): - base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] - root = os.path.dirname(os.path.realpath(os.path.abspath(base))) - root = unicode(root, sys.getfilesystemencoding()) - return root def __createDirs(path): if not os.path.exists(path): os.makedirs(path) + def defPaths(customSavePath): global debug global pyfaPath @@ -77,32 +75,32 @@ def defPaths(customSavePath): # Python 2.X uses ANSI by default, so we need to convert the character encoding pyfaPath = getattr(configforced, "pyfaPath", pyfaPath) if pyfaPath is None: - pyfaPath = getPyfaRoot() + pyfaPath = getPyfaPath() # Where we store the saved fits etc, default is the current users home directory if saveInRoot is True: savePath = getattr(configforced, "savePath", None) if savePath is None: - savePath = os.path.join(pyfaPath, "saveddata") + savePath = getPyfaPath("saveddata") else: savePath = getattr(configforced, "savePath", None) if savePath is None: - if customSavePath is None: # customSavePath is not overriden - savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")), - sys.getfilesystemencoding()) + if customSavePath is None: # customSavePath is not overriden + savePath = getSavePath() else: savePath = customSavePath __createDirs(savePath) if isFrozen(): - os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem") - os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") + certName = "cacert.pem" + os.environ["REQUESTS_CA_BUNDLE"] = getPyfaPath(certName) + os.environ["SSL_CERT_FILE"] = getPyfaPath(certName) - format = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' - logging.basicConfig(format=format, level=logLevel) - handler = logging.handlers.RotatingFileHandler(os.path.join(savePath, "log.txt"), maxBytes=1000000, backupCount=3) - formatter = logging.Formatter(format) + loggingFormat = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' + logging.basicConfig(format=loggingFormat, level=logLevel) + handler = logging.handlers.RotatingFileHandler(getSavePath("log.txt"), maxBytes=1000000, backupCount=3) + formatter = logging.Formatter(loggingFormat) handler.setFormatter(formatter) logging.getLogger('').addHandler(handler) @@ -114,23 +112,68 @@ def defPaths(customSavePath): sys.stdout = sl # This interferes with cx_Freeze's own handling of exceptions. Find a way to fix this. - #stderr_logger = logging.getLogger('STDERR') - #sl = StreamToLogger(stderr_logger, logging.ERROR) - #sys.stderr = sl + # stderr_logger = logging.getLogger('STDERR') + # sl = StreamToLogger(stderr_logger, logging.ERROR) + # sys.stderr = sl # The database where we store all the fits etc - saveDB = os.path.join(savePath, "saveddata.db") + saveDB = getSavePath("saveddata.db") # The database where the static EVE data from the datadump is kept. # This is not the standard sqlite datadump but a modified version created by eos # maintenance script - gameDB = os.path.join(pyfaPath, "eve.db") + gameDB = getPyfaPath("eve.db") ## DON'T MODIFY ANYTHING BELOW ## import eos.config - #Caching modifiers, disable all gamedata caching, its unneeded. + # Caching modifiers, disable all gamedata caching, its unneeded. eos.config.gamedataCache = False # saveddata db location modifier, shouldn't ever need to touch this eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False" eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False" + + +def getPyfaPath(Append=None): + base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] + root = os.path.dirname(os.path.realpath(os.path.abspath(base))) + if type(root) == str: # leave unicode ones alone + try: + root = root.decode('utf8') + except UnicodeDecodeError: + root = root.decode('windows-1252') + + if not Append: + return root + + if type(root) == str: # leave unicode ones alone + try: + path = os.path.abspath(os.path.join(root, Append)).decode('utf8') + except UnicodeDecodeError: + path = os.path.abspath(os.path.join(root, Append)).decode('windows-1252') + else: + path = os.path.abspath(os.path.join(root, Append)) + + return path + + +def getSavePath(Append=None): + root = os.path.expanduser(os.path.join("~", ".pyfa")) + if type(root) == str: # leave unicode ones alone + try: + root = root.decode('utf8') + except UnicodeDecodeError: + root = root.decode('windows-1252') + + if not Append: + return root + + if type(root) == str: # leave unicode ones alone + try: + path = os.path.abspath(os.path.join(root, Append)).decode('utf8') + except UnicodeDecodeError: + path = os.path.abspath(os.path.join(root, Append)).decode('windows-1252') + else: + path = os.path.abspath(os.path.join(root, Append)) + + return path diff --git a/gui/bitmapLoader.py b/gui/bitmapLoader.py index 45026bedb..2b991a818 100644 --- a/gui/bitmapLoader.py +++ b/gui/bitmapLoader.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,23 +15,25 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# =============================================================================== -import os.path -import config -import wx -import zipfile import cStringIO +import os.path +import zipfile + +import wx + +import config try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict -class BitmapLoader(): +class BitmapLoader(): try: - archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') + archive = zipfile.ZipFile(config.getPyfaPath('imgs.zip'), 'r') except IOError: archive = None @@ -42,7 +44,7 @@ class BitmapLoader(): @classmethod def getStaticBitmap(cls, name, parent, location): static = wx.StaticBitmap(parent) - static.SetBitmap(cls.getBitmap(name,location)) + static.SetBitmap(cls.getBitmap(name, location)) return static @classmethod @@ -85,7 +87,7 @@ class BitmapLoader(): except KeyError: print "Missing icon file from zip: {0}".format(path) else: - path = os.path.join(config.pyfaPath, 'imgs', location, filename) + path = config.getPyfaPath('imgs\\' + location + "\\" + filename) if os.path.exists(path): return wx.Image(path) diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 2cedc84d1..4efac6b14 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,21 +15,23 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# =============================================================================== + +import os import wx -import os -from gui.bitmapLoader import BitmapLoader + import gui.display import gui.globalEvents as GE - -from gui.graph import Graph -import service import gui.mainFrame +import service +from gui.bitmapLoader import BitmapLoader +from gui.graph import Graph enabled = True mplImported = False + class GraphFrame(wx.Frame): def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): @@ -46,9 +48,20 @@ class GraphFrame(wx.Frame): try: cache_dir = mpl._get_cachedir() except: - cache_dir = unicode(os.path.expanduser(os.path.join("~", ".matplotlib"))) + cache_dir = os.path.expanduser(os.path.join("~", ".matplotlib")) + if type(cache_dir) == str: # leave unicode ones alone + try: + cache_dir = cache_dir.decode('utf8') + except UnicodeDecodeError: + cache_dir = cache_dir.decode('windows-1252') cache_file = os.path.join(cache_dir, 'fontList.cache') + if type(cache_file) == str: # leave unicode ones alone + try: + cache_file = cache_file.decode('utf8') + except UnicodeDecodeError: + cache_file = cache_file.decode('windows-1252') + if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): # remove matplotlib font cache, see #234 os.remove(cache_file) @@ -58,7 +71,7 @@ class GraphFrame(wx.Frame): from matplotlib.figure import Figure enabled = True if mpl.__version__[0] != "1": - print "pyfa: Found matplotlib version ",mpl.__version__, " - activating OVER9000 workarounds" + print "pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds" print "pyfa: Recommended minimum matplotlib version is 1.0.0" self.legendFix = True except: @@ -91,19 +104,20 @@ class GraphFrame(wx.Frame): self.figure = Figure(figsize=(4, 3)) - rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() - clr = [c/255. for c in rgbtuple] - self.figure.set_facecolor( clr ) - self.figure.set_edgecolor( clr ) + rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() + clr = [c / 255. for c in rgbtuple] + self.figure.set_facecolor(clr) + self.figure.set_edgecolor(clr) self.canvas = Canvas(self, -1, self.figure) - self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) + self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) self.subplot = self.figure.add_subplot(111) self.subplot.grid(True) self.mainSizer.Add(self.canvas, 1, wx.EXPAND) - self.mainSizer.Add(wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0 , wx.EXPAND) + self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + wx.EXPAND) self.gridPanel = wx.Panel(self) self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) @@ -122,8 +136,8 @@ class GraphFrame(wx.Frame): self.graphSelection.SetSelection(0) self.fields = {} self.select(0) - self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) - self.mainSizer.Add(self.sl1,0, wx.EXPAND) + self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + self.mainSizer.Add(self.sl1, 0, wx.EXPAND) self.mainSizer.Add(self.fitList, 0, wx.EXPAND) self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) @@ -160,18 +174,18 @@ class GraphFrame(wx.Frame): self.gridPanel.DestroyChildren() self.fields.clear() - #Setup textboxes + # Setup textboxes for field, defaultVal in view.getFields().iteritems(): textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) self.fields[field] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) - sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) + sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if defaultVal is not None: if not isinstance(defaultVal, basestring): defaultVal = ("%f" % defaultVal).rstrip("0") if defaultVal[-1:] == ".": - defaultVal = defaultVal + "0" + defaultVal += "0" textBox.ChangeValue(defaultVal) @@ -189,7 +203,8 @@ class GraphFrame(wx.Frame): else: label = field - imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) + imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, + wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) self.draw() @@ -204,7 +219,7 @@ class GraphFrame(wx.Frame): try: success, status = view.getPoints(fit, values) if not success: - #TODO: Add a pwetty statys bar to report errors with + # TODO: Add a pwetty statys bar to report errors with self.SetStatusText(status) return @@ -218,15 +233,15 @@ class GraphFrame(wx.Frame): return if self.legendFix and len(legend) > 0: - leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False) + leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) - elif not self.legendFix and len(legend) >0: - leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False, frameon = False) + elif not self.legendFix and len(legend) > 0: + leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) for t in leg.get_texts(): t.set_fontsize('small') @@ -269,10 +284,10 @@ class FitList(wx.Panel): fitToolTip = wx.ToolTip("Drag a fit into this list to graph it") self.fitList.SetToolTip(fitToolTip) + class FitDisplay(gui.display.Display): DEFAULT_COLS = ["Base Icon", "Base Name"] def __init__(self, parent): gui.display.Display.__init__(self, parent) - diff --git a/gui/itemStats.py b/gui/itemStats.py index 63e4ce67d..c0c270519 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,38 +15,41 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# =============================================================================== + +import csv +import re +import sys import wx -import re -import gui.mainFrame -from gui.bitmapLoader import BitmapLoader -import sys -import wx.lib.mixins.listctrl as listmix import wx.html -from eos.types import Fit, Ship, Citadel, Module, Skill, Booster, Implant, Drone, Mode, Fighter -from gui.utils.numberFormatter import formatAmount -import service +import wx.lib.mixins.listctrl as listmix + import config +import gui.mainFrame +import service +from eos.types import Fit, Ship, Citadel, Module, Skill, Booster, Implant, Drone, Mode, Fighter +from gui.bitmapLoader import BitmapLoader from gui.contextMenu import ContextMenu from gui.utils.numberFormatter import formatAmount -import csv try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict + class ItemStatsDialog(wx.Dialog): counter = 0 + def __init__( self, victim, fullContext=None, pos=wx.DefaultPosition, size=wx.DefaultSize, - maximized = False - ): + maximized=False + ): wx.Dialog.__init__( self, @@ -55,7 +58,7 @@ class ItemStatsDialog(wx.Dialog): title="Item stats", pos=pos, size=size, - style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER| wx.SYSTEM_MENU + style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU ) empty = getattr(victim, "isEmpty", False) @@ -77,26 +80,27 @@ class ItemStatsDialog(wx.Dialog): victim = None self.context = itmContext if item.icon is not None: - before,sep,after = item.icon.iconFile.rpartition("_") - iconFile = "%s%s%s" % (before,sep,"0%s" % after if len(after) < 2 else after) + before, sep, after = item.icon.iconFile.rpartition("_") + iconFile = "%s%s%s" % (before, sep, "0%s" % after if len(after) < 2 else after) itemImg = BitmapLoader.getBitmap(iconFile, "icons") if itemImg is not None: self.SetIcon(wx.IconFromBitmap(itemImg)) - self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, " (%d)"%item.ID if config.debug else "")) + self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, + " (%d)" % item.ID if config.debug else "")) self.SetMinSize((300, 200)) if "wxGTK" in wx.PlatformInfo: # GTK has huge tab widgets, give it a bit more room self.SetSize((580, 500)) else: self.SetSize((550, 500)) - #self.SetMaxSize((500, -1)) + # self.SetMaxSize((500, -1)) self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.container = ItemStatsContainer(self, victim, item, itmContext) self.mainSizer.Add(self.container, 1, wx.EXPAND) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + self.mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) self.SetSizer(self.mainSizer) @@ -112,13 +116,13 @@ class ItemStatsDialog(wx.Dialog): counter = ItemStatsDialog.counter dlgStep = 30 - if counter * dlgStep > ppos.x+psize.width-dlgsize.x or counter * dlgStep > ppos.y+psize.height-dlgsize.y: + if counter * dlgStep > ppos.x + psize.width - dlgsize.x or counter * dlgStep > ppos.y + psize.height - dlgsize.y: ItemStatsDialog.counter = 1 dlgx = ppos.x + counter * dlgStep dlgy = ppos.y + counter * dlgStep if pos == wx.DefaultPosition: - self.SetPosition((dlgx,dlgy)) + self.SetPosition((dlgx, dlgy)) else: self.SetPosition(pos) if maximized: @@ -138,26 +142,26 @@ class ItemStatsDialog(wx.Dialog): def closeEvent(self, event): - if self.dlgOrder==ItemStatsDialog.counter: + if self.dlgOrder == ItemStatsDialog.counter: ItemStatsDialog.counter -= 1 self.parentWnd.UnregisterStatsWindow(self) self.Destroy() + ########################################################################### ## Class ItemStatsContainer ########################################################################### -class ItemStatsContainer ( wx.Panel ): - - def __init__( self, parent, stuff, item, context = None): - wx.Panel.__init__ ( self, parent ) +class ItemStatsContainer(wx.Panel): + def __init__(self, parent, stuff, item, context=None): + wx.Panel.__init__(self, parent) sMkt = service.Market.getInstance() - mainSizer = wx.BoxSizer( wx.VERTICAL ) + mainSizer = wx.BoxSizer(wx.VERTICAL) - self.nbContainer = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 ) - mainSizer.Add( self.nbContainer, 1, wx.EXPAND |wx.ALL, 2 ) + self.nbContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.nbContainer, 1, wx.EXPAND | wx.ALL, 2) if item.traits is not None: self.traits = ItemTraits(self.nbContainer, stuff, item) @@ -188,7 +192,7 @@ class ItemStatsContainer ( wx.Panel ): self.SetSizer(mainSizer) self.Layout() - def __del__( self ): + def __del__(self): pass def mouseHit(self, event): @@ -196,54 +200,54 @@ class ItemStatsContainer ( wx.Panel ): if tab != -1: self.nbContainer.SetSelection(tab) + ########################################################################### ## Class AutoListCtrl ########################################################################### 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 AutoListCtrl ########################################################################### 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) + ########################################################################### ## Class ItemTraits ########################################################################### -class ItemTraits ( wx.Panel ): - +class ItemTraits(wx.Panel): def __init__(self, parent, stuff, item): - wx.Panel.__init__ (self, parent) + 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) + mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0) self.Layout() + ########################################################################### ## Class ItemDescription ########################################################################### -class ItemDescription ( wx.Panel ): - +class ItemDescription(wx.Panel): def __init__(self, parent, stuff, item): - wx.Panel.__init__ (self, parent) + wx.Panel.__init__(self, parent) mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainSizer) @@ -260,28 +264,30 @@ class ItemDescription ( wx.Panel ): desc = re.sub("<( *)font( *)color( *)=(.*?)>(?P.*?)<( *)/( *)font( *)>", "\g", desc) # Strip URLs desc = re.sub("<( *)a(.*?)>(?P.*?)<( *)/( *)a( *)>", "\g", desc) - desc = "" + desc + "" + desc = "" + desc + "" self.description.SetPage(desc) - mainSizer.Add(self.description, 1, wx.ALL|wx.EXPAND, 0) + mainSizer.Add(self.description, 1, wx.ALL | wx.EXPAND, 0) self.Layout() + ########################################################################### ## Class ItemParams ########################################################################### -class ItemParams (wx.Panel): - def __init__(self, parent, stuff, item, context = None): - wx.Panel.__init__ (self, parent) - mainSizer = wx.BoxSizer( wx.VERTICAL ) +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_HRULES | - #wx.LC_NO_HEADER | - 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 ) + style= # wx.LC_HRULES | + # wx.LC_NO_HEADER | + 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 @@ -290,29 +296,31 @@ class ItemParams (wx.Panel): 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.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.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.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) + 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 ) + 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) + mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT) self.PopulateList() - self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleViewMode) + self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode) self.exportStatsBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ExportItemStats) def _fetchValues(self): @@ -348,7 +356,7 @@ class ItemParams (wx.Panel): event.Skip() def ToggleViewMode(self, event): - self.toggleView *=-1 + self.toggleView *= -1 self.UpdateList() event.Skip() @@ -412,17 +420,17 @@ class ItemParams (wx.Panel): ) def PopulateList(self): - self.paramList.InsertColumn(0,"Attribute") - self.paramList.InsertColumn(1,"Current Value") + 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) + 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.SetColumnWidth(2, 90) self.paramList.setResizeColumn(0) self.imageList = wx.ImageList(16, 16) - self.paramList.SetImageList(self.imageList,wx.IMAGE_LIST_SMALL) + self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) names = list(self.attrValues.iterkeys()) names.sort() @@ -461,7 +469,6 @@ class ItemParams (wx.Panel): 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) @@ -485,11 +492,9 @@ class ItemParams (wx.Panel): 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.totalAttrsLabel.SetLabel("%d attributes. " % idCount) self.Layout() def TranslateValueUnit(self, value, unitName, unitDisplayName): @@ -505,15 +510,16 @@ class ItemParams (wx.Panel): attribute = service.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), + 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), + "Absolute Percent": (lambda: (value * 100), unitName), "Milliseconds": (lambda: value / 1000.0, unitName), "typeID": (itemIDCallback, ""), - "groupID": (groupIDCallback,""), + "groupID": (groupIDCallback, ""), "attributeID": (attributeIDCallback, "")} override = trans.get(unitDisplayName) @@ -527,9 +533,10 @@ class ItemParams (wx.Panel): fvalue = formatAmount(v, 3, 0, 0) else: fvalue = v - return "%s %s" % (fvalue , override[1]) + return "%s %s" % (fvalue, override[1]) else: - return "%s %s" % (formatAmount(value, 3, 0),unitName) + return "%s %s" % (formatAmount(value, 3, 0), unitName) + class ItemCompare(wx.Panel): def __init__(self, parent, stuff, item, items, context=None): @@ -546,7 +553,8 @@ class ItemCompare(wx.Panel): self.toggleView = 1 self.stuff = stuff self.item = item - self.items = sorted(items, key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else None) + 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 @@ -618,7 +626,7 @@ class ItemCompare(wx.Panel): def processPrices(self, prices): for i, price in enumerate(prices): - self.paramList.SetStringItem(i, len(self.attrs)+1, formatAmount(price.price, 3, 3, 9, currency=True)) + self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(price.price, 3, 3, 9, currency=True)) def PopulateList(self): self.paramList.InsertColumn(0, "Item") @@ -626,11 +634,11 @@ class ItemCompare(wx.Panel): 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(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) + self.paramList.InsertColumn(len(self.attrs) + 1, "Price") + self.paramList.SetColumnWidth(len(self.attrs) + 1, 60) sMkt = service.Market.getInstance() sMkt.getPrices([x.ID for x in self.items], self.processPrices) @@ -648,7 +656,7 @@ class ItemCompare(wx.Panel): else: valueUnit = formatAmount(value, 3, 0, 0) - self.paramList.SetStringItem(i, x+1, valueUnit) + self.paramList.SetStringItem(i, x + 1, valueUnit) self.paramList.RefreshRows() self.Layout() @@ -669,7 +677,7 @@ class ItemCompare(wx.Panel): 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), + 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), @@ -698,19 +706,18 @@ class ItemCompare(wx.Panel): ## Class ItemRequirements ########################################################################### -class ItemRequirements ( wx.Panel ): - +class ItemRequirements(wx.Panel): def __init__(self, parent, stuff, item): - wx.Panel.__init__ (self, parent, style = wx.TAB_TRAVERSAL) + 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 ) + # 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) + 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) + mainSizer.Add(self.reqTree, 1, wx.ALL | wx.EXPAND, 0) self.SetSizer(mainSizer) self.root = self.reqTree.AddRoot("WINRARZOR") @@ -720,17 +727,17 @@ class ItemRequirements ( wx.Panel ): self.reqTree.SetImageList(self.imageList) skillBookId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) - self.getFullSkillTree(item,self.root,skillBookId) + self.getFullSkillTree(item, self.root, skillBookId) self.reqTree.ExpandAll() self.Layout() - def getFullSkillTree(self,parentSkill,parent,sbIconId): + 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) + 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.getFullSkillTree(skill, child, sbIconId) self.skillIdHistory.append(skill.ID) @@ -738,7 +745,7 @@ class ItemRequirements ( wx.Panel ): ## Class ItemEffects ########################################################################### -class ItemEffects (wx.Panel): +class ItemEffects(wx.Panel): def __init__(self, parent, stuff, item): wx.Panel.__init__(self, parent) self.item = item @@ -761,17 +768,17 @@ class ItemEffects (wx.Panel): def PopulateList(self): - self.effectList.InsertColumn(0,"Name") - self.effectList.InsertColumn(1,"Active") + 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.InsertColumn(4, "ID") - #self.effectList.SetColumnWidth(0,385) + # self.effectList.SetColumnWidth(0,385) self.effectList.setResizeColumn(0) - self.effectList.SetColumnWidth(1,50) + self.effectList.SetColumnWidth(1, 50) self.effectList.SetColumnWidth(2, 80) if config.debug: self.effectList.SetColumnWidth(3, 65) @@ -840,7 +847,7 @@ class ItemEffects (wx.Panel): """ import os - file = os.path.join(config.pyfaPath, "eos", "effects", "%s.py"%event.GetText().lower()) + file = config.getPyfaPath("eos\\effects\\%s.py" % event.GetText().lower()) if not os.path.isfile(file): open(file, 'a').close() @@ -848,7 +855,7 @@ class ItemEffects (wx.Panel): if 'wxMSW' in wx.PlatformInfo: os.startfile(file) elif 'wxMac' in wx.PlatformInfo: - os.system("open "+file) + os.system("open " + file) else: import subprocess subprocess.call(["xdg-open", file]) @@ -862,13 +869,15 @@ class ItemEffects (wx.Panel): self.Thaw() event.Skip() + ########################################################################### ## Class ItemAffectedBy ########################################################################### -class ItemAffectedBy (wx.Panel): +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 @@ -884,33 +893,33 @@ class ItemAffectedBy (wx.Panel): 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.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 ) + 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 ) + 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.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.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) + 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.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) + 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) + mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT) self.SetSizer(mainSizer) self.PopulateTree() self.Layout() @@ -950,7 +959,7 @@ class ItemAffectedBy (wx.Panel): self.Thaw() - def ToggleExpand(self,event): + def ToggleExpand(self, event): self.expand *= -1 self.ExpandCollapseTree() @@ -1059,7 +1068,8 @@ class ItemAffectedBy (wx.Panel): else: item = afflictor.item - items[attrName].append((type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) + items[attrName].append( + (type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) # Make sure projected fits are on top rootOrder = container.keys() @@ -1135,7 +1145,6 @@ class ItemAffectedBy (wx.Panel): 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 @@ -1259,9 +1268,10 @@ class ItemAffectedBy (wx.Panel): else: penalized = "" - attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized, attrIcon)) + attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, + attrAmount, penalized, attrIcon)) - attrSorted = sorted(attributes, key = lambda attribName: attribName[0]) + attrSorted = sorted(attributes, key=lambda attribName: attribName[0]) for attr in attrSorted: attrName, displayName, attrModifier, attrAmount, penalized, attrIcon = attr diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index c0f33becd..8218f2bc5 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -1,14 +1,17 @@ -import os import base64 +import logging +import os +import re import time import zlib import requests +from requests.adapters import HTTPAdapter -from . import version +import config from compat import bytes_, text_ from errors import APIException -from requests.adapters import HTTPAdapter +from . import version try: from urllib.parse import urlparse, urlunparse, parse_qsl @@ -24,9 +27,7 @@ try: from urllib.parse import quote except ImportError: # pragma: no cover from urllib import quote -import logging -import re -import config + logger = logging.getLogger("pycrest.eve") cache_re = re.compile(r'max-age=([0-9]+)') @@ -51,7 +52,13 @@ class FileCache(APICache): os.mkdir(self.path, 0o700) def _getpath(self, key): - return os.path.join(self.path, str(hash(key)) + '.cache') + path = os.path.join(self.path, str(hash(key)) + '.cache') + if type(path) == str: # leave unicode ones alone + try: + path = path.decode('utf8') + except UnicodeDecodeError: + path = path.decode('windows-1252') + return path def put(self, key, value): with open(self._getpath(key), 'wb') as f: @@ -111,7 +118,7 @@ class APIConnection(object): }) session.headers.update(additional_headers) session.mount('https://public-crest.eveonline.com', - HTTPAdapter()) + HTTPAdapter()) self._session = session if cache: if isinstance(cache, APICache): @@ -249,19 +256,18 @@ class EVE(APIConnection): def temptoken_authorize(self, access_token=None, expires_in=0, refresh_token=None): self.set_auth_values({'access_token': access_token, - 'refresh_token': refresh_token, - 'expires_in': expires_in}) + 'refresh_token': refresh_token, + 'expires_in': expires_in}) class AuthedConnection(EVE): - def __call__(self): if not self._data: self._data = APIObject(self.get(self._endpoint), self) return self._data def whoami(self): - #if 'whoami' not in self._cache: + # if 'whoami' not in self._cache: # print "Setting this whoami cache" # self._cache['whoami'] = self.get("%s/verify" % self._oauth_endpoint) return self.get("%s/verify" % self._oauth_endpoint) @@ -281,6 +287,7 @@ class AuthedConnection(EVE): self.refr_authorize(self.refresh_token) return self._session.delete(resource, params=params) + class APIObject(object): def __init__(self, parent, connection): self._dict = {} diff --git a/service/settings.py b/service/settings.py index 89f871973..71dbc9977 100644 --- a/service/settings.py +++ b/service/settings.py @@ -1,4 +1,4 @@ -#=============================================================================== +# =============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,33 +15,41 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -#=============================================================================== +# =============================================================================== import cPickle import os.path -import config import urllib2 +import config + + class SettingsProvider(): - BASE_PATH = os.path.join(config.savePath, "settings") + BASE_PATH = config.getSavePath("settings") settings = {} _instance = None + @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = SettingsProvider() return cls._instance def __init__(self): if not os.path.exists(self.BASE_PATH): - os.mkdir(self.BASE_PATH); + os.mkdir(self.BASE_PATH) def getSettings(self, area, defaults=None): s = self.settings.get(area) if s is None: p = os.path.join(self.BASE_PATH, area) + if type(p) == str: # leave unicode ones alone + try: + p = p.decode('utf8') + except UnicodeDecodeError: + p = p.decode('windows-1252') if not os.path.exists(p): info = {} @@ -71,6 +79,7 @@ class SettingsProvider(): for settings in self.settings.itervalues(): settings.save() + class Settings(): def __init__(self, location, info): self.location = location @@ -115,13 +124,13 @@ class NetworkSettings(): _instance = None # constants for serviceNetworkDefaultSettings["mode"] parameter - PROXY_MODE_NONE = 0 # 0 - No proxy + PROXY_MODE_NONE = 0 # 0 - No proxy PROXY_MODE_AUTODETECT = 1 # 1 - Auto-detected proxy settings - PROXY_MODE_MANUAL = 2 # 2 - Manual proxy settings + PROXY_MODE_MANUAL = 2 # 2 - Manual proxy settings @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = NetworkSettings() return cls._instance @@ -184,12 +193,11 @@ class NetworkSettings(): def setAccess(self, access): self.serviceNetworkSettings["access"] = access - def autodetect(self): + @staticmethod + def autodetect(): proxy = None - proxAddr = proxPort = "" proxydict = urllib2.ProxyHandler().proxies - txt = "Auto-detected: " validPrefixes = ("http", "https") @@ -237,53 +245,57 @@ class NetworkSettings(): self.serviceNetworkSettings["password"] = password - """ Settings used by the HTML export feature. """ + + class HTMLExportSettings(): _instance = None @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = HTMLExportSettings() return cls._instance def __init__(self): - serviceHTMLExportDefaultSettings = {"enabled": False, "path": config.pyfaPath + os.sep + 'pyfaFits.html', "minimal": False } - self.serviceHTMLExportSettings = SettingsProvider.getInstance().getSettings("pyfaServiceHTMLExportSettings", serviceHTMLExportDefaultSettings) + serviceHTMLExportDefaultSettings = {"enabled": False, "path": config.pyfaPath + os.sep + 'pyfaFits.html', + "minimal": False} + self.serviceHTMLExportSettings = SettingsProvider.getInstance().getSettings("pyfaServiceHTMLExportSettings", + serviceHTMLExportDefaultSettings) def getEnabled(self): return self.serviceHTMLExportSettings["enabled"] def setEnabled(self, enabled): self.serviceHTMLExportSettings["enabled"] = enabled - - + def getMinimalEnabled(self): return self.serviceHTMLExportSettings["minimal"] def setMinimalEnabled(self, minimal): self.serviceHTMLExportSettings["minimal"] = minimal - def getPath(self): return self.serviceHTMLExportSettings["path"] def setPath(self, path): self.serviceHTMLExportSettings["path"] = path + """ Settings used by update notification """ + + class UpdateSettings(): _instance = None @classmethod def getInstance(cls): - if cls._instance == None: + if cls._instance is None: cls._instance = UpdateSettings() return cls._instance @@ -293,8 +305,9 @@ class UpdateSettings(): # Updates are completely suppressed via network settings # prerelease - If True, suppress prerelease notifications # version - Set to release tag that user does not want notifications for - serviceUpdateDefaultSettings = {"prerelease": True, 'version': None } - self.serviceUpdateSettings = SettingsProvider.getInstance().getSettings("pyfaServiceUpdateSettings", serviceUpdateDefaultSettings) + serviceUpdateDefaultSettings = {"prerelease": True, 'version': None} + self.serviceUpdateSettings = SettingsProvider.getInstance().getSettings("pyfaServiceUpdateSettings", + serviceUpdateDefaultSettings) def get(self, type): return self.serviceUpdateSettings[type] @@ -302,6 +315,7 @@ class UpdateSettings(): def set(self, type, value): self.serviceUpdateSettings[type] = value + class CRESTSettings(): _instance = None @@ -313,13 +327,13 @@ class CRESTSettings(): return cls._instance def __init__(self): - # mode # 0 - Implicit authentication # 1 - User-supplied client details serviceCRESTDefaultSettings = {"mode": 0, "server": 0, "clientID": "", "clientSecret": "", "timeout": 60} - self.serviceCRESTSettings = SettingsProvider.getInstance().getSettings("pyfaServiceCRESTSettings", serviceCRESTDefaultSettings) + self.serviceCRESTSettings = SettingsProvider.getInstance().getSettings("pyfaServiceCRESTSettings", + serviceCRESTDefaultSettings) def get(self, type): return self.serviceCRESTSettings[type] @@ -327,5 +341,4 @@ class CRESTSettings(): def set(self, type, value): self.serviceCRESTSettings[type] = value - # @todo: migrate fit settings (from fit service) here? From d3e360b7c0347319bfc98371d7143efb8f025d89 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sun, 11 Dec 2016 23:03:09 -0500 Subject: [PATCH 11/26] clean up path stuff --- config.py | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/config.py b/config.py index 01342ae91..060c50fea 100644 --- a/config.py +++ b/config.py @@ -134,31 +134,16 @@ def defPaths(customSavePath): eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False" -def getPyfaPath(Append=None): +def getPyfaPath(append=None): base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] root = os.path.dirname(os.path.realpath(os.path.abspath(base))) - if type(root) == str: # leave unicode ones alone - try: - root = root.decode('utf8') - except UnicodeDecodeError: - root = root.decode('windows-1252') + return _getPath(root, append) - if not Append: - return root - - if type(root) == str: # leave unicode ones alone - try: - path = os.path.abspath(os.path.join(root, Append)).decode('utf8') - except UnicodeDecodeError: - path = os.path.abspath(os.path.join(root, Append)).decode('windows-1252') - else: - path = os.path.abspath(os.path.join(root, Append)) - - return path - - -def getSavePath(Append=None): +def getSavePath(append=None): root = os.path.expanduser(os.path.join("~", ".pyfa")) + return _getPath(root, append) + +def _getPath(root, Append=None): if type(root) == str: # leave unicode ones alone try: root = root.decode('utf8') From 16505b30bf4621dd018c23dceba1b26aadf3b842 Mon Sep 17 00:00:00 2001 From: blitzman Date: Mon, 12 Dec 2016 00:43:42 -0500 Subject: [PATCH 12/26] working commit --- eos/db/saveddata/fleet.py | 65 -------- eos/saveddata/fleet.py | 341 -------------------------------------- 2 files changed, 406 deletions(-) delete mode 100644 eos/db/saveddata/fleet.py delete mode 100644 eos/saveddata/fleet.py diff --git a/eos/db/saveddata/fleet.py b/eos/db/saveddata/fleet.py deleted file mode 100644 index eb644b02e..000000000 --- a/eos/db/saveddata/fleet.py +++ /dev/null @@ -1,65 +0,0 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -from sqlalchemy import Table, Column, Integer, ForeignKey, String -from sqlalchemy.orm import mapper, relation - -from eos.db import saveddata_meta -from eos.db.saveddata.fit import fits_table -from eos.types import Fleet, Wing, Squad, Fit - -gangs_table = Table("gangs", saveddata_meta, - Column("ID", Integer, primary_key=True), - Column("leaderID", ForeignKey("fits.ID")), - Column("boosterID", ForeignKey("fits.ID")), - Column("name", String)) - -wings_table = Table("wings", saveddata_meta, - Column("ID", Integer, primary_key=True), - Column("gangID", ForeignKey("gangs.ID")), - Column("boosterID", ForeignKey("fits.ID")), - Column("leaderID", ForeignKey("fits.ID"))) - -squads_table = Table("squads", saveddata_meta, - Column("ID", Integer, primary_key=True), - Column("wingID", ForeignKey("wings.ID")), - Column("leaderID", ForeignKey("fits.ID")), - Column("boosterID", ForeignKey("fits.ID"))) - -squadmembers_table = Table("squadmembers", saveddata_meta, - Column("squadID", ForeignKey("squads.ID"), primary_key=True), - Column("memberID", ForeignKey("fits.ID"), primary_key=True)) - -mapper(Fleet, gangs_table, - properties={"wings": relation(Wing, backref="gang"), - "leader": relation(Fit, primaryjoin=gangs_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin=gangs_table.c.boosterID == fits_table.c.ID)}) - -mapper(Wing, wings_table, - properties={"squads": relation(Squad, backref="wing"), - "leader": relation(Fit, primaryjoin=wings_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin=wings_table.c.boosterID == fits_table.c.ID)}) - -mapper(Squad, squads_table, - properties={"leader": relation(Fit, primaryjoin=squads_table.c.leaderID == fits_table.c.ID), - "booster": relation(Fit, primaryjoin=squads_table.c.boosterID == fits_table.c.ID), - "members": relation(Fit, - primaryjoin=squads_table.c.ID == squadmembers_table.c.squadID, - secondaryjoin=squadmembers_table.c.memberID == fits_table.c.ID, - secondary=squadmembers_table)}) diff --git a/eos/saveddata/fleet.py b/eos/saveddata/fleet.py deleted file mode 100644 index 6fc1b6330..000000000 --- a/eos/saveddata/fleet.py +++ /dev/null @@ -1,341 +0,0 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -from copy import deepcopy -from itertools import chain - -from eos.types import Skill, Module, Ship - - -class Fleet(object): - def calculateModifiedAttributes(self): - # Make sure ALL fits in the gang have been calculated - for c in chain(self.wings, (self.leader,)): - if c is not None: - c.calculateModifiedAttributes() - - leader = self.leader - self.booster = booster = self.booster if self.booster is not None else leader - self.broken = False - self.store = store = Store() - store.set(booster, "fleet") - # Go all the way down for each subtree we have. - for wing in self.wings: - wing.calculateGangBonusses(store) - - # Check skill requirements and wing amount to see if we break or not - if len(self.wings) == 0 or leader is None or leader.character is None or leader.character.getSkill( - "Fleet Command").level < len(self.wings): - self.broken = True - - # Now calculate our own if we aren't broken - if not self.broken: - # We only get our own bonuses *Sadface* - store.apply(leader, "fleet") - - def recalculateLinear(self, withBoosters=True, dirtyStorage=None): - self.store = Store() - self.linearBoosts = {} - if withBoosters is True: - if self.leader is not None and self.leader.character is not None and self.leader.character.getSkill( - "Fleet Command").level >= 1: - self.leader.boostsFits.add(self.wings[0].squads[0].members[0].ID) - self.leader.calculateModifiedAttributes() - self.store.set(self.leader, "squad", clearingUpdate=True) - else: - self.store = Store() - if self.leader is not None: - try: - self.leader.boostsFits.remove(self.wings[0].squads[0].members[0].ID) - except KeyError: - pass - self.wings[0].recalculateLinear(self.store, withBoosters=withBoosters, dirtyStorage=dirtyStorage) - return self.linearBoosts - - def count(self): - total = 0 - for wing in self.wings: - total += wing.count() - - return total - - def extend(self): - self.wings.append(Wing()) - - def __deepcopy__(self, memo): - copy = Fleet() - copy.name = self.name - copy.booster = deepcopy(self.booster) - copy.leader = deepcopy(self.leader) - for wing in self.wings: - copy.wings.append(deepcopy(wing)) - - return copy - - -class Wing(object): - def calculateModifiedAttributes(self): - for c in chain(self.squads, (self.leader,)): - if c is not None: - c.calculateModifiedAttributes() - - def calculateGangBonusses(self, store): - self.broken = False - leader = self.leader - self.booster = booster = self.booster if self.booster is not None else leader - - store.set(booster, "wing") - - # ALWAYS move down - for squad in self.squads: - squad.calculateGangBonusses(store) - - # Check skill requirements and squad amount to see if we break or not - if len(self.squads) == 0 or leader is None or leader.character is None or leader.character.getSkill( - "Wing Command").level < len(self.squads): - self.broken = True - - # Check if we aren't broken, if we aren't, boost - if not self.broken: - store.apply(leader, "wing") - else: - # We broke, don't go up - self.gang.broken = True - - def recalculateLinear(self, store, withBoosters=True, dirtyStorage=None): - if withBoosters is True: - if self.leader is not None and self.leader.character is not None and self.leader.character.getSkill( - "Wing Command").level >= 1: - self.leader.boostsFits.add(self.squads[0].members[0].ID) - self.leader.calculateModifiedAttributes() - store.set(self.leader, "squad", clearingUpdate=False) - else: - store = Store() - if self.gang.leader is not None: - try: - self.gang.leader.boostsFits.remove(self.squads[0].members[0].ID) - except KeyError: - pass - if self.leader is not None: - try: - self.leader.boostsFits.remove(self.squads[0].members[0].ID) - except KeyError: - pass - self.squads[0].recalculateLinear(store, withBoosters=withBoosters, dirtyStorage=dirtyStorage) - - def count(self): - total = 0 if self.leader is None else 1 - for squad in self.squads: - total += squad.count() - - return total - - def extend(self): - self.squads.append(Squad()) - - def __deepcopy__(self, memo): - copy = Wing() - copy.booster = deepcopy(self.booster) - copy.leader = deepcopy(self.leader) - for squad in self.squads: - copy.squads.append(deepcopy(squad)) - - return copy - - -class Squad(object): - def calculateModifiedAttributes(self): - for member in self.members: - member.calculateModifiedAttributes() - - def calculateGangBonusses(self, store): - self.broken = False - leader = self.leader - self.booster = booster = self.booster if self.booster is not None else leader - store.set(booster, "squad") - - # Check skill requirements and squad size to see if we break or not - if len(self.members) <= 0 or leader is None or leader.character is None or leader.character.getSkill( - "Leadership").level * 2 < len(self.members): - self.broken = True - - if not self.broken: - for member in self.members: - store.apply(member, "squad") - else: - self.wing.broken = True - - def recalculateLinear(self, store, withBoosters=True, dirtyStorage=None): - if withBoosters is True: - if self.leader is not None and self.leader.character is not None and self.leader.character.getSkill( - "Leadership").level >= 1: - self.leader.boostsFits.add(self.members[0].ID) - self.leader.calculateModifiedAttributes(dirtyStorage=dirtyStorage) - store.set(self.leader, "squad", clearingUpdate=False) - else: - store = Store() - if self.leader is not None: - try: - self.leader.boostsFits.remove(self.members[0].ID) - except KeyError: - pass - if self.wing.leader is not None: - try: - self.wing.leader.boostsFits.remove(self.members[0].ID) - except KeyError: - pass - if self.wing.gang.leader is not None: - try: - self.wing.gang.leader.boostsFits.remove(self.members[0].ID) - except KeyError: - pass - if getattr(self.wing.gang, "linearBoosts", None) is None: - self.wing.gang.linearBoosts = {} - dict = store.bonuses["squad"] - for boostedAttr, boostInfoList in dict.iteritems(): - for boostInfo in boostInfoList: - effect, thing = boostInfo - # Get current boost value for given attribute, use 0 as fallback if - # no boosts applied yet - currBoostAmount = self.wing.gang.linearBoosts.get(boostedAttr, (0,))[0] - # Attribute name which is used to get boost value - newBoostAttr = effect.getattr("gangBonus") or "commandBonus" - # Get boost amount for current boost - newBoostAmount = thing.getModifiedItemAttr(newBoostAttr) or 0 - # Skill used to modify the gang bonus (for purposes of comparing old vs new) - newBoostSkill = effect.getattr("gangBonusSkill") - # If skill takes part in gang boosting, multiply by skill level - if type(thing) == Skill: - newBoostAmount *= thing.level - # boost the gang bonus based on skill noted in effect file - if newBoostSkill: - newBoostAmount *= thing.parent.character.getSkill(newBoostSkill).level - # If new boost is more powerful, replace older one with it - if abs(newBoostAmount) > abs(currBoostAmount): - self.wing.gang.linearBoosts[boostedAttr] = (newBoostAmount, boostInfo) - - def count(self): - return len(self.members) - - def __deepcopy__(self, memo): - copy = Squad() - copy.booster = deepcopy(self.booster) - copy.leader = deepcopy(self.leader) - for member in self.members: - copy.members.append(deepcopy(member)) - - return copy - - -class Store(object): - def __init__(self): - # Container for gang boosters and their respective bonuses, three-layered - self.bonuses = {} - for dictType in ("fleet", "wing", "squad"): - self.bonuses[dictType] = {} - # Container for boosted fits and corresponding boosts applied onto them - self.boosts = {} - - def set(self, fitBooster, layer, clearingUpdate=True): - """Add all gang boosts of given fit for given layer to boost store""" - if fitBooster is None: - return - - # This dict contains all bonuses for specified layer - dict = self.bonuses[layer] - if clearingUpdate is True: - # Clear existing bonuses - dict.clear() - - # Go through everything which can be used as gang booster - for thing in chain(fitBooster.modules, fitBooster.implants, fitBooster.character.skills, (fitBooster.ship,)): - if thing.item is None: - continue - for effect in thing.item.effects.itervalues(): - # And check if it actually has gang boosting effects - if effect.isType("gang"): - # Attribute which is boosted - boostedAttr = effect.getattr("gangBoost") - # List which contains all bonuses for given attribute for given layer - l = dict.get(boostedAttr) - # If there was no list, create it - if l is None: - l = dict[boostedAttr] = [] - # And append effect which is used to boost stuff and carrier of this effect - l.append((effect, thing)) - - contextMap = {Skill: "skill", - Ship: "ship", - Module: "module"} - - def apply(self, fitBoosted, layer): - """Applies all boosts onto given fit for given layer""" - if fitBoosted is None: - return - # Boosts dict contains all bonuses applied onto given fit - self.boosts[fitBoosted] = boosts = {} - # Go through all bonuses for given layer, and find highest one per boosted attribute - for currLayer in ("fleet", "wing", "squad"): - # Dictionary with boosts for given layer - dict = self.bonuses[currLayer] - for boostedAttr, boostInfoList in dict.iteritems(): - for boostInfo in boostInfoList: - effect, thing = boostInfo - # Get current boost value for given attribute, use 0 as fallback if - # no boosts applied yet - currBoostAmount = boosts.get(boostedAttr, (0,))[0] - # Attribute name which is used to get boost value - newBoostAttr = effect.getattr("gangBonus") or "commandBonus" - # Get boost amount for current boost - newBoostAmount = thing.getModifiedItemAttr(newBoostAttr) or 0 - # Skill used to modify the gang bonus (for purposes of comparing old vs new) - newBoostSkill = effect.getattr("gangBonusSkill") - # If skill takes part in gang boosting, multiply by skill level - if type(thing) == Skill: - newBoostAmount *= thing.level - # boost the gang bonus based on skill noted in effect file - if newBoostSkill: - newBoostAmount *= thing.parent.character.getSkill(newBoostSkill).level - # If new boost is more powerful, replace older one with it - if abs(newBoostAmount) > abs(currBoostAmount): - boosts[boostedAttr] = (newBoostAmount, boostInfo) - - # Don't look further down then current layer, wing commanders don't get squad bonuses and all that - if layer == currLayer: - break - - self.modify(fitBoosted) - - def getBoosts(self, fit): - """Return all boosts applied onto given fit""" - return self.boosts.get(fit) - - def modify(self, fitBoosted): - # Get all boosts which should be applied onto current fit - boosts = self.getBoosts(fitBoosted) - # Now we got it all figured out, actually do the useful part of all this - for name, info in boosts.iteritems(): - # Unpack all data required to run effect properly - effect, thing = info[1] - context = ("gang", self.contextMap[type(thing)]) - # Run effect, and get proper bonuses applied - try: - effect.handler(fitBoosted, thing, context) - except: - pass From f198ff17730c6d7d37715ce700d2f4792caca13e Mon Sep 17 00:00:00 2001 From: blitzman Date: Mon, 12 Dec 2016 00:49:47 -0500 Subject: [PATCH 13/26] more clean up --- eos/saveddata/fit.py | 28 ---------------------------- service/fit.py | 3 ++- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 27cbece91..01d0771ed 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -134,7 +134,6 @@ class Fit(object): self.__capRecharge = None self.__calculatedTargets = [] self.factorReload = False - self.fleet = None self.boostsFits = set() self.gangBoosts = None self.ecmProjectedStr = 1 @@ -471,14 +470,6 @@ class Fit(object): self.register(thing) effect.handler(self, thing, context, warfareBuffID = warfareBuffID) - # if effect.isType("offline") or (effect.isType("passive") and thing.state >= State.ONLINE) or \ - # (effect.isType("active") and thing.state >= State.ACTIVE): - # # Run effect, and get proper bonuses applied - # try: - # self.register(thing) - # effect.handler(self, thing, context) - # except: - # pass else: # Run effect, and get proper bonuses applied try: @@ -504,7 +495,6 @@ class Fit(object): # Don't inspect this, we genuinely want to reassign self # noinspection PyMethodFirstArgAssignment self = copy.deepcopy(self) - self.fleet = copied.fleet logger.debug("Handling self projection - making shadow copy of fit. %r => %r", copied, self) # we delete the fit because when we copy a fit, flush() is # called to properly handle projection updates. However, we do @@ -517,24 +507,6 @@ class Fit(object): continue fit.calculateModifiedAttributes(self, True) - # - # for thing in chain(fit.modules, fit.implants, fit.character.skills, (fit.ship,)): - # if thing.item is None: - # continue - # for effect in thing.item.effects.itervalues(): - # # And check if it actually has gang boosting effects - # if effect.isType("gang"): - # effect.handler(self, thing, ("commandRun")) - - # if self.fleet is not None and withBoosters is True: - # logger.debug("Fleet is set, gathering gang boosts") - # - # self.gangBoosts = self.fleet.recalculateLinear(withBoosters=withBoosters) - # - # timer.checkpoint("Done calculating gang boosts for %r"%self) - - # elif self.fleet is None: - # self.gangBoosts = None # If we're not explicitly asked to project fit onto something, # set self as target fit diff --git a/service/fit.py b/service/fit.py index 4178c2bdf..42eb60046 100644 --- a/service/fit.py +++ b/service/fit.py @@ -235,7 +235,8 @@ class Fit(object): self.recalc(fit, withBoosters=True) def getFit(self, fitID, projected=False, basic=False): - ''' Gets fit from database, and populates fleet data. + ''' Gets fit from database + Projected is a recursion flag that is set to reduce recursions into projected fits Basic is a flag to simply return the fit without any other processing ''' From 2b9342692d3ce69f6e3eb5ce76d0acbf9bf05078 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sun, 18 Dec 2016 09:45:26 -0500 Subject: [PATCH 14/26] working commit --- gui/additionsPane.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/additionsPane.py b/gui/additionsPane.py index c7b28b48b..e67963ee1 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -29,7 +29,6 @@ from gui.projectedView import ProjectedView from gui.commandView import CommandView from gui.notesView import NotesView from gui.pyfatogglepanel import TogglePanel -from gui.gangView import GangView from gui.bitmapLoader import BitmapLoader import gui.chromeTabs From 50c6133046c4e0b92a85d4405da5c9b8dde5e50e Mon Sep 17 00:00:00 2001 From: Ryan Holmes Date: Sun, 18 Dec 2016 10:09:55 -0500 Subject: [PATCH 15/26] Revert "Remembers user's meta filtering choices" --- gui/marketBrowser.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index 7659c897f..14dea666c 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -63,7 +63,6 @@ class MarketBrowser(wx.Panel): self.metaButtons = [] for name in self.sMkt.META_MAP.keys(): btn = wx.ToggleButton(p, wx.ID_ANY, name.capitalize(), style=wx.BU_EXACTFIT) - btn.SetValue(True) setattr(self, name, btn) box.Add(btn, 1, wx.ALIGN_CENTER) btn.Bind(wx.EVT_TOGGLEBUTTON, self.toggleMetaButton) @@ -176,6 +175,7 @@ class MarketTree(wx.TreeCtrl): self.marketBrowser.searchMode = False sMkt = self.sMkt mg = sMkt.getMarketGroupByItem(item) + metaId = sMkt.getMetaGroupIdByItem(item) jumpList = [] while mg is not None: @@ -197,7 +197,7 @@ class MarketTree(wx.TreeCtrl): self.Expand(item) self.SelectItem(item) - self.marketBrowser.itemView.selectionMade() + self.marketBrowser.itemView.selectionMade(forcedMetaSelect=metaId) class ItemView(d.Display): DEFAULT_COLS = ["Base Icon", @@ -269,7 +269,7 @@ class ItemView(d.Display): self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].append(itemID) - def selectionMade(self, event=None): + def selectionMade(self, event=None, forcedMetaSelect=None): self.marketBrowser.searchMode = False # Grab the threeview selection and check if it's fine sel = self.marketView.GetSelection() @@ -299,7 +299,7 @@ class ItemView(d.Display): # Set toggle buttons / use search mode flag if recently used modules category is selected (in order to have all modules listed and not filtered) if seldata is not RECENTLY_USED_MODULES: - self.setToggles() + self.setToggles(forcedMetaSelect=forcedMetaSelect) else: self.marketBrowser.searchMode = True self.setToggles() @@ -319,7 +319,7 @@ class ItemView(d.Display): self.filteredStore = sMkt.filterItemsByMeta(self.unfilteredStore, selectedMetas) self.update(list(self.filteredStore)) - def setToggles(self): + def setToggles(self, forcedMetaSelect=None): metaIDs = set() sMkt = self.sMkt for item in self.unfilteredStore: @@ -329,8 +329,23 @@ class ItemView(d.Display): btnMetas = sMkt.META_MAP[btn.metaName] if len(metaIDs.intersection(btnMetas)) > 0: btn.Enable(True) + # Select all available buttons if we're searching + if self.marketBrowser.searchMode is True: + btn.SetValue(True) + # Select explicitly requested button + if forcedMetaSelect is not None: + btn.SetValue(True if forcedMetaSelect in btnMetas else False) else: btn.Enable(False) + btn.SetValue(False) + if btn.GetValue(): + anySelection = True + # If no buttons are pressed, press first active + if anySelection is False: + for btn in self.marketBrowser.metaButtons: + if btn.Enabled: + btn.SetValue(True) + break def scheduleSearch(self, event=None): search = self.marketBrowser.search.GetLineText(0) From ff60538e21eb39296c1062edcfb0cd232d5bc6f0 Mon Sep 17 00:00:00 2001 From: blitzman Date: Sun, 18 Dec 2016 15:56:06 -0500 Subject: [PATCH 16/26] Revert "Merge branch 'Ebag333-UnicodePaths' into development" This reverts commit 8a6ef788b33656ecc288dcd6c08e7f3d1776e2c3, reversing changes made to cf91ae762718efc06a900b498f118cf4702f0cc4. --- config.py | 94 +++++--------- gui/bitmapLoader.py | 20 ++- gui/graphFrame.py | 67 ++++------ gui/itemStats.py | 284 ++++++++++++++++++++--------------------- service/pycrest/eve.py | 31 ++--- service/settings.py | 63 ++++----- 6 files changed, 242 insertions(+), 317 deletions(-) diff --git a/config.py b/config.py index 66649a2fe..0a9c1a799 100644 --- a/config.py +++ b/config.py @@ -31,20 +31,18 @@ gameDB = None class StreamToLogger(object): - """ - Fake file-like stream object that redirects writes to a logger instance. - From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ - """ - - def __init__(self, logger, log_level=logging.INFO): - self.logger = logger - self.log_level = log_level - self.linebuf = '' - - def write(self, buf): - for line in buf.rstrip().splitlines(): - self.logger.log(self.log_level, line.rstrip()) + """ + Fake file-like stream object that redirects writes to a logger instance. + From: http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ + """ + def __init__(self, logger, log_level=logging.INFO): + self.logger = logger + self.log_level = log_level + self.linebuf = '' + def write(self, buf): + for line in buf.rstrip().splitlines(): + self.logger.log(self.log_level, line.rstrip()) def isFrozen(): if hasattr(sys, 'frozen'): @@ -52,12 +50,16 @@ def isFrozen(): else: return False +def getPyfaRoot(): + base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] + root = os.path.dirname(os.path.realpath(os.path.abspath(base))) + root = unicode(root, sys.getfilesystemencoding()) + return root def __createDirs(path): if not os.path.exists(path): os.makedirs(path) - def defPaths(customSavePath): global debug global pyfaPath @@ -75,32 +77,32 @@ def defPaths(customSavePath): # Python 2.X uses ANSI by default, so we need to convert the character encoding pyfaPath = getattr(configforced, "pyfaPath", pyfaPath) if pyfaPath is None: - pyfaPath = getPyfaPath() + pyfaPath = getPyfaRoot() # Where we store the saved fits etc, default is the current users home directory if saveInRoot is True: savePath = getattr(configforced, "savePath", None) if savePath is None: - savePath = getPyfaPath("saveddata") + savePath = os.path.join(pyfaPath, "saveddata") else: savePath = getattr(configforced, "savePath", None) if savePath is None: - if customSavePath is None: # customSavePath is not overriden - savePath = getSavePath() + if customSavePath is None: # customSavePath is not overriden + savePath = unicode(os.path.expanduser(os.path.join("~", ".pyfa")), + sys.getfilesystemencoding()) else: savePath = customSavePath __createDirs(savePath) if isFrozen(): - certName = "cacert.pem" - os.environ["REQUESTS_CA_BUNDLE"] = getPyfaPath(certName) - os.environ["SSL_CERT_FILE"] = getPyfaPath(certName) + os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem") + os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") - loggingFormat = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' - logging.basicConfig(format=loggingFormat, level=logLevel) - handler = logging.handlers.RotatingFileHandler(getSavePath("log.txt"), maxBytes=1000000, backupCount=3) - formatter = logging.Formatter(loggingFormat) + format = '%(asctime)s %(name)-24s %(levelname)-8s %(message)s' + logging.basicConfig(format=format, level=logLevel) + handler = logging.handlers.RotatingFileHandler(os.path.join(savePath, "log.txt"), maxBytes=1000000, backupCount=3) + formatter = logging.Formatter(format) handler.setFormatter(formatter) logging.getLogger('').addHandler(handler) @@ -112,53 +114,23 @@ def defPaths(customSavePath): sys.stdout = sl # This interferes with cx_Freeze's own handling of exceptions. Find a way to fix this. - # stderr_logger = logging.getLogger('STDERR') - # sl = StreamToLogger(stderr_logger, logging.ERROR) - # sys.stderr = sl + #stderr_logger = logging.getLogger('STDERR') + #sl = StreamToLogger(stderr_logger, logging.ERROR) + #sys.stderr = sl # The database where we store all the fits etc - saveDB = getSavePath("saveddata.db") + saveDB = os.path.join(savePath, "saveddata.db") # The database where the static EVE data from the datadump is kept. # This is not the standard sqlite datadump but a modified version created by eos # maintenance script - gameDB = getPyfaPath("eve.db") + gameDB = os.path.join(pyfaPath, "eve.db") ## DON'T MODIFY ANYTHING BELOW ## import eos.config - # Caching modifiers, disable all gamedata caching, its unneeded. + #Caching modifiers, disable all gamedata caching, its unneeded. eos.config.gamedataCache = False # saveddata db location modifier, shouldn't ever need to touch this eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False" eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False" - - -def getPyfaPath(append=None): - base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] - root = os.path.dirname(os.path.realpath(os.path.abspath(base))) - return _getPath(root, append) - -def getSavePath(append=None): - root = os.path.expanduser(os.path.join("~", ".pyfa")) - return _getPath(root, append) - -def _getPath(root, Append=None): - if type(root) == str: # leave unicode ones alone - try: - root = root.decode('utf8') - except UnicodeDecodeError: - root = root.decode('windows-1252') - - if not Append: - return root - - if type(root) == str: # leave unicode ones alone - try: - path = os.path.abspath(os.path.join(root, Append)).decode('utf8') - except UnicodeDecodeError: - path = os.path.abspath(os.path.join(root, Append)).decode('windows-1252') - else: - path = os.path.abspath(os.path.join(root, Append)) - - return path diff --git a/gui/bitmapLoader.py b/gui/bitmapLoader.py index 2b991a818..45026bedb 100644 --- a/gui/bitmapLoader.py +++ b/gui/bitmapLoader.py @@ -1,4 +1,4 @@ -# =============================================================================== +#=============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,25 +15,23 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -# =============================================================================== +#=============================================================================== -import cStringIO import os.path -import zipfile - -import wx - import config +import wx +import zipfile +import cStringIO try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict - class BitmapLoader(): + try: - archive = zipfile.ZipFile(config.getPyfaPath('imgs.zip'), 'r') + archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') except IOError: archive = None @@ -44,7 +42,7 @@ class BitmapLoader(): @classmethod def getStaticBitmap(cls, name, parent, location): static = wx.StaticBitmap(parent) - static.SetBitmap(cls.getBitmap(name, location)) + static.SetBitmap(cls.getBitmap(name,location)) return static @classmethod @@ -87,7 +85,7 @@ class BitmapLoader(): except KeyError: print "Missing icon file from zip: {0}".format(path) else: - path = config.getPyfaPath('imgs\\' + location + "\\" + filename) + path = os.path.join(config.pyfaPath, 'imgs', location, filename) if os.path.exists(path): return wx.Image(path) diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 4efac6b14..2cedc84d1 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -1,4 +1,4 @@ -# =============================================================================== +#=============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,23 +15,21 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -# =============================================================================== - -import os +#=============================================================================== import wx - +import os +from gui.bitmapLoader import BitmapLoader import gui.display import gui.globalEvents as GE -import gui.mainFrame -import service -from gui.bitmapLoader import BitmapLoader + from gui.graph import Graph +import service +import gui.mainFrame enabled = True mplImported = False - class GraphFrame(wx.Frame): def __init__(self, parent, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE | wx.FRAME_FLOAT_ON_PARENT): @@ -48,20 +46,9 @@ class GraphFrame(wx.Frame): try: cache_dir = mpl._get_cachedir() except: - cache_dir = os.path.expanduser(os.path.join("~", ".matplotlib")) - if type(cache_dir) == str: # leave unicode ones alone - try: - cache_dir = cache_dir.decode('utf8') - except UnicodeDecodeError: - cache_dir = cache_dir.decode('windows-1252') + cache_dir = unicode(os.path.expanduser(os.path.join("~", ".matplotlib"))) cache_file = os.path.join(cache_dir, 'fontList.cache') - if type(cache_file) == str: # leave unicode ones alone - try: - cache_file = cache_file.decode('utf8') - except UnicodeDecodeError: - cache_file = cache_file.decode('windows-1252') - if os.access(cache_dir, os.W_OK | os.X_OK) and os.path.isfile(cache_file): # remove matplotlib font cache, see #234 os.remove(cache_file) @@ -71,7 +58,7 @@ class GraphFrame(wx.Frame): from matplotlib.figure import Figure enabled = True if mpl.__version__[0] != "1": - print "pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds" + print "pyfa: Found matplotlib version ",mpl.__version__, " - activating OVER9000 workarounds" print "pyfa: Recommended minimum matplotlib version is 1.0.0" self.legendFix = True except: @@ -104,20 +91,19 @@ class GraphFrame(wx.Frame): self.figure = Figure(figsize=(4, 3)) - rgbtuple = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE).Get() - clr = [c / 255. for c in rgbtuple] - self.figure.set_facecolor(clr) - self.figure.set_edgecolor(clr) + rgbtuple = wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ).Get() + clr = [c/255. for c in rgbtuple] + self.figure.set_facecolor( clr ) + self.figure.set_edgecolor( clr ) self.canvas = Canvas(self, -1, self.figure) - self.canvas.SetBackgroundColour(wx.Colour(*rgbtuple)) + self.canvas.SetBackgroundColour( wx.Colour( *rgbtuple ) ) self.subplot = self.figure.add_subplot(111) self.subplot.grid(True) self.mainSizer.Add(self.canvas, 1, wx.EXPAND) - self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, - wx.EXPAND) + self.mainSizer.Add(wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0 , wx.EXPAND) self.gridPanel = wx.Panel(self) self.mainSizer.Add(self.gridPanel, 0, wx.EXPAND) @@ -136,8 +122,8 @@ class GraphFrame(wx.Frame): self.graphSelection.SetSelection(0) self.fields = {} self.select(0) - self.sl1 = wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - self.mainSizer.Add(self.sl1, 0, wx.EXPAND) + self.sl1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + self.mainSizer.Add(self.sl1,0, wx.EXPAND) self.mainSizer.Add(self.fitList, 0, wx.EXPAND) self.fitList.fitList.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) @@ -174,18 +160,18 @@ class GraphFrame(wx.Frame): self.gridPanel.DestroyChildren() self.fields.clear() - # Setup textboxes + #Setup textboxes for field, defaultVal in view.getFields().iteritems(): textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) self.fields[field] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) - sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) + sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if defaultVal is not None: if not isinstance(defaultVal, basestring): defaultVal = ("%f" % defaultVal).rstrip("0") if defaultVal[-1:] == ".": - defaultVal += "0" + defaultVal = defaultVal + "0" textBox.ChangeValue(defaultVal) @@ -203,8 +189,7 @@ class GraphFrame(wx.Frame): else: label = field - imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, - wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) + imgLabelSizer.Add(wx.StaticText(self.gridPanel, wx.ID_ANY, label), 0, wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, 3) sizer.Add(imgLabelSizer, 0, wx.ALIGN_CENTER_VERTICAL) self.draw() @@ -219,7 +204,7 @@ class GraphFrame(wx.Frame): try: success, status = view.getPoints(fit, values) if not success: - # TODO: Add a pwetty statys bar to report errors with + #TODO: Add a pwetty statys bar to report errors with self.SetStatusText(status) return @@ -233,15 +218,15 @@ class GraphFrame(wx.Frame): return if self.legendFix and len(legend) > 0: - leg = self.subplot.legend(tuple(legend), "upper right", shadow=False) + leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False) for t in leg.get_texts(): t.set_fontsize('small') for l in leg.get_lines(): l.set_linewidth(1) - elif not self.legendFix and len(legend) > 0: - leg = self.subplot.legend(tuple(legend), "upper right", shadow=False, frameon=False) + elif not self.legendFix and len(legend) >0: + leg = self.subplot.legend(tuple(legend), "upper right" , shadow = False, frameon = False) for t in leg.get_texts(): t.set_fontsize('small') @@ -284,10 +269,10 @@ class FitList(wx.Panel): fitToolTip = wx.ToolTip("Drag a fit into this list to graph it") self.fitList.SetToolTip(fitToolTip) - class FitDisplay(gui.display.Display): DEFAULT_COLS = ["Base Icon", "Base Name"] def __init__(self, parent): gui.display.Display.__init__(self, parent) + diff --git a/gui/itemStats.py b/gui/itemStats.py index 5acbcaba7..35fb48355 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -1,4 +1,4 @@ -# =============================================================================== +#=============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,41 +15,38 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -# =============================================================================== - -import csv -import re -import sys +#=============================================================================== import wx -import wx.html -import wx.lib.mixins.listctrl as listmix - -import config +import re import gui.mainFrame -import service -from eos.types import Fit, Ship, Citadel, Module, Skill, Booster, Implant, Drone, Mode, Fighter from gui.bitmapLoader import BitmapLoader +import sys +import wx.lib.mixins.listctrl as listmix +import wx.html +from eos.types import Fit, Ship, Citadel, Module, Skill, Booster, Implant, Drone, Mode, Fighter +from gui.utils.numberFormatter import formatAmount +import service +import config from gui.contextMenu import ContextMenu from gui.utils.numberFormatter import formatAmount +import csv try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict - class ItemStatsDialog(wx.Dialog): counter = 0 - def __init__( self, victim, fullContext=None, pos=wx.DefaultPosition, size=wx.DefaultSize, - maximized=False - ): + maximized = False + ): wx.Dialog.__init__( self, @@ -58,7 +55,7 @@ class ItemStatsDialog(wx.Dialog): title="Item stats", pos=pos, size=size, - style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU + style=wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER| wx.SYSTEM_MENU ) empty = getattr(victim, "isEmpty", False) @@ -80,27 +77,26 @@ class ItemStatsDialog(wx.Dialog): victim = None self.context = itmContext if item.icon is not None: - before, sep, after = item.icon.iconFile.rpartition("_") - iconFile = "%s%s%s" % (before, sep, "0%s" % after if len(after) < 2 else after) + before,sep,after = item.icon.iconFile.rpartition("_") + iconFile = "%s%s%s" % (before,sep,"0%s" % after if len(after) < 2 else after) itemImg = BitmapLoader.getBitmap(iconFile, "icons") if itemImg is not None: self.SetIcon(wx.IconFromBitmap(itemImg)) - self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, - " (%d)" % item.ID if config.debug else "")) + self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, " (%d)"%item.ID if config.debug else "")) self.SetMinSize((300, 200)) if "wxGTK" in wx.PlatformInfo: # GTK has huge tab widgets, give it a bit more room self.SetSize((580, 500)) else: self.SetSize((550, 500)) - # self.SetMaxSize((500, -1)) + #self.SetMaxSize((500, -1)) self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.container = ItemStatsContainer(self, victim, item, itmContext) self.mainSizer.Add(self.container, 1, wx.EXPAND) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) - self.mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) + self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) self.SetSizer(self.mainSizer) @@ -116,13 +112,13 @@ class ItemStatsDialog(wx.Dialog): counter = ItemStatsDialog.counter dlgStep = 30 - if counter * dlgStep > ppos.x + psize.width - dlgsize.x or counter * dlgStep > ppos.y + psize.height - dlgsize.y: + if counter * dlgStep > ppos.x+psize.width-dlgsize.x or counter * dlgStep > ppos.y+psize.height-dlgsize.y: ItemStatsDialog.counter = 1 dlgx = ppos.x + counter * dlgStep dlgy = ppos.y + counter * dlgStep if pos == wx.DefaultPosition: - self.SetPosition((dlgx, dlgy)) + self.SetPosition((dlgx,dlgy)) else: self.SetPosition(pos) if maximized: @@ -142,26 +138,26 @@ class ItemStatsDialog(wx.Dialog): def closeEvent(self, event): - if self.dlgOrder == ItemStatsDialog.counter: + if self.dlgOrder==ItemStatsDialog.counter: ItemStatsDialog.counter -= 1 self.parentWnd.UnregisterStatsWindow(self) self.Destroy() - ########################################################################### ## Class ItemStatsContainer ########################################################################### -class ItemStatsContainer(wx.Panel): - def __init__(self, parent, stuff, item, context=None): - wx.Panel.__init__(self, parent) +class ItemStatsContainer ( wx.Panel ): + + def __init__( self, parent, stuff, item, context = None): + wx.Panel.__init__ ( self, parent ) sMkt = service.Market.getInstance() - mainSizer = wx.BoxSizer(wx.VERTICAL) + mainSizer = wx.BoxSizer( wx.VERTICAL ) - self.nbContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) - mainSizer.Add(self.nbContainer, 1, wx.EXPAND | wx.ALL, 2) + self.nbContainer = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 ) + mainSizer.Add( self.nbContainer, 1, wx.EXPAND |wx.ALL, 2 ) if item.traits is not None: self.traits = ItemTraits(self.nbContainer, stuff, item) @@ -192,7 +188,7 @@ class ItemStatsContainer(wx.Panel): self.SetSizer(mainSizer) self.Layout() - def __del__(self): + def __del__( self ): pass def mouseHit(self, event): @@ -200,54 +196,54 @@ class ItemStatsContainer(wx.Panel): if tab != -1: self.nbContainer.SetSelection(tab) - ########################################################################### ## Class AutoListCtrl ########################################################################### 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 AutoListCtrl ########################################################################### 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) - ########################################################################### ## Class ItemTraits ########################################################################### -class ItemTraits(wx.Panel): +class ItemTraits ( wx.Panel ): + def __init__(self, parent, stuff, item): - wx.Panel.__init__(self, parent) + 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) + mainSizer.Add(self.traits, 1, wx.ALL|wx.EXPAND, 0) self.Layout() - ########################################################################### ## Class ItemDescription ########################################################################### -class ItemDescription(wx.Panel): +class ItemDescription ( wx.Panel ): + def __init__(self, parent, stuff, item): - wx.Panel.__init__(self, parent) + wx.Panel.__init__ (self, parent) mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainSizer) @@ -264,30 +260,28 @@ class ItemDescription(wx.Panel): desc = re.sub("<( *)font( *)color( *)=(.*?)>(?P.*?)<( *)/( *)font( *)>", "\g", desc) # Strip URLs desc = re.sub("<( *)a(.*?)>(?P.*?)<( *)/( *)a( *)>", "\g", desc) - desc = "" + desc + "" + desc = "" + desc + "" self.description.SetPage(desc) - mainSizer.Add(self.description, 1, wx.ALL | wx.EXPAND, 0) + mainSizer.Add(self.description, 1, wx.ALL|wx.EXPAND, 0) self.Layout() - ########################################################################### ## Class ItemParams ########################################################################### -class ItemParams(wx.Panel): - def __init__(self, parent, stuff, item, context=None): - wx.Panel.__init__(self, parent) - mainSizer = wx.BoxSizer(wx.VERTICAL) +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_HRULES | - # wx.LC_NO_HEADER | - 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) + style = #wx.LC_HRULES | + #wx.LC_NO_HEADER | + 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 @@ -296,31 +290,29 @@ class ItemParams(wx.Panel): 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.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.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.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) + 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) + 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) + mainSizer.Add( bSizer, 0, wx.ALIGN_RIGHT) self.PopulateList() - self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode) + self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON,self.ToggleViewMode) self.exportStatsBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ExportItemStats) def _fetchValues(self): @@ -356,7 +348,7 @@ class ItemParams(wx.Panel): event.Skip() def ToggleViewMode(self, event): - self.toggleView *= -1 + self.toggleView *=-1 self.UpdateList() event.Skip() @@ -420,17 +412,17 @@ class ItemParams(wx.Panel): ) def PopulateList(self): - self.paramList.InsertColumn(0, "Attribute") - self.paramList.InsertColumn(1, "Current Value") + 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) + 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.SetColumnWidth(2,90) self.paramList.setResizeColumn(0) self.imageList = wx.ImageList(16, 16) - self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) + self.paramList.SetImageList(self.imageList,wx.IMAGE_LIST_SMALL) names = list(self.attrValues.iterkeys()) names.sort() @@ -469,6 +461,7 @@ class ItemParams(wx.Panel): 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) @@ -492,9 +485,11 @@ class ItemParams(wx.Panel): 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.totalAttrsLabel.SetLabel("%d attributes. " %idCount) self.Layout() def TranslateValueUnit(self, value, unitName, unitDisplayName): @@ -510,16 +505,15 @@ class ItemParams(wx.Panel): attribute = service.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), + 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), + "Absolute Percent": (lambda: (value * 100) , unitName), "Milliseconds": (lambda: value / 1000.0, unitName), "typeID": (itemIDCallback, ""), - "groupID": (groupIDCallback, ""), + "groupID": (groupIDCallback,""), "attributeID": (attributeIDCallback, "")} override = trans.get(unitDisplayName) @@ -533,10 +527,9 @@ class ItemParams(wx.Panel): fvalue = formatAmount(v, 3, 0, 0) else: fvalue = v - return "%s %s" % (fvalue, override[1]) + return "%s %s" % (fvalue , override[1]) else: - return "%s %s" % (formatAmount(value, 3, 0), unitName) - + return "%s %s" % (formatAmount(value, 3, 0),unitName) class ItemCompare(wx.Panel): def __init__(self, parent, stuff, item, items, context=None): @@ -555,8 +548,7 @@ class ItemCompare(wx.Panel): 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.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 @@ -635,7 +627,7 @@ class ItemCompare(wx.Panel): def processPrices(self, prices): for i, price in enumerate(prices): - self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(price.price, 3, 3, 9, currency=True)) + self.paramList.SetStringItem(i, len(self.attrs)+1, formatAmount(price.price, 3, 3, 9, currency=True)) def PopulateList(self, sort=None): @@ -666,11 +658,11 @@ class ItemCompare(wx.Panel): 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(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) + self.paramList.InsertColumn(len(self.attrs)+1, "Price") + self.paramList.SetColumnWidth(len(self.attrs)+1, 60) sMkt = service.Market.getInstance() sMkt.getPrices([x.ID for x in self.items], self.processPrices) @@ -688,7 +680,7 @@ class ItemCompare(wx.Panel): else: valueUnit = formatAmount(value, 3, 0, 0) - self.paramList.SetStringItem(i, x + 1, valueUnit) + self.paramList.SetStringItem(i, x+1, valueUnit) self.paramList.RefreshRows() self.Layout() @@ -709,7 +701,7 @@ class ItemCompare(wx.Panel): 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), + 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), @@ -738,18 +730,19 @@ class ItemCompare(wx.Panel): ## Class ItemRequirements ########################################################################### -class ItemRequirements(wx.Panel): +class ItemRequirements ( wx.Panel ): + def __init__(self, parent, stuff, item): - wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) + 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) + #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) + 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) + mainSizer.Add(self.reqTree, 1, wx.ALL|wx.EXPAND, 0) self.SetSizer(mainSizer) self.root = self.reqTree.AddRoot("WINRARZOR") @@ -759,17 +752,17 @@ class ItemRequirements(wx.Panel): self.reqTree.SetImageList(self.imageList) skillBookId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) - self.getFullSkillTree(item, self.root, skillBookId) + self.getFullSkillTree(item,self.root,skillBookId) self.reqTree.ExpandAll() self.Layout() - def getFullSkillTree(self, parentSkill, parent, sbIconId): + 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) + 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.getFullSkillTree(skill,child,sbIconId) self.skillIdHistory.append(skill.ID) @@ -777,7 +770,7 @@ class ItemRequirements(wx.Panel): ## Class ItemEffects ########################################################################### -class ItemEffects(wx.Panel): +class ItemEffects (wx.Panel): def __init__(self, parent, stuff, item): wx.Panel.__init__(self, parent) self.item = item @@ -800,17 +793,17 @@ class ItemEffects(wx.Panel): def PopulateList(self): - self.effectList.InsertColumn(0, "Name") - self.effectList.InsertColumn(1, "Active") + 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.InsertColumn(4,"ID") - # self.effectList.SetColumnWidth(0,385) + #self.effectList.SetColumnWidth(0,385) self.effectList.setResizeColumn(0) - self.effectList.SetColumnWidth(1, 50) + self.effectList.SetColumnWidth(1,50) self.effectList.SetColumnWidth(2, 80) if config.debug: self.effectList.SetColumnWidth(3, 65) @@ -879,7 +872,7 @@ class ItemEffects(wx.Panel): """ import os - file = config.getPyfaPath("eos\\effects\\%s.py" % event.GetText().lower()) + file = os.path.join(config.pyfaPath, "eos", "effects", "%s.py"%event.GetText().lower()) if not os.path.isfile(file): open(file, 'a').close() @@ -887,7 +880,7 @@ class ItemEffects(wx.Panel): if 'wxMSW' in wx.PlatformInfo: os.startfile(file) elif 'wxMac' in wx.PlatformInfo: - os.system("open " + file) + os.system("open "+file) else: import subprocess subprocess.call(["xdg-open", file]) @@ -901,15 +894,13 @@ class ItemEffects(wx.Panel): self.Thaw() event.Skip() - ########################################################################### ## Class ItemAffectedBy ########################################################################### -class ItemAffectedBy(wx.Panel): +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 @@ -925,33 +916,33 @@ class ItemAffectedBy(wx.Panel): 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.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) + 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) + 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.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.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) + 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.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) + 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) + mainSizer.Add( bSizer, 0, wx.ALIGN_RIGHT) self.SetSizer(mainSizer) self.PopulateTree() self.Layout() @@ -991,7 +982,7 @@ class ItemAffectedBy(wx.Panel): self.Thaw() - def ToggleExpand(self, event): + def ToggleExpand(self,event): self.expand *= -1 self.ExpandCollapseTree() @@ -1100,8 +1091,7 @@ class ItemAffectedBy(wx.Panel): else: item = afflictor.item - items[attrName].append( - (type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) + items[attrName].append((type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) # Make sure projected fits are on top rootOrder = container.keys() @@ -1177,6 +1167,7 @@ class ItemAffectedBy(wx.Panel): 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 @@ -1300,10 +1291,9 @@ class ItemAffectedBy(wx.Panel): else: penalized = "" - attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, - attrAmount, penalized, attrIcon)) + attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized, attrIcon)) - attrSorted = sorted(attributes, key=lambda attribName: attribName[0]) + attrSorted = sorted(attributes, key = lambda attribName: attribName[0]) for attr in attrSorted: attrName, displayName, attrModifier, attrAmount, penalized, attrIcon = attr diff --git a/service/pycrest/eve.py b/service/pycrest/eve.py index 8218f2bc5..c0f33becd 100644 --- a/service/pycrest/eve.py +++ b/service/pycrest/eve.py @@ -1,17 +1,14 @@ -import base64 -import logging import os -import re +import base64 import time import zlib import requests -from requests.adapters import HTTPAdapter -import config +from . import version from compat import bytes_, text_ from errors import APIException -from . import version +from requests.adapters import HTTPAdapter try: from urllib.parse import urlparse, urlunparse, parse_qsl @@ -27,7 +24,9 @@ try: from urllib.parse import quote except ImportError: # pragma: no cover from urllib import quote - +import logging +import re +import config logger = logging.getLogger("pycrest.eve") cache_re = re.compile(r'max-age=([0-9]+)') @@ -52,13 +51,7 @@ class FileCache(APICache): os.mkdir(self.path, 0o700) def _getpath(self, key): - path = os.path.join(self.path, str(hash(key)) + '.cache') - if type(path) == str: # leave unicode ones alone - try: - path = path.decode('utf8') - except UnicodeDecodeError: - path = path.decode('windows-1252') - return path + return os.path.join(self.path, str(hash(key)) + '.cache') def put(self, key, value): with open(self._getpath(key), 'wb') as f: @@ -118,7 +111,7 @@ class APIConnection(object): }) session.headers.update(additional_headers) session.mount('https://public-crest.eveonline.com', - HTTPAdapter()) + HTTPAdapter()) self._session = session if cache: if isinstance(cache, APICache): @@ -256,18 +249,19 @@ class EVE(APIConnection): def temptoken_authorize(self, access_token=None, expires_in=0, refresh_token=None): self.set_auth_values({'access_token': access_token, - 'refresh_token': refresh_token, - 'expires_in': expires_in}) + 'refresh_token': refresh_token, + 'expires_in': expires_in}) class AuthedConnection(EVE): + def __call__(self): if not self._data: self._data = APIObject(self.get(self._endpoint), self) return self._data def whoami(self): - # if 'whoami' not in self._cache: + #if 'whoami' not in self._cache: # print "Setting this whoami cache" # self._cache['whoami'] = self.get("%s/verify" % self._oauth_endpoint) return self.get("%s/verify" % self._oauth_endpoint) @@ -287,7 +281,6 @@ class AuthedConnection(EVE): self.refr_authorize(self.refresh_token) return self._session.delete(resource, params=params) - class APIObject(object): def __init__(self, parent, connection): self._dict = {} diff --git a/service/settings.py b/service/settings.py index 71dbc9977..89f871973 100644 --- a/service/settings.py +++ b/service/settings.py @@ -1,4 +1,4 @@ -# =============================================================================== +#=============================================================================== # Copyright (C) 2010 Diego Duclos # # This file is part of pyfa. @@ -15,41 +15,33 @@ # # You should have received a copy of the GNU General Public License # along with pyfa. If not, see . -# =============================================================================== +#=============================================================================== import cPickle import os.path +import config import urllib2 -import config - - class SettingsProvider(): - BASE_PATH = config.getSavePath("settings") + BASE_PATH = os.path.join(config.savePath, "settings") settings = {} _instance = None - @classmethod def getInstance(cls): - if cls._instance is None: + if cls._instance == None: cls._instance = SettingsProvider() return cls._instance def __init__(self): if not os.path.exists(self.BASE_PATH): - os.mkdir(self.BASE_PATH) + os.mkdir(self.BASE_PATH); def getSettings(self, area, defaults=None): s = self.settings.get(area) if s is None: p = os.path.join(self.BASE_PATH, area) - if type(p) == str: # leave unicode ones alone - try: - p = p.decode('utf8') - except UnicodeDecodeError: - p = p.decode('windows-1252') if not os.path.exists(p): info = {} @@ -79,7 +71,6 @@ class SettingsProvider(): for settings in self.settings.itervalues(): settings.save() - class Settings(): def __init__(self, location, info): self.location = location @@ -124,13 +115,13 @@ class NetworkSettings(): _instance = None # constants for serviceNetworkDefaultSettings["mode"] parameter - PROXY_MODE_NONE = 0 # 0 - No proxy + PROXY_MODE_NONE = 0 # 0 - No proxy PROXY_MODE_AUTODETECT = 1 # 1 - Auto-detected proxy settings - PROXY_MODE_MANUAL = 2 # 2 - Manual proxy settings + PROXY_MODE_MANUAL = 2 # 2 - Manual proxy settings @classmethod def getInstance(cls): - if cls._instance is None: + if cls._instance == None: cls._instance = NetworkSettings() return cls._instance @@ -193,11 +184,12 @@ class NetworkSettings(): def setAccess(self, access): self.serviceNetworkSettings["access"] = access - @staticmethod - def autodetect(): + def autodetect(self): proxy = None + proxAddr = proxPort = "" proxydict = urllib2.ProxyHandler().proxies + txt = "Auto-detected: " validPrefixes = ("http", "https") @@ -245,57 +237,53 @@ class NetworkSettings(): self.serviceNetworkSettings["password"] = password + """ Settings used by the HTML export feature. """ - - class HTMLExportSettings(): _instance = None @classmethod def getInstance(cls): - if cls._instance is None: + if cls._instance == None: cls._instance = HTMLExportSettings() return cls._instance def __init__(self): - serviceHTMLExportDefaultSettings = {"enabled": False, "path": config.pyfaPath + os.sep + 'pyfaFits.html', - "minimal": False} - self.serviceHTMLExportSettings = SettingsProvider.getInstance().getSettings("pyfaServiceHTMLExportSettings", - serviceHTMLExportDefaultSettings) + serviceHTMLExportDefaultSettings = {"enabled": False, "path": config.pyfaPath + os.sep + 'pyfaFits.html', "minimal": False } + self.serviceHTMLExportSettings = SettingsProvider.getInstance().getSettings("pyfaServiceHTMLExportSettings", serviceHTMLExportDefaultSettings) def getEnabled(self): return self.serviceHTMLExportSettings["enabled"] def setEnabled(self, enabled): self.serviceHTMLExportSettings["enabled"] = enabled - + + def getMinimalEnabled(self): return self.serviceHTMLExportSettings["minimal"] def setMinimalEnabled(self, minimal): self.serviceHTMLExportSettings["minimal"] = minimal + def getPath(self): return self.serviceHTMLExportSettings["path"] def setPath(self, path): self.serviceHTMLExportSettings["path"] = path - """ Settings used by update notification """ - - class UpdateSettings(): _instance = None @classmethod def getInstance(cls): - if cls._instance is None: + if cls._instance == None: cls._instance = UpdateSettings() return cls._instance @@ -305,9 +293,8 @@ class UpdateSettings(): # Updates are completely suppressed via network settings # prerelease - If True, suppress prerelease notifications # version - Set to release tag that user does not want notifications for - serviceUpdateDefaultSettings = {"prerelease": True, 'version': None} - self.serviceUpdateSettings = SettingsProvider.getInstance().getSettings("pyfaServiceUpdateSettings", - serviceUpdateDefaultSettings) + serviceUpdateDefaultSettings = {"prerelease": True, 'version': None } + self.serviceUpdateSettings = SettingsProvider.getInstance().getSettings("pyfaServiceUpdateSettings", serviceUpdateDefaultSettings) def get(self, type): return self.serviceUpdateSettings[type] @@ -315,7 +302,6 @@ class UpdateSettings(): def set(self, type, value): self.serviceUpdateSettings[type] = value - class CRESTSettings(): _instance = None @@ -327,13 +313,13 @@ class CRESTSettings(): return cls._instance def __init__(self): + # mode # 0 - Implicit authentication # 1 - User-supplied client details serviceCRESTDefaultSettings = {"mode": 0, "server": 0, "clientID": "", "clientSecret": "", "timeout": 60} - self.serviceCRESTSettings = SettingsProvider.getInstance().getSettings("pyfaServiceCRESTSettings", - serviceCRESTDefaultSettings) + self.serviceCRESTSettings = SettingsProvider.getInstance().getSettings("pyfaServiceCRESTSettings", serviceCRESTDefaultSettings) def get(self, type): return self.serviceCRESTSettings[type] @@ -341,4 +327,5 @@ class CRESTSettings(): def set(self, type, value): self.serviceCRESTSettings[type] = value + # @todo: migrate fit settings (from fit service) here? From cef58423441ca45ae2f2209d1fbe68b65a5c7b23 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 26 Dec 2016 21:54:52 -0800 Subject: [PATCH 17/26] Database validation and cleanup --- eos/db/saveddata/databaseRepair.py | 122 +++++++++++++++++++++++++++++ service/prefetch.py | 14 +++- 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 eos/db/saveddata/databaseRepair.py diff --git a/eos/db/saveddata/databaseRepair.py b/eos/db/saveddata/databaseRepair.py new file mode 100644 index 000000000..be26c8231 --- /dev/null +++ b/eos/db/saveddata/databaseRepair.py @@ -0,0 +1,122 @@ +# =============================================================================== +# 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 . +# =============================================================================== + +import sqlalchemy +import logging + +logger = logging.getLogger(__name__) + + +class DatabaseCleanup: + def __init__(self): + pass + + @staticmethod + def OrphanedCharacterSkills(saveddata_engine): + # Finds and fixes database corruption issues. + logger.debug("Start databsae validation and cleanup.") + + # Find orphaned character skills. + # This solves an issue where the character doesn't exist, but skills for that character do. + # See issue #917 + try: + logger.debug("Running database cleanup for character skills.") + results = saveddata_engine.execute("SELECT * FROM characterSkills " + "WHERE characterID NOT IN (SELECT ID from characters)") + + # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. + result_count = 0 + for _ in results: + result_count += 1 + + if result_count > 0: + logger.error("Database corruption found. Cleaning up %d records.", result_count) + saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)") + except sqlalchemy.exc.DatabaseError: + logger.error("Failed to connect to database.") + + @staticmethod + def OrphanedFitDamagePatterns(saveddata_engine): + # Find orphaned damage patterns. + # This solves an issue where the damage pattern doesn't exist, but fits reference the pattern. + # See issue #777 + try: + logger.debug("Running database cleanup for orphaned damage patterns attached to fits.") + results = saveddata_engine.execute("SELECT * FROM fits WHERE damagePatternID not in (select ID from damagePatterns)") + + # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. + result_count = 0 + for _ in results: + result_count += 1 + + if result_count > 0: + # Get Uniform damage pattern ID + uniform_results = saveddata_engine.execute("select ID from damagePatterns WHERE name = 'Uniform'") + + uniform_result_count = 0 + uniform_damage_pattern_id = 0 + for uniform_result in uniform_results: + uniform_damage_pattern_id = uniform_result[0] + uniform_result_count += 1 + + if uniform_result_count == 0: + logger.error("Missing uniform damage pattern.") + elif uniform_result_count > 1: + logger.error("More than one uniform damage pattern found.") + else: + logger.error("Database corruption found. Cleaning up %d records.", result_count) + saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " + "WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns)", + uniform_damage_pattern_id) + except sqlalchemy.exc.DatabaseError: + logger.error("Failed to connect to database.") + + @staticmethod + def OrphanedFitCharacterIDs(saveddata_engine): + # Find orphaned character IDs. This solves an issue where the chaaracter doesn't exist, but fits reference the pattern. + try: + logger.debug("Running database cleanup for orphaned characters attached to fits.") + results = saveddata_engine.execute("SELECT * FROM fits WHERE characterID NOT IN (SELECT ID FROM characters)") + + # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. + result_count = 0 + for _ in results: + result_count += 1 + + if result_count > 0: + # Get All 5 character ID + all5_results = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'") + + all5_result_count = 0 + all5_id = 0 + for all5_result in all5_results: + all5_id = all5_result[0] + all5_result_count += 1 + + if all5_result_count == 0: + logger.error("Missing 'All 5' character.") + elif all5_result_count > 1: + logger.error("More than one 'All 5' character found.") + else: + logger.error("Database corruption found. Cleaning up %d records.", result_count) + saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " + "WHERE damagePatternID not in (select ID from damagePatterns)", + all5_id) + except sqlalchemy.exc.DatabaseError: + logger.error("Failed to connect to database.") diff --git a/service/prefetch.py b/service/prefetch.py index b16f52d40..6c45ffc63 100644 --- a/service/prefetch.py +++ b/service/prefetch.py @@ -23,6 +23,11 @@ import os import eos.types import eos.db.migration as migration from eos.db.saveddata.loadDefaultDatabaseValues import DefaultDatabaseValues +from eos.db.saveddata.databaseRepair import DatabaseCleanup +import logging + +logger = logging.getLogger(__name__) + class PrefetchThread(threading.Thread): def run(self): @@ -55,6 +60,14 @@ if os.path.isfile(config.saveDB): # Import default database values # Import values that must exist otherwise Pyfa breaks DefaultDatabaseValues.importRequiredDefaults() + + logging.debug("Starting database validation.") + database_cleanup_instance = DatabaseCleanup() + database_cleanup_instance.OrphanedCharacterSkills(eos.db.saveddata_engine) + database_cleanup_instance.OrphanedFitCharacterIDs(eos.db.saveddata_engine) + database_cleanup_instance.OrphanedFitDamagePatterns(eos.db.saveddata_engine) + logging.debug("Completed database validation.") + else: # If database does not exist, do not worry about migration. Simply # create and set version @@ -67,4 +80,3 @@ else: DefaultDatabaseValues.importDamageProfileDefaults() # Import default values for target resist profiles DefaultDatabaseValues.importResistProfileDefaults() - From 7ee4d1d5887caecd3d1f8e403d9fcbe038e4e0ab Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 26 Dec 2016 23:29:22 -0800 Subject: [PATCH 18/26] Try loading item as a booster if implant fails. --- service/port.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/service/port.py b/service/port.py index 66e105c06..702e836fb 100644 --- a/service/port.py +++ b/service/port.py @@ -406,7 +406,11 @@ class Port(object): cargoMap[modName] = 0 cargoMap[modName] += extraAmount elif item.category.name == "Implant": - fit.implants.append(Implant(item)) + try: + fit.implants.append(Implant(item)) + except ValueError: + # Passed item isn't an implant. Try loading it as a booster. + fit.boosters.append(Booster(item)) # elif item.category.name == "Subsystem": # try: # subsystem = Module(item) From f9ba95b3ded19e17a84a21002afd731bfe81c996 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Tue, 27 Dec 2016 12:32:52 -0800 Subject: [PATCH 19/26] Check the `ness` to see if it's an implant, booster, or something else. Log it if it's something else and don't try and import it. This prevents a bad line from failing the entire import, and (hopefully) we know about it. --- service/port.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/service/port.py b/service/port.py index 702e836fb..f0b24306b 100644 --- a/service/port.py +++ b/service/port.py @@ -406,11 +406,12 @@ class Port(object): cargoMap[modName] = 0 cargoMap[modName] += extraAmount elif item.category.name == "Implant": - try: + if "implantness" in item.attributes: fit.implants.append(Implant(item)) - except ValueError: - # Passed item isn't an implant. Try loading it as a booster. + elif "boosterness" in item.attributes: fit.boosters.append(Booster(item)) + else: + logger.error("Failed to import implant: %s", line) # elif item.category.name == "Subsystem": # try: # subsystem = Module(item) From be3696debacdbfe0cbb4e2a56cef32492f7b5ef2 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sun, 1 Jan 2017 12:47:28 -0800 Subject: [PATCH 20/26] Add some sanity checks and validation of incoming data --- service/character.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/service/character.py b/service/character.py index 802d11092..76702328e 100644 --- a/service/character.py +++ b/service/character.py @@ -34,6 +34,8 @@ import service import config import logging +from eos.saveddata.character import Character as es_Character + logger = logging.getLogger(__name__) class CharacterImportThread(threading.Thread): @@ -45,6 +47,12 @@ class CharacterImportThread(threading.Thread): def run(self): paths = self.paths sCharacter = Character.getInstance() + all5_character = es_Character("All 5", 5) + all_skill_ids = [] + for skill in all5_character.skills: + # Parse out the skill item IDs to make searching it easier later on + all_skill_ids.append(skill.itemID) + for path in paths: try: # we try to parse api XML data first @@ -59,19 +67,28 @@ class CharacterImportThread(threading.Thread): charFile = open(path, mode='r').read() doc = minidom.parseString(charFile) if doc.documentElement.tagName not in ("SerializableCCPCharacter", "SerializableUriCharacter"): + logger.error("Incorrect EVEMon XML sheet") raise RuntimeError("Incorrect EVEMon XML sheet") name = doc.getElementsByTagName("name")[0].firstChild.nodeValue skill_els = doc.getElementsByTagName("skill") skills = [] for skill in skill_els: - skills.append({ - "typeID": int(skill.getAttribute("typeID")), - "level": int(skill.getAttribute("level")), - }) + if int(skill.getAttribute("typeID")) in all_skill_ids and (0 <= int(skill.getAttribute("level")) <= 5): + skills.append({ + "typeID": int(skill.getAttribute("typeID")), + "level": int(skill.getAttribute("level")), + }) + else: + logger.error("Attempted to import unknown skill %s (ID: %s) (Level: %s)", + skill.getAttribute("name"), + skill.getAttribute("typeID"), + skill.getAttribute("level"), + ) char = sCharacter.new(name+" (EVEMon)") sCharacter.apiUpdateCharSheet(char.ID, skills) except Exception, e: - print e.message + logger.error("Exception on character import:") + logger.error(e) continue wx.CallAfter(self.callback) From f4761e099b27a604e1ce2ac40e4e6c6a1be82ba0 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Sun, 1 Jan 2017 13:17:39 -0800 Subject: [PATCH 21/26] Don't let people rename built in chars --- service/character.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/service/character.py b/service/character.py index 76702328e..d439f1cd4 100644 --- a/service/character.py +++ b/service/character.py @@ -272,8 +272,11 @@ class Character(object): return char def rename(self, char, newName): - char.name = newName - eos.db.commit() + if char.name in ("All 0", "All 5"): + logger.info("Cannot rename built in characters.") + else: + char.name = newName + eos.db.commit() def copy(self, char): newChar = copy.deepcopy(char) From 85a89339a73ee9a040e98011b27db2c87835bdd6 Mon Sep 17 00:00:00 2001 From: blitzman Date: Mon, 2 Jan 2017 00:58:54 -0500 Subject: [PATCH 22/26] Cleaned up database corruption stuff --- eos/db/saveddata/databaseRepair.py | 37 ++++++++++-------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/eos/db/saveddata/databaseRepair.py b/eos/db/saveddata/databaseRepair.py index be26c8231..5561b41a1 100644 --- a/eos/db/saveddata/databaseRepair.py +++ b/eos/db/saveddata/databaseRepair.py @@ -37,17 +37,13 @@ class DatabaseCleanup: # See issue #917 try: logger.debug("Running database cleanup for character skills.") - results = saveddata_engine.execute("SELECT * FROM characterSkills " + results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM characterSkills " "WHERE characterID NOT IN (SELECT ID from characters)") - # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. - result_count = 0 - for _ in results: - result_count += 1 + if results.fetchone()['num'] > 0: + delete = saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)") + logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) - if result_count > 0: - logger.error("Database corruption found. Cleaning up %d records.", result_count) - saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)") except sqlalchemy.exc.DatabaseError: logger.error("Failed to connect to database.") @@ -58,14 +54,9 @@ class DatabaseCleanup: # See issue #777 try: logger.debug("Running database cleanup for orphaned damage patterns attached to fits.") - results = saveddata_engine.execute("SELECT * FROM fits WHERE damagePatternID not in (select ID from damagePatterns)") + results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID not in (select ID from damagePatterns)") - # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. - result_count = 0 - for _ in results: - result_count += 1 - - if result_count > 0: + if results.fetchone()['num'] > 0: # Get Uniform damage pattern ID uniform_results = saveddata_engine.execute("select ID from damagePatterns WHERE name = 'Uniform'") @@ -80,10 +71,10 @@ class DatabaseCleanup: elif uniform_result_count > 1: logger.error("More than one uniform damage pattern found.") else: - logger.error("Database corruption found. Cleaning up %d records.", result_count) - saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " + update = saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " "WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns)", uniform_damage_pattern_id) + logger.error("Database corruption found. Cleaning up %d records.", update.rowcount) except sqlalchemy.exc.DatabaseError: logger.error("Failed to connect to database.") @@ -92,14 +83,10 @@ class DatabaseCleanup: # Find orphaned character IDs. This solves an issue where the chaaracter doesn't exist, but fits reference the pattern. try: logger.debug("Running database cleanup for orphaned characters attached to fits.") - results = saveddata_engine.execute("SELECT * FROM fits WHERE characterID NOT IN (SELECT ID FROM characters)") + results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters)") # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. - result_count = 0 - for _ in results: - result_count += 1 - - if result_count > 0: + if results.fetchone()['num'] > 0: # Get All 5 character ID all5_results = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'") @@ -114,9 +101,9 @@ class DatabaseCleanup: elif all5_result_count > 1: logger.error("More than one 'All 5' character found.") else: - logger.error("Database corruption found. Cleaning up %d records.", result_count) - saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " + update = saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " "WHERE damagePatternID not in (select ID from damagePatterns)", all5_id) + logger.error("Database corruption found. Cleaning up %d records.", update.rowcount) except sqlalchemy.exc.DatabaseError: logger.error("Failed to connect to database.") From c1ead5fe27eda8be9f15c4c9f3133ff9ec5e37e4 Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 2 Jan 2017 00:29:44 -0800 Subject: [PATCH 23/26] change method for retreiving count --- eos/db/saveddata/databaseRepair.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/eos/db/saveddata/databaseRepair.py b/eos/db/saveddata/databaseRepair.py index 5561b41a1..bfc6e8569 100644 --- a/eos/db/saveddata/databaseRepair.py +++ b/eos/db/saveddata/databaseRepair.py @@ -40,7 +40,13 @@ class DatabaseCleanup: results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM characterSkills " "WHERE characterID NOT IN (SELECT ID from characters)") - if results.fetchone()['num'] > 0: + count_results = 0 + for result in results: + count_results = result[0] + break + + # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. + if count_results > 0: delete = saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)") logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) @@ -56,7 +62,13 @@ class DatabaseCleanup: logger.debug("Running database cleanup for orphaned damage patterns attached to fits.") results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID not in (select ID from damagePatterns)") - if results.fetchone()['num'] > 0: + count_results = 0 + for result in results: + count_results = result[0] + break + + # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. + if count_results > 0: # Get Uniform damage pattern ID uniform_results = saveddata_engine.execute("select ID from damagePatterns WHERE name = 'Uniform'") @@ -80,13 +92,18 @@ class DatabaseCleanup: @staticmethod def OrphanedFitCharacterIDs(saveddata_engine): - # Find orphaned character IDs. This solves an issue where the chaaracter doesn't exist, but fits reference the pattern. + # Find orphaned character IDs. This solves an issue where the character doesn't exist, but fits reference the pattern. try: logger.debug("Running database cleanup for orphaned characters attached to fits.") results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters)") + count_results = 0 + for result in results: + count_results = result[0] + break + # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. - if results.fetchone()['num'] > 0: + if count_results > 0: # Get All 5 character ID all5_results = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'") From a76ee6b2fc42e69add53ce9ba7d1db1bc87315fb Mon Sep 17 00:00:00 2001 From: blitzman Date: Mon, 2 Jan 2017 12:07:15 -0500 Subject: [PATCH 24/26] more tweaks --- eos/db/saveddata/databaseRepair.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/eos/db/saveddata/databaseRepair.py b/eos/db/saveddata/databaseRepair.py index bfc6e8569..d11f97aed 100644 --- a/eos/db/saveddata/databaseRepair.py +++ b/eos/db/saveddata/databaseRepair.py @@ -40,13 +40,9 @@ class DatabaseCleanup: results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM characterSkills " "WHERE characterID NOT IN (SELECT ID from characters)") - count_results = 0 - for result in results: - count_results = result[0] - break + row = results.first() - # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. - if count_results > 0: + if row and row['num'] > 0: delete = saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)") logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) @@ -62,13 +58,9 @@ class DatabaseCleanup: logger.debug("Running database cleanup for orphaned damage patterns attached to fits.") results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID not in (select ID from damagePatterns)") - count_results = 0 - for result in results: - count_results = result[0] - break + row = results.first() - # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. - if count_results > 0: + if row and row['num'] > 0: # Get Uniform damage pattern ID uniform_results = saveddata_engine.execute("select ID from damagePatterns WHERE name = 'Uniform'") @@ -97,13 +89,9 @@ class DatabaseCleanup: logger.debug("Running database cleanup for orphaned characters attached to fits.") results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters)") - count_results = 0 - for result in results: - count_results = result[0] - break + row = results.first() - # Count how many records exist. This is ugly, but SQLAlchemy doesn't return a count from a select query. - if count_results > 0: + if row and row['num'] > 0: # Get All 5 character ID all5_results = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'") From 3313b7421f9b343b8adc500bed246a61e0328c3c Mon Sep 17 00:00:00 2001 From: Ebag333 Date: Mon, 2 Jan 2017 13:41:39 -0800 Subject: [PATCH 25/26] Move query logic into own function in queries. Tighten up flow in DB repair. Catch null values. --- eos/db/saveddata/databaseRepair.py | 45 +++++++++++------------------- eos/db/saveddata/queries.py | 16 +++++++++++ 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/eos/db/saveddata/databaseRepair.py b/eos/db/saveddata/databaseRepair.py index d11f97aed..c0e1b427e 100644 --- a/eos/db/saveddata/databaseRepair.py +++ b/eos/db/saveddata/databaseRepair.py @@ -19,6 +19,7 @@ import sqlalchemy import logging +from eos.db.saveddata.queries import executeDatabaseQuery logger = logging.getLogger(__name__) @@ -39,10 +40,9 @@ class DatabaseCleanup: logger.debug("Running database cleanup for character skills.") results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM characterSkills " "WHERE characterID NOT IN (SELECT ID from characters)") - row = results.first() - if row and row['num'] > 0: + if row['num']: delete = saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)") logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) @@ -56,27 +56,22 @@ class DatabaseCleanup: # See issue #777 try: logger.debug("Running database cleanup for orphaned damage patterns attached to fits.") - results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID not in (select ID from damagePatterns)") + results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL") row = results.first() - if row and row['num'] > 0: + if row['num']: # Get Uniform damage pattern ID - uniform_results = saveddata_engine.execute("select ID from damagePatterns WHERE name = 'Uniform'") + query_return = executeDatabaseQuery(saveddata_engine, "SELECT ID FROM damagePatterns WHERE name = 'Uniform'") - uniform_result_count = 0 - uniform_damage_pattern_id = 0 - for uniform_result in uniform_results: - uniform_damage_pattern_id = uniform_result[0] - uniform_result_count += 1 - - if uniform_result_count == 0: + if query_return.__len__() <= 0: logger.error("Missing uniform damage pattern.") - elif uniform_result_count > 1: + elif query_return.__len__() > 1: logger.error("More than one uniform damage pattern found.") else: + uniform_damage_pattern_id = query_return[0]['ID'] update = saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " - "WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns)", + "WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL", uniform_damage_pattern_id) logger.error("Database corruption found. Cleaning up %d records.", update.rowcount) except sqlalchemy.exc.DatabaseError: @@ -87,27 +82,21 @@ class DatabaseCleanup: # Find orphaned character IDs. This solves an issue where the character doesn't exist, but fits reference the pattern. try: logger.debug("Running database cleanup for orphaned characters attached to fits.") - results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters)") - + results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE characterID NOT IN (SELECT ID FROM characters) OR characterID IS NULL") row = results.first() - if row and row['num'] > 0: + if row['num']: # Get All 5 character ID - all5_results = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'") + query_return = executeDatabaseQuery(saveddata_engine, "SELECT ID FROM characters WHERE name = 'All 5'") - all5_result_count = 0 - all5_id = 0 - for all5_result in all5_results: - all5_id = all5_result[0] - all5_result_count += 1 - - if all5_result_count == 0: + if query_return.__len__() <= 0: logger.error("Missing 'All 5' character.") - elif all5_result_count > 1: + elif query_return.__len__() > 1: logger.error("More than one 'All 5' character found.") else: - update = saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " - "WHERE damagePatternID not in (select ID from damagePatterns)", + all5_id = query_return[0]['ID'] + update = saveddata_engine.execute("UPDATE 'fits' SET 'characterID' = ? " + "WHERE characterID not in (select ID from characters) OR characterID IS NULL", all5_id) logger.error("Database corruption found. Cleaning up %d records.", update.rowcount) except sqlalchemy.exc.DatabaseError: diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index cb3033cc1..c18dff912 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -407,6 +407,22 @@ def getCrestCharacter(lookfor, eager=None): raise TypeError("Need integer or string as argument") return character +def executeDatabaseQuery(saveddata_engine, query): + # Executes a query against the database, and returns a dict instead of a resultsproxy + results = saveddata_engine.execute(query) + + return_list = [] + + for row in results: + internal_row = {} + for key in row._keymap: + idx = row._keymap[key][2] + internal_row.update({key: row._row[idx]}) + + return_list.append(internal_row) + + return return_list + def getOverrides(itemID, eager=None): if isinstance(itemID, int): return saveddata_session.query(Override).filter(Override.itemID == itemID).all() From a0ec13d2ed3242acbf9260e8353bda1cfe64dc29 Mon Sep 17 00:00:00 2001 From: blitzman Date: Mon, 2 Jan 2017 18:45:08 -0500 Subject: [PATCH 26/26] More tweaks --- eos/db/saveddata/databaseRepair.py | 31 +++++++++++++++--------------- eos/db/saveddata/queries.py | 16 --------------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/eos/db/saveddata/databaseRepair.py b/eos/db/saveddata/databaseRepair.py index c0e1b427e..ede0f2fde 100644 --- a/eos/db/saveddata/databaseRepair.py +++ b/eos/db/saveddata/databaseRepair.py @@ -19,7 +19,6 @@ import sqlalchemy import logging -from eos.db.saveddata.queries import executeDatabaseQuery logger = logging.getLogger(__name__) @@ -42,7 +41,7 @@ class DatabaseCleanup: "WHERE characterID NOT IN (SELECT ID from characters)") row = results.first() - if row['num']: + if row and row['num']: delete = saveddata_engine.execute("DELETE FROM characterSkills WHERE characterID NOT IN (SELECT ID from characters)") logger.error("Database corruption found. Cleaning up %d records.", delete.rowcount) @@ -60,19 +59,20 @@ class DatabaseCleanup: results = saveddata_engine.execute("SELECT COUNT(*) AS num FROM fits WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL") row = results.first() - if row['num']: + if row and row['num']: # Get Uniform damage pattern ID - query_return = executeDatabaseQuery(saveddata_engine, "SELECT ID FROM damagePatterns WHERE name = 'Uniform'") + query = saveddata_engine.execute("SELECT ID FROM damagePatterns WHERE name = 'Uniform'") + rows = query.fetchall() - if query_return.__len__() <= 0: + if len(rows) == 0: logger.error("Missing uniform damage pattern.") - elif query_return.__len__() > 1: + elif len(rows) > 1: logger.error("More than one uniform damage pattern found.") else: - uniform_damage_pattern_id = query_return[0]['ID'] + uniform_damage_pattern_id = rows[0]['ID'] update = saveddata_engine.execute("UPDATE 'fits' SET 'damagePatternID' = ? " - "WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL", - uniform_damage_pattern_id) + "WHERE damagePatternID NOT IN (SELECT ID FROM damagePatterns) OR damagePatternID IS NULL", + uniform_damage_pattern_id) logger.error("Database corruption found. Cleaning up %d records.", update.rowcount) except sqlalchemy.exc.DatabaseError: logger.error("Failed to connect to database.") @@ -87,17 +87,18 @@ class DatabaseCleanup: if row['num']: # Get All 5 character ID - query_return = executeDatabaseQuery(saveddata_engine, "SELECT ID FROM characters WHERE name = 'All 5'") + query = saveddata_engine.execute("SELECT ID FROM characters WHERE name = 'All 5'") + rows = query.fetchall() - if query_return.__len__() <= 0: + if len(rows) == 0: logger.error("Missing 'All 5' character.") - elif query_return.__len__() > 1: + elif len(rows) > 1: logger.error("More than one 'All 5' character found.") else: - all5_id = query_return[0]['ID'] + all5_id = rows[0]['ID'] update = saveddata_engine.execute("UPDATE 'fits' SET 'characterID' = ? " - "WHERE characterID not in (select ID from characters) OR characterID IS NULL", - all5_id) + "WHERE characterID not in (select ID from characters) OR characterID IS NULL", + all5_id) logger.error("Database corruption found. Cleaning up %d records.", update.rowcount) except sqlalchemy.exc.DatabaseError: logger.error("Failed to connect to database.") diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index c18dff912..cb3033cc1 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -407,22 +407,6 @@ def getCrestCharacter(lookfor, eager=None): raise TypeError("Need integer or string as argument") return character -def executeDatabaseQuery(saveddata_engine, query): - # Executes a query against the database, and returns a dict instead of a resultsproxy - results = saveddata_engine.execute(query) - - return_list = [] - - for row in results: - internal_row = {} - for key in row._keymap: - idx = row._keymap[key][2] - internal_row.update({key: row._row[idx]}) - - return_list.append(internal_row) - - return return_list - def getOverrides(itemID, eager=None): if isinstance(itemID, int): return saveddata_session.query(Override).filter(Override.itemID == itemID).all()