diff --git a/eos/db/saveddata/override.py b/eos/db/saveddata/override.py
new file mode 100644
index 000000000..2d5d74a47
--- /dev/null
+++ b/eos/db/saveddata/override.py
@@ -0,0 +1,31 @@
+#===============================================================================
+# 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, Float
+from sqlalchemy.orm import mapper
+
+from eos.db import saveddata_meta
+from eos.types import Override
+
+overrides_table = Table("overrides", saveddata_meta,
+ Column("itemID", Integer, primary_key=True, index = True),
+ Column("attrID", Integer, primary_key=True, index = True),
+ Column("value", Float, nullable = False))
+
+mapper(Override, overrides_table)
diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py
index 7b2853b6f..172093191 100644
--- a/eos/db/saveddata/queries.py
+++ b/eos/db/saveddata/queries.py
@@ -422,6 +422,12 @@ def getOverrides(itemID, eager=None):
else:
raise TypeError("Need integer as argument")
+def clearOverrides():
+ with sd_lock:
+ deleted_rows = saveddata_session.query(Override).delete()
+ commit()
+ return deleted_rows
+
def getAllOverrides(eager=None):
return saveddata_session.query(Override).all()
diff --git a/eos/saveddata/override.py b/eos/saveddata/override.py
new file mode 100644
index 000000000..b33875e89
--- /dev/null
+++ b/eos/saveddata/override.py
@@ -0,0 +1,59 @@
+#===============================================================================
+# Copyright (C) 2015 Ryan Holmes
+#
+# 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 eos.eqBase import EqBase
+from sqlalchemy.orm import validates, reconstructor
+import eos.db
+import logging
+
+logger = logging.getLogger(__name__)
+
+class Override(EqBase):
+
+ def __init__(self, item, attr, value):
+ self.itemID = item.ID
+ self.__item = item
+ self.attrID = attr.ID
+ self.__attr = attr
+ self.value = value
+
+ @reconstructor
+ def init(self):
+ self.__attr = None
+ self.__item = None
+
+ if self.attrID:
+ self.__attr = eos.db.getAttributeInfo(self.attrID)
+ if self.__attr is None:
+ logger.error("Attribute (id: %d) does not exist", self.attrID)
+ return
+
+ if self.itemID:
+ self.__item = eos.db.getItem(self.itemID)
+ if self.__item is None:
+ logger.error("Item (id: %d) does not exist", self.itemID)
+ return
+
+ @property
+ def attr(self):
+ return self.__attr
+
+ @property
+ def item(self):
+ return self.__item
diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py
index 4172985ef..44c63307b 100644
--- a/gui/propertyEditor.py
+++ b/gui/propertyEditor.py
@@ -7,6 +7,8 @@ import gui.display as d
import gui.globalEvents as GE
from gui.bitmapLoader import BitmapLoader
import service
+import csv
+import eos.db
import logging
@@ -22,26 +24,53 @@ class AttributeEditor( wx.Frame ):
self.SetIcon(i)
self.mainFrame = parent
- self.panel = panel = wx.Panel(self, wx.ID_ANY)
- topsizer = wx.BoxSizer(wx.HORIZONTAL)
- leftsizer = wx.BoxSizer(wx.VERTICAL)
+ menubar = wx.MenuBar()
+ fileMenu = wx.Menu()
+ fileImport = fileMenu.Append(wx.ID_ANY, 'Import', 'Import overrides')
+ fileExport = fileMenu.Append(wx.ID_ANY, 'Export', 'Import overrides')
+ fileClear = fileMenu.Append(wx.ID_ANY, 'Clear All', 'Clear all overrides')
+
+ menubar.Append(fileMenu, '&File')
+ self.SetMenuBar(menubar)
+
+ self.Bind(wx.EVT_MENU, self.OnImport, fileImport)
+ self.Bind(wx.EVT_MENU, self.OnExport, fileExport)
+ self.Bind(wx.EVT_MENU, self.OnClear, fileClear)
+
+
+ i = wx.IconFromBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui"))
+ self.SetIcon(i)
+
+ self.mainFrame = parent
+ self.panel = panel = wx.Panel(self, wx.ID_ANY)
+
+ mainSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ leftSizer = wx.BoxSizer(wx.VERTICAL)
leftPanel = wx.Panel(panel, wx.ID_ANY, style=wx.DOUBLE_BORDER if 'wxMSW' in wx.PlatformInfo else wx.SIMPLE_BORDER)
self.searchBox = SearchBox(leftPanel)
self.itemView = ItemView(leftPanel)
- leftsizer.Add(self.searchBox, 0, wx.EXPAND)
- leftsizer.Add(self.itemView, 1, wx.EXPAND)
+ leftSizer.Add(self.searchBox, 0, wx.EXPAND)
+ leftSizer.Add(self.itemView, 1, wx.EXPAND)
- leftPanel.SetSizer(leftsizer)
- topsizer.Add(leftPanel, 1, wx.ALL | wx.EXPAND, 5)
+ leftPanel.SetSizer(leftSizer)
+ mainSizer.Add(leftPanel, 1, wx.ALL | wx.EXPAND, 5)
+ rightSizer = wx.BoxSizer(wx.VERTICAL)
+ self.btnRemoveOverrides = wx.Button( panel, wx.ID_ANY, u"Remove Overides for Item", wx.DefaultPosition, wx.DefaultSize, 0 )
self.pg = AttributeGrid(panel)
- topsizer.Add(self.pg, 1, wx.ALL|wx.EXPAND, 5)
+ rightSizer.Add(self.pg, 1, wx.ALL|wx.EXPAND, 5)
+ rightSizer.Add(self.btnRemoveOverrides, 0, wx.ALL | wx.EXPAND, 5 )
+ self.btnRemoveOverrides.Bind(wx.EVT_BUTTON, self.pg.removeOverrides)
+ self.btnRemoveOverrides.Enable(False)
- panel.SetSizer(topsizer)
- topsizer.SetSizeHints(panel)
+ mainSizer.Add(rightSizer, 1, wx.EXPAND)
+
+ panel.SetSizer(mainSizer)
+ mainSizer.SetSizeHints(panel)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(panel, 1, wx.EXPAND)
@@ -56,6 +85,48 @@ class AttributeEditor( wx.Frame ):
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
self.Destroy()
+ def OnImport(self, event):
+ dlg = wx.FileDialog(self, "Import pyfa override file",
+ wildcard = "pyfa override file (*.csv)|*.csv",
+ style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
+ if (dlg.ShowModal() == wx.ID_OK):
+ path = dlg.GetPath()
+ with open(path, 'rb') as csvfile:
+ spamreader = csv.reader(csvfile)
+ for row in spamreader:
+ itemID, attrID, value = row
+ item = eos.db.getItem(int(itemID))
+ attr = eos.db.getAttributeInfo(int(attrID))
+ item.setOverride(attr, float(value))
+ self.itemView.updateItems(True)
+
+ def OnExport(self, event):
+ sMkt = service.Market.getInstance()
+ items = sMkt.getItemsWithOverrides()
+ defaultFile = "pyfa_overrides.csv"
+
+ dlg = wx.FileDialog(self, "Save Overrides As...",
+ wildcard = "pyfa overrides (*.csv)|*.csv",
+ style = wx.FD_SAVE,
+ defaultFile=defaultFile)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ path = dlg.GetPath()
+ with open(path, 'wb') as csvfile:
+ writer = csv.writer(csvfile)
+ for item in items:
+ for key, override in item.overrides.iteritems():
+ writer.writerow([item.ID, override.attrID, override.value])
+
+ def OnClear(self, event):
+ dlg = wx.MessageDialog(self,
+ "Are you sure you want to delete all overrides?",
+ "Confirm Delete", wx.YES | wx.NO | wx.ICON_EXCLAMATION)
+
+ if dlg.ShowModal() == wx.ID_YES:
+ eos.db.clearOverrides()
+ self.itemView.updateItems(True)
+ self.pg.Clear()
# This is literally a stripped down version of the market.
class ItemView(d.Display):
@@ -86,9 +157,12 @@ class ItemView(d.Display):
self.items = self.things
self.update(self.items)
- def updateItems(self):
+ def updateItems(self, updateDisplay=False):
sMkt = service.Market.getInstance()
self.things = sMkt.getItemsWithOverrides()
+ self.items = self.things
+ if updateDisplay:
+ self.update(self.things)
def scheduleSearch(self, event=None):
sMkt = service.Market.getInstance()
@@ -118,6 +192,8 @@ class AttributeGrid(wxpg.PropertyGrid):
self.itemView = parent.Parent.itemView
+ self.btn = parent.Parent.btnRemoveOverrides
+
self.Bind( wxpg.EVT_PG_CHANGED, self.OnPropGridChange )
self.Bind( wxpg.EVT_PG_SELECTED, self.OnPropGridSelect )
self.Bind( wxpg.EVT_PG_RIGHT_CLICK, self.OnPropGridRightClick )
@@ -127,6 +203,7 @@ class AttributeGrid(wxpg.PropertyGrid):
def itemActivated(self, event):
self.Clear()
+ self.btn.Enable(True)
sel = event.EventObject.GetFirstSelected()
self.item = item = self.itemView.items[sel]
@@ -140,9 +217,25 @@ class AttributeGrid(wxpg.PropertyGrid):
prop = wxpg.FloatProperty(key, value=default)
prop.SetClientData(item.attributes[key]) # set this so that we may access it later
- prop.SetHelpString("%s\n%s"%(item.attributes[key].displayName or key, "Default Value: %0.2f"%default))
+ prop.SetHelpString("%s\n%s"%(item.attributes[key].displayName or key, "Default Value: %0.3f"%default))
self.Append(prop)
+ def removeOverrides(self, event):
+ if self.item is None:
+ return
+
+ for _, x in self.item.overrides.items():
+ self.item.deleteOverride(x.attr)
+ self.itemView.updateItems(True)
+ self.ClearModifiedStatus()
+ self.itemView.Select(self.itemView.GetFirstSelected(), on=False)
+ self.Clear()
+
+ def Clear(self):
+ self.item = None
+ self.btn.Enable(False)
+ wxpg.PropertyGrid.Clear(self)
+
def OnPropGridChange(self, event):
p = event.GetProperty()
attr = p.GetClientData()