Merge branch 'overrides' into singularity
Conflicts: eos/db/__init__.py eos/db/saveddata/__init__.py eos/db/saveddata/queries.py gui/mainFrame.py gui/mainMenuBar.py
This commit is contained in:
@@ -68,14 +68,8 @@ from eos.db.gamedata import *
|
||||
from eos.db.saveddata import *
|
||||
|
||||
#Import queries
|
||||
from eos.db.gamedata.queries import getItem, searchItems, getVariations, getItemsByCategory, directAttributeRequest, \
|
||||
getMarketGroup, getGroup, getCategory, getAttributeInfo, getMetaData, getMetaGroup
|
||||
from eos.db.saveddata.queries import getUser, getCharacter, getFit, getFitsWithShip, countFitsWithShip, searchFits, \
|
||||
getCharacterList, getPrice, getDamagePatternList, getDamagePattern, \
|
||||
getFitList, getFleetList, getFleet, save, remove, commit, add, \
|
||||
getCharactersForUser, getMiscData, getSquadsIDsWithFitID, getWing, \
|
||||
getSquad, getBoosterFits, getProjectedFits, getTargetResistsList, getTargetResists,\
|
||||
clearPrices, countAllFits, getCrestCharacters, getCrestCharacter
|
||||
from eos.db.gamedata.queries import *
|
||||
from eos.db.saveddata.queries import *
|
||||
|
||||
#If using in memory saveddata, you'll want to reflect it so the data structure is good.
|
||||
if config.saveddata_connectionstring == "sqlite:///:memory:":
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
__all__ = ["character", "fit", "module", "user", "crest", "skill", "price",
|
||||
"booster", "drone", "implant", "fleet", "damagePattern",
|
||||
"miscData", "targetResists"]
|
||||
__all__ = [
|
||||
"character",
|
||||
"fit",
|
||||
"module",
|
||||
"user",
|
||||
"skill",
|
||||
"price",
|
||||
"booster",
|
||||
"drone",
|
||||
"implant",
|
||||
"fleet",
|
||||
"damagePattern",
|
||||
"miscData",
|
||||
"targetResists",
|
||||
"override",
|
||||
"crest"
|
||||
]
|
||||
|
||||
|
||||
31
eos/db/saveddata/override.py
Normal file
31
eos/db/saveddata/override.py
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
#===============================================================================
|
||||
|
||||
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)
|
||||
@@ -19,7 +19,8 @@
|
||||
|
||||
from eos.db.util import processEager, processWhere
|
||||
from eos.db import saveddata_session, sd_lock
|
||||
from eos.types import User, Character, Fit, Price, DamagePattern, Fleet, MiscData, Wing, Squad, TargetResists, CrestChar
|
||||
|
||||
from eos.types import User, Character, Fit, Price, DamagePattern, Fleet, MiscData, Wing, Squad, TargetResists, Override, CrestChar
|
||||
from eos.db.saveddata.fleet import squadmembers_table
|
||||
from eos.db.saveddata.fit import projectedFits_table
|
||||
from sqlalchemy.sql import and_
|
||||
@@ -182,7 +183,7 @@ def getFit(lookfor, eager=None):
|
||||
else:
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
fit = saveddata_session.query(Fit).options(*eager).filter(Fit.ID == fitID).first()
|
||||
fit = saveddata_session.query(Fit).options(*eager).filter(Fit.ID == lookfor).first()
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
|
||||
@@ -440,6 +441,21 @@ def getCrestCharacter(lookfor, eager=None):
|
||||
raise TypeError("Need integer or string as argument")
|
||||
return character
|
||||
|
||||
def getOverrides(itemID, eager=None):
|
||||
if isinstance(itemID, int):
|
||||
return saveddata_session.query(Override).filter(Override.itemID == itemID).all()
|
||||
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()
|
||||
|
||||
def removeInvalid(fits):
|
||||
invalids = [f for f in fits if f.isInvalid]
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from sqlalchemy.orm import reconstructor
|
||||
from eqBase import EqBase
|
||||
|
||||
import traceback
|
||||
import eos.db
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
@@ -168,7 +169,6 @@ class Item(EqBase):
|
||||
info = getattr(cls, "MOVE_ATTR_INFO", None)
|
||||
if info is None:
|
||||
cls.MOVE_ATTR_INFO = info = []
|
||||
import eos.db
|
||||
for id in cls.MOVE_ATTRS:
|
||||
info.append(eos.db.getAttributeInfo(id))
|
||||
|
||||
@@ -191,6 +191,7 @@ class Item(EqBase):
|
||||
self.__moved = False
|
||||
self.__offensive = None
|
||||
self.__assistive = None
|
||||
self.__overrides = None
|
||||
|
||||
@property
|
||||
def attributes(self):
|
||||
@@ -210,6 +211,32 @@ class Item(EqBase):
|
||||
|
||||
return False
|
||||
|
||||
@property
|
||||
def overrides(self):
|
||||
if self.__overrides is None:
|
||||
self.__overrides = {}
|
||||
overrides = eos.db.getOverrides(self.ID)
|
||||
for x in overrides:
|
||||
if x.attr.name in self.__attributes:
|
||||
self.__overrides[x.attr.name] = x
|
||||
|
||||
return self.__overrides
|
||||
|
||||
def setOverride(self, attr, value):
|
||||
from eos.saveddata.override import Override
|
||||
if attr.name in self.__overrides:
|
||||
override = self.__overrides.get(attr.name)
|
||||
override.value = value
|
||||
else:
|
||||
override = Override(self, attr, value)
|
||||
self.__overrides[attr.name] = override
|
||||
eos.db.save(override)
|
||||
|
||||
def deleteOverride(self, attr):
|
||||
override = self.__overrides.pop(attr.name, None)
|
||||
eos.db.saveddata_session.delete(override)
|
||||
eos.db.commit()
|
||||
|
||||
@property
|
||||
def requiredSkills(self):
|
||||
if self.__requiredSkills is None:
|
||||
@@ -345,6 +372,12 @@ class Item(EqBase):
|
||||
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return "Item(ID={}, name={}) at {}".format(
|
||||
self.ID, self.name, hex(id(self))
|
||||
)
|
||||
|
||||
|
||||
class MetaData(EqBase):
|
||||
pass
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ class ChargeAttrShortcut(object):
|
||||
return None
|
||||
|
||||
class ModifiedAttributeDict(collections.MutableMapping):
|
||||
|
||||
OVERRIDES = False
|
||||
|
||||
class CalculationPlaceholder():
|
||||
pass
|
||||
|
||||
@@ -51,6 +54,8 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
self.__modified = {}
|
||||
# Affected by entities
|
||||
self.__affectedBy = {}
|
||||
# Overrides
|
||||
self.__overrides = {}
|
||||
# Dictionaries for various value modification types
|
||||
self.__forced = {}
|
||||
self.__preAssigns = {}
|
||||
@@ -79,6 +84,14 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
self.__original = val
|
||||
self.__modified.clear()
|
||||
|
||||
@property
|
||||
def overrides(self):
|
||||
return self.__overrides
|
||||
|
||||
@overrides.setter
|
||||
def overrides(self, val):
|
||||
self.__overrides = val
|
||||
|
||||
def __getitem__(self, key):
|
||||
# Check if we have final calculated value
|
||||
if key in self.__modified:
|
||||
@@ -99,6 +112,8 @@ class ModifiedAttributeDict(collections.MutableMapping):
|
||||
del self.__intermediary[key]
|
||||
|
||||
def getOriginal(self, key):
|
||||
if self.OVERRIDES and key in self.__overrides:
|
||||
return self.__overrides.get(key).value
|
||||
val = self.__original.get(key)
|
||||
if val is None:
|
||||
return None
|
||||
|
||||
@@ -58,6 +58,7 @@ class Booster(HandledItem, ItemAttrShortcut):
|
||||
self.__sideEffects = []
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
self.__slot = self.__calculateSlot(self.__item)
|
||||
|
||||
for effect in self.__item.effects.itervalues():
|
||||
|
||||
@@ -34,6 +34,7 @@ class Cargo(HandledItem, ItemAttrShortcut):
|
||||
self.amount = 0
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = item.attributes
|
||||
self.__itemModifiedAttributes.overrides = item.overrides
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
@@ -48,6 +49,7 @@ class Cargo(HandledItem, ItemAttrShortcut):
|
||||
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
|
||||
@property
|
||||
def itemModifiedAttributes(self):
|
||||
|
||||
@@ -67,6 +67,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
self.__miningyield = None
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
|
||||
self.__chargeModifiedAttributes = ModifiedAttributeDict()
|
||||
chargeID = self.getModifiedItemAttr("entityMissileTypeID")
|
||||
@@ -74,6 +75,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
charge = eos.db.getItem(int(chargeID))
|
||||
self.__charge = charge
|
||||
self.__chargeModifiedAttributes.original = charge.attributes
|
||||
self.__chargeModifiedAttributes.overrides = charge.overrides
|
||||
|
||||
@property
|
||||
def itemModifiedAttributes(self):
|
||||
|
||||
@@ -56,6 +56,7 @@ class Implant(HandledItem, ItemAttrShortcut):
|
||||
""" Build object. Assumes proper and valid item already set """
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
self.__slot = self.__calculateSlot(self.__item)
|
||||
|
||||
@property
|
||||
|
||||
@@ -30,6 +30,7 @@ class Mode(ItemAttrShortcut, HandledItem):
|
||||
self.__item = item
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = self.item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.item.overrides
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
|
||||
@@ -113,10 +113,13 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
|
||||
if self.__item:
|
||||
self.__itemModifiedAttributes.original = self.__item.attributes
|
||||
self.__itemModifiedAttributes.overrides = self.__item.overrides
|
||||
self.__hardpoint = self.__calculateHardpoint(self.__item)
|
||||
self.__slot = self.__calculateSlot(self.__item)
|
||||
if self.__charge:
|
||||
self.__chargeModifiedAttributes.original = self.__charge.attributes
|
||||
self.__chargeModifiedAttributes.overrides = self.__charge.overrides
|
||||
|
||||
|
||||
@classmethod
|
||||
def buildEmpty(cls, slot):
|
||||
@@ -283,9 +286,11 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
|
||||
if charge is not None:
|
||||
self.chargeID = charge.ID
|
||||
self.__chargeModifiedAttributes.original = charge.attributes
|
||||
self.__chargeModifiedAttributes.overrides = charge.overrides
|
||||
else:
|
||||
self.chargeID = None
|
||||
self.__chargeModifiedAttributes.original = None
|
||||
self.__chargeModifiedAttributes.overrides = {}
|
||||
|
||||
self.__itemModifiedAttributes.clear()
|
||||
|
||||
|
||||
59
eos/saveddata/override.py
Normal file
59
eos/saveddata/override.py
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
#===============================================================================
|
||||
|
||||
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
|
||||
@@ -51,6 +51,7 @@ class Ship(ItemAttrShortcut, HandledItem):
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__itemModifiedAttributes.original = dict(self.item.attributes)
|
||||
self.__itemModifiedAttributes.original.update(self.EXTRA_ATTRIBUTES)
|
||||
self.__itemModifiedAttributes.overrides = self.item.overrides
|
||||
|
||||
self.commandBonus = 0
|
||||
|
||||
|
||||
@@ -36,4 +36,5 @@ from eos.saveddata.fit import Fit
|
||||
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
|
||||
import eos.db
|
||||
|
||||
@@ -11,7 +11,7 @@ TextTyped, EVT_TEXT = wx.lib.newevent.NewEvent()
|
||||
|
||||
class PFSearchBox(wx.Window):
|
||||
def __init__(self, parent, id = wx.ID_ANY, value = "", pos = wx.DefaultPosition, size = wx.Size(-1,24), style = 0):
|
||||
wx.Window.__init__(self, parent, id, pos, size, style = 0)
|
||||
wx.Window.__init__(self, parent, id, pos, size, style = style)
|
||||
|
||||
self.isSearchButtonVisible = False
|
||||
self.isCancelButtonVisible = False
|
||||
|
||||
@@ -35,9 +35,6 @@ class CharacterEditor(wx.Frame):
|
||||
size=wx.Size(641, 600), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.TAB_TRAVERSAL)
|
||||
|
||||
i = wx.IconFromBitmap(BitmapLoader.getBitmap("character_small", "gui"))
|
||||
|
||||
self.mainFrame = parent
|
||||
|
||||
self.SetIcon(i)
|
||||
|
||||
self.disableWin= wx.WindowDisabler(self)
|
||||
|
||||
@@ -55,7 +55,12 @@ from gui.copySelectDialog import CopySelectDialog
|
||||
from gui.utils.clipboard import toClipboard, fromClipboard
|
||||
from gui.fleetBrowser import FleetBrowser
|
||||
from gui.updateDialog import UpdateDialog
|
||||
from gui.propertyEditor import AttributeEditor
|
||||
from gui.builtinViews import *
|
||||
|
||||
# import this to access override setting
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict
|
||||
|
||||
from time import gmtime, strftime
|
||||
|
||||
from service.crest import CrestModes
|
||||
@@ -340,6 +345,10 @@ class MainFrame(wx.Frame):
|
||||
dlg=CharacterEditor(self)
|
||||
dlg.Show()
|
||||
|
||||
def showAttrEditor(self, event):
|
||||
dlg=AttributeEditor(self)
|
||||
dlg.Show()
|
||||
|
||||
def showTargetResistsEditor(self, event):
|
||||
dlg=ResistsEditorDlg(self)
|
||||
dlg.ShowModal()
|
||||
@@ -427,6 +436,7 @@ class MainFrame(wx.Frame):
|
||||
self.Bind(wx.EVT_MENU, self.saveCharAs, id = menuBar.saveCharAsId)
|
||||
# Save current character
|
||||
self.Bind(wx.EVT_MENU, self.revertChar, id = menuBar.revertCharId)
|
||||
|
||||
# Browse fittings
|
||||
self.Bind(wx.EVT_MENU, self.eveFittings, id = menuBar.eveFittingsId)
|
||||
# Export to EVE
|
||||
@@ -434,6 +444,11 @@ class MainFrame(wx.Frame):
|
||||
# Login to EVE
|
||||
self.Bind(wx.EVT_MENU, self.ssoLogin, id = menuBar.ssoLoginId)
|
||||
|
||||
# Open attribute editor
|
||||
self.Bind(wx.EVT_MENU, self.showAttrEditor, id = menuBar.attrEditorId)
|
||||
# Toggle Overrides
|
||||
self.Bind(wx.EVT_MENU, self.toggleOverrides, id = menuBar.toggleOverridesId)
|
||||
|
||||
#Clipboard exports
|
||||
self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY)
|
||||
|
||||
@@ -534,6 +549,12 @@ class MainFrame(wx.Frame):
|
||||
dlg=ExportToEve(self)
|
||||
dlg.Show()
|
||||
|
||||
def toggleOverrides(self, event):
|
||||
ModifiedAttributeDict.OVERRIDES = not ModifiedAttributeDict.OVERRIDES
|
||||
wx.PostEvent(self, GE.FitChanged(fitID=self.getActiveFit()))
|
||||
menu = self.GetMenuBar()
|
||||
menu.SetLabel(menu.toggleOverridesId, "Turn Overrides Off" if ModifiedAttributeDict.OVERRIDES else "Turn Overrides On")
|
||||
|
||||
def saveChar(self, event):
|
||||
sChr = service.Character.getInstance()
|
||||
charID = self.charSelection.getActiveCharacter()
|
||||
|
||||
@@ -47,6 +47,8 @@ class MainMenuBar(wx.MenuBar):
|
||||
self.eveFittingsId = wx.NewId()
|
||||
self.exportToEveId = wx.NewId()
|
||||
self.ssoLoginId = wx.NewId()
|
||||
self.attrEditorId = wx.NewId()
|
||||
self.toggleOverridesId = wx.NewId()
|
||||
|
||||
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
|
||||
|
||||
@@ -87,6 +89,9 @@ class MainMenuBar(wx.MenuBar):
|
||||
editMenu.Append(self.saveCharId, "Save Character")
|
||||
editMenu.Append(self.saveCharAsId, "Save Character As...")
|
||||
editMenu.Append(self.revertCharId, "Revert Character")
|
||||
editMenu.AppendSeparator()
|
||||
editMenu.Append(self.toggleOverridesId, "Turn Overrides On")
|
||||
|
||||
# Character menu
|
||||
windowMenu = wx.Menu()
|
||||
self.Append(windowMenu, "&Window")
|
||||
@@ -125,6 +130,10 @@ class MainMenuBar(wx.MenuBar):
|
||||
self.Enable(self.eveFittingsId, False)
|
||||
self.Enable(self.exportToEveId, False)
|
||||
|
||||
attrItem = wx.MenuItem(windowMenu, self.attrEditorId, "Attribute Overrides\tCTRL+A")
|
||||
attrItem.SetBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui"))
|
||||
windowMenu.AppendItem(attrItem)
|
||||
|
||||
# Help menu
|
||||
helpMenu = wx.Menu()
|
||||
self.Append(helpMenu, "&Help")
|
||||
|
||||
@@ -103,8 +103,8 @@ class MarketBrowser(wx.Panel):
|
||||
self.marketView.jump(item)
|
||||
|
||||
class SearchBox(SBox.PFSearchBox):
|
||||
def __init__(self, parent):
|
||||
SBox.PFSearchBox.__init__(self, parent)
|
||||
def __init__(self, parent, **kwargs):
|
||||
SBox.PFSearchBox.__init__(self, parent, **kwargs)
|
||||
cancelBitmap = BitmapLoader.getBitmap("fit_delete_small","gui")
|
||||
searchBitmap = BitmapLoader.getBitmap("fsearch_small","gui")
|
||||
self.SetSearchBitmap(searchBitmap)
|
||||
|
||||
264
gui/propertyEditor.py
Normal file
264
gui/propertyEditor.py
Normal file
@@ -0,0 +1,264 @@
|
||||
import wx
|
||||
import wx.propgrid as wxpg
|
||||
|
||||
import gui.PFSearchBox as SBox
|
||||
from gui.marketBrowser import SearchBox
|
||||
import gui.display as d
|
||||
import gui.globalEvents as GE
|
||||
from gui.bitmapLoader import BitmapLoader
|
||||
import service
|
||||
import csv
|
||||
import eos.db
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AttributeEditor( wx.Frame ):
|
||||
|
||||
def __init__( self, parent ):
|
||||
wx.Frame.__init__(self, parent, wx.ID_ANY, title="Attribute Editor", pos=wx.DefaultPosition,
|
||||
size=wx.Size(650, 600), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.TAB_TRAVERSAL)
|
||||
|
||||
i = wx.IconFromBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui"))
|
||||
self.SetIcon(i)
|
||||
|
||||
self.mainFrame = parent
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
mainSizer.Add(rightSizer, 1, wx.EXPAND)
|
||||
|
||||
panel.SetSizer(mainSizer)
|
||||
mainSizer.SetSizeHints(panel)
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(panel, 1, wx.EXPAND)
|
||||
self.SetSizer(sizer)
|
||||
self.SetAutoLayout(True)
|
||||
|
||||
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
||||
|
||||
def OnClose(self, event):
|
||||
fitID = self.mainFrame.getActiveFit()
|
||||
if fitID is not None:
|
||||
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:
|
||||
sMkt = service.Market.getInstance()
|
||||
items = sMkt.getItemsWithOverrides()
|
||||
# We can't just delete overrides, as loaded items will still have
|
||||
# them assigned. Deleting them from the database won't propagate
|
||||
# them due to the eve/user database disconnect. We must loop through
|
||||
# all items that have overrides and remove them
|
||||
for item in items:
|
||||
for _, x in item.overrides.items():
|
||||
item.deleteOverride(x.attr)
|
||||
self.itemView.updateItems(True)
|
||||
self.pg.Clear()
|
||||
|
||||
# This is literally a stripped down version of the market.
|
||||
class ItemView(d.Display):
|
||||
DEFAULT_COLS = ["Base Icon",
|
||||
"Base Name",
|
||||
"attr:power,,,True",
|
||||
"attr:cpu,,,True"]
|
||||
|
||||
def __init__(self, parent):
|
||||
d.Display.__init__(self, parent)
|
||||
sMkt = service.Market.getInstance()
|
||||
|
||||
self.things = sMkt.getItemsWithOverrides()
|
||||
self.items = self.things
|
||||
|
||||
self.searchBox = parent.Parent.Parent.searchBox
|
||||
# Bind search actions
|
||||
self.searchBox.Bind(SBox.EVT_TEXT_ENTER, self.scheduleSearch)
|
||||
self.searchBox.Bind(SBox.EVT_SEARCH_BTN, self.scheduleSearch)
|
||||
self.searchBox.Bind(SBox.EVT_CANCEL_BTN, self.clearSearch)
|
||||
self.searchBox.Bind(SBox.EVT_TEXT, self.scheduleSearch)
|
||||
|
||||
self.update(self.items)
|
||||
|
||||
def clearSearch(self, event=None):
|
||||
if event:
|
||||
self.searchBox.Clear()
|
||||
self.items = self.things
|
||||
self.update(self.items)
|
||||
|
||||
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()
|
||||
|
||||
search = self.searchBox.GetLineText(0)
|
||||
# Make sure we do not count wildcard as search symbol
|
||||
realsearch = search.replace("*", "")
|
||||
# Show nothing if query is too short
|
||||
if len(realsearch) < 3:
|
||||
self.clearSearch()
|
||||
return
|
||||
|
||||
sMkt.searchItems(search, self.populateSearch, False)
|
||||
|
||||
def populateSearch(self, items):
|
||||
self.items = list(items)
|
||||
self.update(items)
|
||||
|
||||
|
||||
class AttributeGrid(wxpg.PropertyGrid):
|
||||
|
||||
def __init__(self, parent):
|
||||
wxpg.PropertyGrid.__init__(self, parent, style=wxpg.PG_HIDE_MARGIN|wxpg.PG_HIDE_CATEGORIES|wxpg.PG_BOLD_MODIFIED|wxpg.PG_TOOLTIPS)
|
||||
self.SetExtraStyle(wxpg.PG_EX_HELP_AS_TOOLTIPS)
|
||||
|
||||
self.item = None
|
||||
|
||||
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 )
|
||||
|
||||
self.itemView.Bind(wx.EVT_LIST_ITEM_SELECTED, self.itemActivated)
|
||||
self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated)
|
||||
|
||||
def itemActivated(self, event):
|
||||
self.Clear()
|
||||
self.btn.Enable(True)
|
||||
sel = event.EventObject.GetFirstSelected()
|
||||
self.item = item = self.itemView.items[sel]
|
||||
|
||||
for key in sorted(item.attributes.keys()):
|
||||
override = item.overrides.get(key, None)
|
||||
default = item.attributes[key].value
|
||||
if override and override.value != default:
|
||||
prop = wxpg.FloatProperty(key, value=override.value)
|
||||
prop.SetModifiedStatus(True)
|
||||
else:
|
||||
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.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()
|
||||
if p.GetValue() == attr.value:
|
||||
self.item.deleteOverride(attr)
|
||||
p.SetModifiedStatus(False)
|
||||
else:
|
||||
self.item.setOverride(attr, p.GetValue())
|
||||
|
||||
self.itemView.updateItems()
|
||||
|
||||
logger.debug('%s changed to "%s"' % (p.GetName(), p.GetValueAsString()))
|
||||
|
||||
def OnPropGridSelect(self, event):
|
||||
pass
|
||||
|
||||
def OnPropGridRightClick(self, event):
|
||||
pass
|
||||
@@ -116,12 +116,15 @@ class SearchWorkerThread(threading.Thread):
|
||||
while self.searchRequest is None:
|
||||
cv.wait()
|
||||
|
||||
request, callback = self.searchRequest
|
||||
request, callback, filterOn = self.searchRequest
|
||||
self.searchRequest = None
|
||||
cv.release()
|
||||
sMkt = Market.getInstance()
|
||||
# Rely on category data provided by eos as we don't hardcode them much in service
|
||||
filter = eos.types.Category.name.in_(sMkt.SEARCH_CATEGORIES)
|
||||
if filterOn:
|
||||
# Rely on category data provided by eos as we don't hardcode them much in service
|
||||
filter = eos.types.Category.name.in_(sMkt.SEARCH_CATEGORIES)
|
||||
else:
|
||||
filter=None
|
||||
results = eos.db.searchItems(request, where=filter,
|
||||
join=(eos.types.Item.group, eos.types.Group.category),
|
||||
eager=("icon", "group.category", "metaGroup", "metaGroup.parent"))
|
||||
@@ -133,9 +136,9 @@ class SearchWorkerThread(threading.Thread):
|
||||
items.add(item)
|
||||
wx.CallAfter(callback, items)
|
||||
|
||||
def scheduleSearch(self, text, callback):
|
||||
def scheduleSearch(self, text, callback, filterOn=True):
|
||||
self.cv.acquire()
|
||||
self.searchRequest = (text, callback)
|
||||
self.searchRequest = (text, callback, filterOn)
|
||||
self.cv.notify()
|
||||
self.cv.release()
|
||||
|
||||
@@ -665,9 +668,16 @@ class Market():
|
||||
ships.add(item)
|
||||
return ships
|
||||
|
||||
def searchItems(self, name, callback):
|
||||
def searchItems(self, name, callback, filterOn=True):
|
||||
"""Find items according to given text pattern"""
|
||||
self.searchWorkerThread.scheduleSearch(name, callback)
|
||||
self.searchWorkerThread.scheduleSearch(name, callback, filterOn)
|
||||
|
||||
def getItemsWithOverrides(self):
|
||||
overrides = eos.db.getAllOverrides()
|
||||
items = set()
|
||||
for x in overrides:
|
||||
items.add(x.item)
|
||||
return list(items)
|
||||
|
||||
def directAttrRequest(self, items, attribs):
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user