From 5b5fdd97d6d5d1b4efe5a0396964a2545fd8d1fa Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 25 Sep 2015 13:59:37 -0400 Subject: [PATCH 01/30] Enable and fix market tree for character implant view --- gui/characterEditor.py | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 6bf81619a..1b96a9c83 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -92,7 +92,7 @@ class CharacterEditor(wx.Frame): self.viewsNBContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) self.sview = SkillTreeView(self.viewsNBContainer) - #self.iview = ImplantsTreeView(self.viewsNBContainer) + self.iview = ImplantsTreeView(self.viewsNBContainer) #======================================================================= # RC2 #self.iview.Show(False) @@ -103,7 +103,7 @@ class CharacterEditor(wx.Frame): #======================================================================= # Disabled for RC2 - # self.viewsNBContainer.AddPage(self.iview, "Implants") + self.viewsNBContainer.AddPage(self.iview, "Implants") #======================================================================= self.viewsNBContainer.AddPage(self.aview, "API") @@ -536,7 +536,7 @@ class ImplantsTreeView (wx.Panel): #Bind the change of a character* self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged) - self.Enable(False) + #self.Enable(False) self.Layout() def update(self, implants): @@ -558,35 +558,34 @@ class ImplantsTreeView (wx.Panel): def expandLookup(self, event): tree = self.availableImplantsTree - root = event.Item - child, cookie = tree.GetFirstChild(root) + sMkt = service.Market.getInstance() + parent = event.Item + child, _ = tree.GetFirstChild(parent) text = tree.GetItemText(child) if text == "dummy" or text == "itemdummy": - sMkt = service.Market.getInstance() - #A DUMMY! Keeeel!!! EBUL DUMMY MUST DIAF! tree.Delete(child) + # if the dummy item is a market group, replace with actual market groups if text == "dummy": #Add 'real stoof!' instead - for id, name, iconFile, more in sMkt.getChildren(tree.GetPyData(root)): - iconId = self.addMarketViewImage(iconFile) - childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id)) - if more: + currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent), eager="children") + for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): + iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(childMktGrp)) + childId = tree.AppendItem(parent, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) + if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: tree.AppendItem(childId, "dummy") else: tree.AppendItem(childId, "itemdummy") + # replace dummy with actual items if text == "itemdummy": - sMkt = service.Market.getInstance() - data, usedMetas = sMkt.getVariations(tree.GetPyData(root)) - for item in data: - id = item.ID - name = item.name - iconFile = item.icon.iconFile - iconId = self.addMarketViewImage(iconFile) - tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id)) + currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent)) + items = sMkt.getItemsByMarketGroup(currentMktGrp) + for item in items: + iconId = self.addMarketViewImage(item.icon.iconFile) + tree.AppendItem(parent, item.name, iconId, data=wx.TreeItemData(item.ID)) - tree.SortChildren(root) + tree.SortChildren(parent) def addImplant(self, event): root = self.availableImplantsTree.GetSelection() From d0ec17febabf19086c91cc9923ea3067582426fb Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 25 Sep 2015 14:13:33 -0400 Subject: [PATCH 02/30] Some syntax --- eos/db/saveddata/character.py | 24 ++++++++++++++++++------ eos/saveddata/character.py | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/eos/db/saveddata/character.py b/eos/db/saveddata/character.py index 6600d975e..ee4e9de08 100644 --- a/eos/db/saveddata/character.py +++ b/eos/db/saveddata/character.py @@ -36,9 +36,21 @@ characters_table = Table("characters", saveddata_meta, Column("ownerID", ForeignKey("users.ID"), nullable = True)) mapper(Character, characters_table, - properties = {"_Character__owner" : relation(User, backref = "characters"), - "_Character__skills" : relation(Skill, backref="character", cascade = "all,delete-orphan"), - "_Character__implants" : relation(Implant, collection_class = HandledImplantBoosterList, cascade='all,delete-orphan', single_parent=True, - primaryjoin = charImplants_table.c.charID == characters_table.c.ID, - secondaryjoin = charImplants_table.c.implantID == Implant.ID, - secondary = charImplants_table),}) + properties = { + "_Character__owner": relation( + User, + backref = "characters"), + "_Character__skills": relation( + Skill, + backref="character", + cascade = "all,delete-orphan"), + "_Character__implants": relation( + Implant, + collection_class = HandledImplantBoosterList, + cascade='all,delete-orphan', + single_parent=True, + primaryjoin = charImplants_table.c.charID == characters_table.c.ID, + secondaryjoin = charImplants_table.c.implantID == Implant.ID, + secondary = charImplants_table), + } +) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 06559b8eb..9e08217ff 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -20,7 +20,7 @@ from sqlalchemy.orm import validates, reconstructor -from eos.effectHandlerHelpers import HandledItem +from eos.effectHandlerHelpers import HandledItem, HandledImplantBoosterList import eos.db import eos @@ -98,7 +98,7 @@ class Character(object): for item in self.getSkillList(): self.addSkill(Skill(item.ID, self.defaultLevel)) - self.__implants = eos.saveddata.fit.HandledImplantBoosterList() + self.__implants = HandledImplantBoosterList() self.apiKey = None @reconstructor From dc55dbdf3637fa823bda79279e55f43e0066880a Mon Sep 17 00:00:00 2001 From: blitzmann Date: Fri, 25 Sep 2015 14:46:41 -0400 Subject: [PATCH 03/30] Polish some events --- gui/characterEditor.py | 34 ++++++++++++++++++++++++++-------- service/character.py | 5 ++--- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 1b96a9c83..c64470c5d 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -504,14 +504,20 @@ class ImplantsTreeView (wx.Panel): availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND) buttonSizer = wx.BoxSizer(wx.VERTICAL) - pmainSizer.Add(buttonSizer, 0, wx.TOP, 5) - + buttonSizer.AddStretchSpacer() self.btnAdd = GenBitmapButton(self, wx.ID_ADD, bitmapLoader.getBitmap("fit_add_small", "icons"), style = wx.BORDER_NONE) buttonSizer.Add(self.btnAdd, 0) self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, bitmapLoader.getBitmap("fit_delete_small", "icons"), style = wx.BORDER_NONE) buttonSizer.Add(self.btnRemove, 0) + buttonSizer.AddStretchSpacer() - self.pluggedImplantsTree = AvailableImplantsView(self, style=wx.LC_SINGLE_SEL) + pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 5) + + self.pluggedImplantsTree = AvailableImplantsView(self) + + sChar = service.Character.getInstance() + charID = self.Parent.Parent.getActiveCharacter() + self.update(sChar.getImplants(charID)) pmainSizer.Add(self.pluggedImplantsTree, 1, wx.ALL | wx.EXPAND, 5) @@ -529,6 +535,7 @@ class ImplantsTreeView (wx.Panel): #Bind the event to replace dummies by real data self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) + self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.addImplant) #Bind add & remove buttons self.btnAdd.Bind(wx.EVT_BUTTON, self.addImplant) @@ -562,6 +569,7 @@ class ImplantsTreeView (wx.Panel): parent = event.Item child, _ = tree.GetFirstChild(parent) text = tree.GetItemText(child) + if text == "dummy" or text == "itemdummy": tree.Delete(child) @@ -600,21 +608,31 @@ class ImplantsTreeView (wx.Panel): itemID = self.availableImplantsTree.GetPyData(root) sChar.addImplant(charID, itemID) self.update(sChar.getImplants(charID)) + else: + event.Skip() def removeImplant(self, event): pos = self.pluggedImplantsTree.GetFirstSelected() if pos != -1: sChar = service.Character.getInstance() charID = self.Parent.Parent.getActiveCharacter() - sChar.removeImplant(charID, self.implants[pos].slot) + sChar.removeImplant(charID, self.implants[pos]) self.update(sChar.getImplants(charID)) class AvailableImplantsView(d.Display): - DEFAULT_COLS = ["Base Name", - "attr:implantness"] + DEFAULT_COLS = ["attr:implantness", + "Base Icon", + "Base Name"] - def __init__(self, parent, style): - d.Display.__init__(self, parent, style=style) + def __init__(self, parent): + d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL) + + self.Bind(wx.EVT_LEFT_DCLICK, parent.removeImplant) + + #if "__WXGTK__" in wx.PlatformInfo: + # self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) + #else: + # self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) class APIView (wx.Panel): def __init__(self, parent): diff --git a/service/character.py b/service/character.py index 83e50bbaa..dccd5c9f7 100644 --- a/service/character.py +++ b/service/character.py @@ -344,12 +344,11 @@ class Character(object): def addImplant(self, charID, itemID): char = eos.db.getCharacter(charID) implant = eos.types.Implant(eos.db.getItem(itemID)) - char.implants.freeSlot(implant.slot) char.implants.append(implant) - def removeImplant(self, charID, slot): + def removeImplant(self, charID, implant): char = eos.db.getCharacter(charID) - char.implants.freeSlot(slot) + char.implants.remove(implant) def getImplants(self, charID): char = eos.db.getCharacter(charID) From ca34d7cced3e1cb7f0152cd4bccc60a5c5e2549e Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 10 Nov 2015 02:06:44 -0500 Subject: [PATCH 04/30] Differentiate between character implants and fits --- eos/db/saveddata/character.py | 1 + gui/builtinViewColumns/baseIcon.py | 7 ++++++- gui/builtinViewColumns/baseName.py | 4 +++- gui/implantView.py | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/eos/db/saveddata/character.py b/eos/db/saveddata/character.py index ee4e9de08..f3d4d1e13 100644 --- a/eos/db/saveddata/character.py +++ b/eos/db/saveddata/character.py @@ -48,6 +48,7 @@ mapper(Character, characters_table, Implant, collection_class = HandledImplantBoosterList, cascade='all,delete-orphan', + backref='character', single_parent=True, primaryjoin = charImplants_table.c.charID == characters_table.c.ID, secondaryjoin = charImplants_table.c.implantID == Implant.ID, diff --git a/gui/builtinViewColumns/baseIcon.py b/gui/builtinViewColumns/baseIcon.py index 30cbf6fb2..868c73a21 100644 --- a/gui/builtinViewColumns/baseIcon.py +++ b/gui/builtinViewColumns/baseIcon.py @@ -2,7 +2,7 @@ from gui import builtinViewColumns from gui.viewColumn import ViewColumn from gui.bitmapLoader import BitmapLoader import wx -from eos.types import Drone, Fit, Module, Slot, Rack +from eos.types import Drone, Fit, Module, Slot, Rack, Implant class BaseIcon(ViewColumn): name = "Base Icon" @@ -21,6 +21,11 @@ class BaseIcon(ViewColumn): return self.shipImage if isinstance(stuff, Rack): return -1 + if isinstance(stuff, Implant): + if stuff.character: # if it has a character as it's parent + return self.fittingView.imageList.GetImageIndex("character_small", "gui") + else: + return self.shipImage if isinstance(stuff, Module): if stuff.isEmpty: return self.fittingView.imageList.GetImageIndex("slot_%s_small" % Slot.getName(stuff.slot).lower(), "gui") diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index e5f59a9e2..edb45044e 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -22,7 +22,7 @@ from gui.viewColumn import ViewColumn import gui.mainFrame import wx -from eos.types import Drone, Cargo, Fit, Module, Slot, Rack +from eos.types import Drone, Cargo, Fit, Module, Slot, Rack, Implant import service class BaseName(ViewColumn): @@ -61,6 +61,8 @@ class BaseName(ViewColumn): return "%s Slot" % Slot.getName(stuff.slot).capitalize() else: return stuff.item.name + elif isinstance(stuff, Implant): + return stuff.item.name else: item = getattr(stuff, "item", stuff) diff --git a/gui/implantView.py b/gui/implantView.py index d87838113..1672b5b83 100644 --- a/gui/implantView.py +++ b/gui/implantView.py @@ -27,6 +27,7 @@ import globalEvents as GE class ImplantView(d.Display): DEFAULT_COLS = ["State", "attr:implantness", + "Base Icon", "Base Name"] def __init__(self, parent): @@ -65,7 +66,7 @@ class ImplantView(d.Display): fit = sFit.getFit(event.fitID) self.original = fit.implants if fit is not None else None - self.implants = stuff = fit.implants if fit is not None else None + self.implants = stuff = fit.appliedImplants if fit is not None else None if stuff is not None: stuff.sort(key=lambda implant: implant.slot) if event.fitID != self.lastFitId: From b5cf835959cf4dcfa2da4726dcaf52e528a3d6e6 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 13 Mar 2016 14:48:43 -0400 Subject: [PATCH 05/30] Add a toggle between fit-specific implants and character implants. This avoids a lot of the problems of how to mix these two sources of implants. Character implants cannot change stage. --- eos/db/saveddata/fit.py | 3 +- eos/saveddata/fit.py | 27 ++++++++++------ eos/types.py | 2 +- gui/builtinViewColumns/state.py | 5 ++- gui/implantView.py | 55 +++++++++++++++++++++++++++++++-- 5 files changed, 77 insertions(+), 15 deletions(-) diff --git a/eos/db/saveddata/fit.py b/eos/db/saveddata/fit.py index a40d8c7fb..27defd2ab 100644 --- a/eos/db/saveddata/fit.py +++ b/eos/db/saveddata/fit.py @@ -28,7 +28,7 @@ from eos.db.saveddata.module import modules_table from eos.db.saveddata.drone import drones_table from eos.db.saveddata.cargo import cargo_table from eos.db.saveddata.implant import fitImplants_table -from eos.types import Fit, Module, User, Booster, Drone, Cargo, Implant, Character, DamagePattern, TargetResists +from eos.types import Fit, Module, User, Booster, Drone, Cargo, Implant, Character, DamagePattern, TargetResists, ImplantLocation from eos.effectHandlerHelpers import * fits_table = Table("fits", saveddata_meta, @@ -42,6 +42,7 @@ fits_table = Table("fits", saveddata_meta, Column("booster", Boolean, nullable = False, index = True, default = 0), Column("targetResistsID", ForeignKey("targetResists.ID"), nullable=True), Column("modeID", Integer, nullable=True), + Column("implantLocation", Integer, nullable=False, default=ImplantLocation.FIT), ) projectedFits_table = Table("projectedFits", saveddata_meta, diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index ecb7f926d..332faea21 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -31,6 +31,8 @@ import eos.db import time import copy from utils.timer import Timer +from eos.enum import Enum + import logging @@ -41,6 +43,10 @@ try: except ImportError: from utils.compat import OrderedDict +class ImplantLocation(Enum): + FIT = 0 + CHARACTER = 1 + class Fit(object): """Represents a fitting, with modules, ship, implants, etc.""" @@ -318,17 +324,20 @@ class Fit(object): return -log(0.25) * agility * mass / 1000000 + @property + def implantSource(self): + return self.implantLocation + + @implantSource.setter + def implantSource(self, source): + self.implantLocation = source + @property def appliedImplants(self): - implantsBySlot = {} - if self.character: - for implant in self.character.implants: - implantsBySlot[implant.slot] = implant - - for implant in self.implants: - implantsBySlot[implant.slot] = implant - - return implantsBySlot.values() + if self.implantLocation == ImplantLocation.CHARACTER: + return self.character.implants + else: + return self.implants @validates("ID", "ownerID", "shipID") def validator(self, key, val): diff --git a/eos/types.py b/eos/types.py index c1cafd1f5..d1628b4eb 100644 --- a/eos/types.py +++ b/eos/types.py @@ -32,7 +32,7 @@ from eos.saveddata.implant import Implant from eos.saveddata.booster import SideEffect from eos.saveddata.booster import Booster from eos.saveddata.ship import Ship -from eos.saveddata.fit import Fit +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 diff --git a/gui/builtinViewColumns/state.py b/gui/builtinViewColumns/state.py index c12db54df..cc3dee59b 100644 --- a/gui/builtinViewColumns/state.py +++ b/gui/builtinViewColumns/state.py @@ -22,7 +22,7 @@ from gui.bitmapLoader import BitmapLoader import gui.mainFrame import wx -from eos.types import Drone, Module, Rack, Fit +from eos.types import Drone, Module, Rack, Fit, Implant from eos.types import State as State_ class State(ViewColumn): @@ -67,6 +67,9 @@ class State(ViewColumn): if projectionInfo.active: return generic_active return generic_inactive + elif isinstance(stuff, Implant) and stuff.character: + # if we're showing character implants, show an "online" state, which should not be changed + return self.fittingView.imageList.GetImageIndex("state_%s_small" % State_.getName(0).lower(), "gui") else: active = getattr(stuff, "active", None) if active is None: diff --git a/gui/implantView.py b/gui/implantView.py index 1672b5b83..94d363f4f 100644 --- a/gui/implantView.py +++ b/gui/implantView.py @@ -21,10 +21,60 @@ import wx import service import gui.display as d import gui.marketBrowser as mb +import gui.mainFrame from gui.builtinViewColumns.state import State from gui.contextMenu import ContextMenu import globalEvents as GE -class ImplantView(d.Display): +from eos.types import ImplantLocation + + +class ImplantView(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL ) + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + mainSizer = wx.BoxSizer(wx.VERTICAL) + + radioSizer = wx.BoxSizer(wx.HORIZONTAL) + + self.rbFit = wx.RadioButton(self, id=wx.ID_ANY, label="Use Fit-specific Implants", style=wx.RB_GROUP) + self.rbChar = wx.RadioButton(self, id=wx.ID_ANY, label="Use Character Implants") + radioSizer.Add(self.rbFit, 0, wx.EXPAND, 5) + radioSizer.Add(self.rbChar, 0, wx.EXPAND, 5) + + mainSizer.Add(radioSizer) + self.implantDisplay = ImplantDisplay(self) + mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0 ) + self.SetSizer( mainSizer ) + self.SetAutoLayout(True) + + self.Bind(wx.EVT_RADIOBUTTON, self.OnRadioSelect, self.rbFit) + self.Bind(wx.EVT_RADIOBUTTON, self.OnRadioSelect, self.rbChar) + self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) + + def fitChanged(self, event): + sFit = service.Fit.getInstance() + activeFitID = self.mainFrame.getActiveFit() + fit = sFit.getFit(activeFitID) + if fit: + if fit.implantSource == ImplantLocation.FIT: + self.rbFit.SetValue(True) + else: + self.rbChar.SetValue(True) + + def OnRadioSelect(self, event): + sFit = service.Fit.getInstance() + activeFitID = self.mainFrame.getActiveFit() + fit = sFit.getFit(activeFitID) + if self.rbFit.GetValue(): + fit.implantSource = ImplantLocation.FIT + else: + fit.implantSource = ImplantLocation.CHARACTER + + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) + + +class ImplantDisplay(d.Display): DEFAULT_COLS = ["State", "attr:implantness", "Base Icon", @@ -79,8 +129,7 @@ class ImplantView(d.Display): self.deselectItems() - self.populate(stuff) - self.refresh(stuff) + self.update(stuff) event.Skip() def addItem(self, event): From 7c787cd13b88beb12c0991e8a3043dffc2e502d3 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 19 Mar 2016 15:42:49 -0400 Subject: [PATCH 06/30] Recalc fit when changing implant source --- gui/implantView.py | 10 +++------- service/fit.py | 8 ++++++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/gui/implantView.py b/gui/implantView.py index 94d363f4f..2e1b3bf08 100644 --- a/gui/implantView.py +++ b/gui/implantView.py @@ -63,15 +63,11 @@ class ImplantView(wx.Panel): self.rbChar.SetValue(True) def OnRadioSelect(self, event): + fitID = self.mainFrame.getActiveFit() sFit = service.Fit.getInstance() - activeFitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(activeFitID) - if self.rbFit.GetValue(): - fit.implantSource = ImplantLocation.FIT - else: - fit.implantSource = ImplantLocation.CHARACTER + sFit.toggleImplantSource(fitID, ImplantLocation.FIT if self.rbFit.GetValue() else ImplantLocation.CHARACTER) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=activeFitID)) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) class ImplantDisplay(d.Display): diff --git a/service/fit.py b/service/fit.py index 590651abe..378b92c83 100644 --- a/service/fit.py +++ b/service/fit.py @@ -721,6 +721,14 @@ class Fit(object): self.recalc(fit) return True + def toggleImplantSource(self, fitID, source): + fit = eos.db.getFit(fitID) + fit.implantSource = source + + eos.db.commit() + self.recalc(fit) + return True + def toggleBooster(self, fitID, i): fit = eos.db.getFit(fitID) booster = fit.boosters[i] From 443c917c6b3293b54a22df821255f73bc6165f0c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 19 Mar 2016 18:23:34 -0400 Subject: [PATCH 07/30] Get context menus working for character implants --- gui/builtinContextMenus/itemStats.py | 2 +- gui/builtinContextMenus/marketJump.py | 11 +++++------ gui/implantView.py | 4 ++-- service/fit.py | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index 73ba66a56..40e94164c 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -16,7 +16,7 @@ class ItemStats(ContextMenu): "implantItem", "boosterItem", "skillItem", "projectedModule", "projectedDrone", "projectedCharge", - "itemStats") + "itemStats", "implantItemChar") def getText(self, itmContext, selection): return "{0} Stats".format(itmContext if itmContext is not None else "Item") diff --git a/gui/builtinContextMenus/marketJump.py b/gui/builtinContextMenus/marketJump.py index 4980bf61f..d38fd9f1c 100644 --- a/gui/builtinContextMenus/marketJump.py +++ b/gui/builtinContextMenus/marketJump.py @@ -12,7 +12,8 @@ class MarketJump(ContextMenu): "fittingCharge", "droneItem", "implantItem", "boosterItem", "projectedModule", "projectedDrone", - "projectedCharge", "cargoItem") + "projectedCharge", "cargoItem", + "implantItemChar") if not srcContext in validContexts or selection is None or len(selection) < 1: return False @@ -33,12 +34,10 @@ class MarketJump(ContextMenu): def activate(self, fullContext, selection, i): srcContext = fullContext[0] - if srcContext in ("fittingModule", "droneItem", "implantItem", - "boosterItem", "projectedModule", "projectedDrone", - "cargoItem"): - item = selection[0].item - elif srcContext in ("fittingCharge", "projectedCharge"): + if srcContext in ("fittingCharge", "projectedCharge"): item = selection[0].charge + elif hasattr(selection[0], "item"): + item = selection[0].item else: item = selection[0] diff --git a/gui/implantView.py b/gui/implantView.py index 2e1b3bf08..c85b4678e 100644 --- a/gui/implantView.py +++ b/gui/implantView.py @@ -172,10 +172,10 @@ class ImplantDisplay(d.Display): if sel != -1: sFit = service.Fit.getInstance() fit = sFit.getFit(self.mainFrame.getActiveFit()) - implant = fit.implants[sel] + implant = fit.appliedImplants[sel] sMkt = service.Market.getInstance() - sourceContext = "implantItem" + sourceContext = "implantItem" if fit.implantSource == ImplantLocation.FIT else "implantItemChar" itemContext = sMkt.getCategoryByItem(implant.item).name menu = ContextMenu.getMenu((implant,), (sourceContext, itemContext)) diff --git a/service/fit.py b/service/fit.py index 378b92c83..ae1d4ec40 100644 --- a/service/fit.py +++ b/service/fit.py @@ -724,7 +724,7 @@ class Fit(object): def toggleImplantSource(self, fitID, source): fit = eos.db.getFit(fitID) fit.implantSource = source - + eos.db.commit() self.recalc(fit) return True From f9d2a78c5eecda0eb81de8949c20eaa49cf8fb91 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 19 Mar 2016 19:28:14 -0400 Subject: [PATCH 08/30] Fix up styling for character implants and fitting window implant view --- gui/characterEditor.py | 34 ++++++++++++++++++++++------------ gui/implantView.py | 19 +++++++++++-------- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index dbe628f4b..ad9ee2808 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -488,14 +488,15 @@ class ImplantsTreeView (wx.Panel): def __init__(self, parent): wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) + self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.HORIZONTAL) availableSizer = wx.BoxSizer(wx.VERTICAL) - pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) self.availableImplantsSearch.ShowCancelButton(True) + availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2) self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) @@ -505,26 +506,26 @@ class ImplantsTreeView (wx.Panel): availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND) - buttonSizer = wx.BoxSizer(wx.VERTICAL) + pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) - pmainSizer.Add(buttonSizer, 0, wx.TOP, 5) + + buttonSizer = wx.BoxSizer(wx.VERTICAL) + buttonSizer.AddSpacer(( 0, 0), 1) + #pmainSizer.Add(buttonSizer, 0, wx.TOP, 5) self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style = wx.BORDER_NONE) - buttonSizer.Add(self.btnAdd, 0) + self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style = wx.BORDER_NONE) buttonSizer.Add(self.btnRemove, 0) - buttonSizer.AddStretchSpacer() - pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 5) + buttonSizer.AddSpacer(( 0, 0), 1) + pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 0) + characterImplantSizer = wx.BoxSizer(wx.VERTICAL) self.pluggedImplantsTree = AvailableImplantsView(self) - - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - self.update(sChar.getImplants(charID)) - - pmainSizer.Add(self.pluggedImplantsTree, 1, wx.ALL | wx.EXPAND, 5) + characterImplantSizer.Add(self.pluggedImplantsTree, 1, wx.ALL|wx.EXPAND, 5) + pmainSizer.Add(characterImplantSizer, 1, wx.EXPAND, 5) self.SetSizer(pmainSizer) @@ -549,8 +550,17 @@ class ImplantsTreeView (wx.Panel): #Bind the change of a character* self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged) #self.Enable(False) + + # We update with an empty list first to set the initial size for Layout(), then update later with actual + # implants for character. This helps with sizing issues. + self.update([]) + self.Layout() + sChar = service.Character.getInstance() + charID = self.Parent.Parent.getActiveCharacter() + self.update(sChar.getImplants(charID)) + def update(self, implants): self.implants = implants[:] self.implants.sort(key=lambda i: int(i.getModifiedItemAttr("implantness"))) diff --git a/gui/implantView.py b/gui/implantView.py index c85b4678e..5c065094a 100644 --- a/gui/implantView.py +++ b/gui/implantView.py @@ -35,16 +35,19 @@ class ImplantView(wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) - radioSizer = wx.BoxSizer(wx.HORIZONTAL) - - self.rbFit = wx.RadioButton(self, id=wx.ID_ANY, label="Use Fit-specific Implants", style=wx.RB_GROUP) - self.rbChar = wx.RadioButton(self, id=wx.ID_ANY, label="Use Character Implants") - radioSizer.Add(self.rbFit, 0, wx.EXPAND, 5) - radioSizer.Add(self.rbChar, 0, wx.EXPAND, 5) - - mainSizer.Add(radioSizer) self.implantDisplay = ImplantDisplay(self) mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0 ) + + radioSizer = wx.BoxSizer(wx.HORIZONTAL) + radioSizer.AddSpacer(( 0, 0), 1, wx.EXPAND, 5) + self.rbFit = wx.RadioButton(self, id=wx.ID_ANY, label="Use Fit-specific Implants", style=wx.RB_GROUP) + self.rbChar = wx.RadioButton(self, id=wx.ID_ANY, label="Use Character Implants") + radioSizer.Add(self.rbFit, 0, wx.ALL, 5) + radioSizer.Add(self.rbChar, 0, wx.ALL, 5) + radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + + mainSizer.Add(radioSizer, 0, wx.EXPAND, 5) + self.SetSizer( mainSizer ) self.SetAutoLayout(True) From 1ddd37f381fb9863f0b56a751551cfadf16e6b66 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 19 Mar 2016 20:55:01 -0400 Subject: [PATCH 09/30] Enable implant searching. --- gui/characterEditor.py | 83 ++++++++++++++++++++++++++++++++++++++++++ service/market.py | 5 ++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index ad9ee2808..4473a12bd 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -29,6 +29,9 @@ from gui.contextMenu import ContextMenu from wx.lib.buttons import GenBitmapButton import gui.globalEvents as GE +import gui.PFSearchBox as SBox +from gui.marketBrowser import SearchBox + class CharacterEditor(wx.Frame): def __init__(self, parent): wx.Frame.__init__ (self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition, @@ -494,10 +497,20 @@ class ImplantsTreeView (wx.Panel): availableSizer = wx.BoxSizer(wx.VERTICAL) + self.searchBox = SearchBox(self) + self.itemView = ItemView(self) + + self.itemView.Hide() + + availableSizer.Add(self.searchBox, 0, wx.EXPAND) + availableSizer.Add(self.itemView, 1, wx.EXPAND) + + ''' self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) self.availableImplantsSearch.ShowCancelButton(True) availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2) + ''' self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) root = self.availableRoot = self.availableImplantsTree.AddRoot("Available") @@ -506,6 +519,7 @@ class ImplantsTreeView (wx.Panel): availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND) + pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) @@ -530,6 +544,7 @@ class ImplantsTreeView (wx.Panel): self.SetSizer(pmainSizer) # Populate the market tree + sMkt = service.Market.getInstance() for mktGrp in sMkt.getImplantTree(): iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp)) @@ -543,6 +558,8 @@ class ImplantsTreeView (wx.Panel): self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.addImplant) + self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated) + #Bind add & remove buttons self.btnAdd.Bind(wx.EVT_BUTTON, self.addImplant) self.btnRemove.Bind(wx.EVT_BUTTON, self.removeImplant) @@ -561,6 +578,17 @@ class ImplantsTreeView (wx.Panel): charID = self.Parent.Parent.getActiveCharacter() self.update(sChar.getImplants(charID)) + def itemActivated(self, event): + sel = event.EventObject.GetFirstSelected() + item = self.itemView.items[sel] + + if item: + sChar = service.Character.getInstance() + charID = self.Parent.Parent.getActiveCharacter() + + sChar.addImplant(charID, item.ID) + self.update(sChar.getImplants(charID)) + def update(self, implants): self.implants = implants[:] self.implants.sort(key=lambda i: int(i.getModifiedItemAttr("implantness"))) @@ -649,6 +677,61 @@ class AvailableImplantsView(d.Display): #else: # self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) +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) + self.parent = parent + self.searchBox = parent.searchBox + + self.items = [] + + # 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) + + def clearSearch(self, event=None): + if self.IsShown(): + self.parent.availableImplantsTree.Show() + self.Hide() + self.parent.Layout() + + if event: + self.searchBox.Clear() + + self.items = [] + self.update(self.items) + + 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, ["Implant"]) + + def populateSearch(self, items): + if not self.IsShown(): + self.parent.availableImplantsTree.Hide() + self.Show() + self.parent.Layout() + + self.items = sorted(list(items), key=lambda i: i.name) + + self.update(self.items) + + class APIView (wx.Panel): def __init__(self, parent): wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) diff --git a/service/market.py b/service/market.py index 39c700ce5..b8c0ab341 100644 --- a/service/market.py +++ b/service/market.py @@ -120,11 +120,14 @@ class SearchWorkerThread(threading.Thread): self.searchRequest = None cv.release() sMkt = Market.getInstance() - if filterOn: + if filterOn is True: # 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) + elif filterOn: # filter by selected categories + filter = eos.types.Category.name.in_(filterOn) 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")) From 201263237f443acdf61da05ecc18424c9afe3437 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 19 Mar 2016 21:20:20 -0400 Subject: [PATCH 10/30] Add migration to update fits tablet for implant source --- eos/db/migration.py | 6 ++++-- eos/db/migrations/upgrade13.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 eos/db/migrations/upgrade13.py diff --git a/eos/db/migration.py b/eos/db/migration.py index c99be1ffc..a0824f812 100644 --- a/eos/db/migration.py +++ b/eos/db/migration.py @@ -4,6 +4,9 @@ import time import re import os import migrations +import logging + +logger = logging.getLogger(__name__) def getVersion(db): cursor = db.execute('PRAGMA user_version') @@ -30,10 +33,9 @@ def update(saveddata_engine): shutil.copyfile(config.saveDB, toFile) for version in xrange(dbVersion, appVersion): - func = migrations.updates[version+1] if func: - print "applying update",version+1 + logger.info("Applying database update: %d", version+1) func(saveddata_engine) # when all is said and done, set version to current diff --git a/eos/db/migrations/upgrade13.py b/eos/db/migrations/upgrade13.py new file mode 100644 index 000000000..047dc7129 --- /dev/null +++ b/eos/db/migrations/upgrade13.py @@ -0,0 +1,15 @@ +""" +Migration 13 + +- Alters fits table to introduce implant location attribute +""" + +import sqlalchemy + +def upgrade(saveddata_engine): + # Update fits schema to include implant location attribute + try: + saveddata_engine.execute("SELECT implantLocation FROM fits LIMIT 1") + except sqlalchemy.exc.DatabaseError: + saveddata_engine.execute("ALTER TABLE fits ADD COLUMN implantLocation INTEGER;") + saveddata_engine.execute("UPDATE fits SET implantLocation = 0") \ No newline at end of file From ff56e70b817d5cc5fd54f74158e7ad9986fcd7ac Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 19 Mar 2016 22:09:31 -0400 Subject: [PATCH 11/30] Opps, dunno how this happened --- gui/characterEditor.py.BASE | 804 ------------------------------------ 1 file changed, 804 deletions(-) delete mode 100644 gui/characterEditor.py.BASE diff --git a/gui/characterEditor.py.BASE b/gui/characterEditor.py.BASE deleted file mode 100644 index e2279c31e..000000000 --- a/gui/characterEditor.py.BASE +++ /dev/null @@ -1,804 +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 . -#=============================================================================== - -import wx - -import gui.mainFrame -import wx.lib.newevent -import wx.gizmos -from gui.bitmapLoader import BitmapLoader -import service -import gui.display as d -from gui.contextMenu import ContextMenu -from wx.lib.buttons import GenBitmapButton -import gui.globalEvents as GE - -class CharacterEditor(wx.Frame): - def __init__(self, parent): - wx.Frame.__init__ (self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition, - 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.SetIcon(i) - - self.mainFrame = parent - - self.disableWin= wx.WindowDisabler(self) - self.SetSizeHintsSz(wx.Size(640, 600), wx.DefaultSize) - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) - - mainSizer = wx.BoxSizer(wx.VERTICAL) - self.navSizer = wx.BoxSizer(wx.HORIZONTAL) - - sChar = service.Character.getInstance() - - self.btnSave = wx.Button(self, wx.ID_SAVE) - self.btnSave.Hide() - self.btnSave.Bind(wx.EVT_BUTTON, self.processRename) - - self.characterRename = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) - self.characterRename.Hide() - self.characterRename.Bind(wx.EVT_TEXT_ENTER, self.processRename) - - self.charChoice = wx.Choice(self, wx.ID_ANY, style=0) - self.navSizer.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 5) - - charList = sChar.getCharacterList() - - for id, name, active in charList: - i = self.charChoice.Append(name, id) - if active: - self.charChoice.SetSelection(i) - - self.navSizer.Add(self.btnSave, 0, wx.ALL , 5) - - - buttons = (("new", wx.ART_NEW), - ("rename", BitmapLoader.getBitmap("rename", "gui")), - ("copy", wx.ART_COPY), - ("delete", wx.ART_DELETE)) - - size = None - for name, art in buttons: - bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art - btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - if size is None: - size = btn.GetSize() - - btn.SetMinSize(size) - btn.SetMaxSize(size) - - btn.SetToolTipString("%s character" % name.capitalize()) - btn.Bind(wx.EVT_BUTTON, getattr(self, name)) - setattr(self, "btn%s" % name.capitalize(), btn) - self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2) - - - mainSizer.Add(self.navSizer, 0, wx.ALL | wx.EXPAND, 5) - - self.viewsNBContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) - - self.sview = SkillTreeView(self.viewsNBContainer) - #self.iview = ImplantsTreeView(self.viewsNBContainer) - #======================================================================= - # RC2 - #self.iview.Show(False) - #======================================================================= - self.aview = APIView(self.viewsNBContainer) - - self.viewsNBContainer.AddPage(self.sview, "Skills") - - #======================================================================= - # Disabled for RC2 - # self.viewsNBContainer.AddPage(self.iview, "Implants") - #======================================================================= - self.viewsNBContainer.AddPage(self.aview, "API") - - mainSizer.Add(self.viewsNBContainer, 1, wx.EXPAND | wx.ALL, 5) - - bSizerButtons = wx.BoxSizer(wx.HORIZONTAL) - - self.btnSaveChar = wx.Button(self, wx.ID_ANY, "Save") - self.btnSaveAs = wx.Button(self, wx.ID_ANY, "Save As...") - self.btnRevert = wx.Button(self, wx.ID_ANY, "Revert") - self.btnOK = wx.Button(self, wx.ID_OK) - - bSizerButtons.Add(self.btnSaveChar, 0, wx.ALL, 5) - bSizerButtons.Add(self.btnSaveAs, 0, wx.ALL, 5) - bSizerButtons.Add(self.btnRevert, 0, wx.ALL, 5) - bSizerButtons.AddStretchSpacer() - bSizerButtons.Add(self.btnOK, 0, wx.ALL, 5) - - - self.btnSaveChar.Bind(wx.EVT_BUTTON, self.saveChar) - self.btnSaveAs.Bind(wx.EVT_BUTTON, self.saveCharAs) - self.btnRevert.Bind(wx.EVT_BUTTON, self.revertChar) - self.btnOK.Bind(wx.EVT_BUTTON, self.editingFinished) - - mainSizer.Add(bSizerButtons, 0, wx.EXPAND, 5) - - self.btnRestrict() - - self.SetSizer(mainSizer) - self.Layout() - - self.Centre(wx.BOTH) - - charID = self.getActiveCharacter() - if sChar.getCharName(charID) in ("All 0", "All 5"): - self.restrict() - - self.registerEvents() - - def btnRestrict(self): - sChar = service.Character.getInstance() - charID = self.getActiveCharacter() - char = sChar.getCharacter(charID) - - # enable/disable character saving stuff - self.btnSaveChar.Enable(not char.ro and char.isDirty) - self.btnSaveAs.Enable(char.isDirty) - self.btnRevert.Enable(char.isDirty) - - def refreshCharacterList(self, event=None): - sChar = service.Character.getInstance() - charList = sChar.getCharacterList() - active = self.getActiveCharacter() - self.charChoice.Clear() - - for id, name, _ in charList: - i = self.charChoice.Append(name, id) - if active == id: - self.charChoice.SetSelection(i) - - self.btnRestrict() - - def editingFinished(self, event): - del self.disableWin - wx.PostEvent(self.mainFrame, GE.CharListUpdated()) - self.Destroy() - - def registerEvents(self): - self.Bind(wx.EVT_CLOSE, self.closeEvent) - self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList) - self.charChoice.Bind(wx.EVT_CHOICE, self.charChanged) - - def saveChar(self, event): - sChr = service.Character.getInstance() - charID = self.getActiveCharacter() - sChr.saveCharacter(charID) - self.sview.populateSkillTree() - wx.PostEvent(self, GE.CharListUpdated()) - - def saveCharAs(self, event): - charID = self.getActiveCharacter() - dlg = SaveCharacterAs(self, charID) - dlg.ShowModal() - self.sview.populateSkillTree() - - def revertChar(self, event): - sChr = service.Character.getInstance() - charID = self.getActiveCharacter() - sChr.revertCharacter(charID) - self.sview.populateSkillTree() - wx.PostEvent(self, GE.CharListUpdated()) - - def closeEvent(self, event): - del self.disableWin - wx.PostEvent(self.mainFrame, GE.CharListUpdated()) - self.Destroy() - - def restrict(self): - self.btnRename.Enable(False) - self.btnDelete.Enable(False) - self.aview.stDisabledTip.Show() - self.aview.inputID.Enable(False) - self.aview.inputKey.Enable(False) - self.aview.charChoice.Enable(False) - self.aview.btnFetchCharList.Enable(False) - self.aview.btnFetchSkills.Enable(False) - self.aview.stStatus.SetLabel("") - self.aview.Layout() - - def unrestrict(self): - self.btnRename.Enable(True) - self.btnDelete.Enable(True) - self.aview.stDisabledTip.Hide() - self.aview.inputID.Enable(True) - self.aview.inputKey.Enable(True) - self.aview.btnFetchCharList.Enable(True) - self.aview.btnFetchSkills.Enable(True) - self.aview.stStatus.SetLabel("") - self.aview.Layout() - - def charChanged(self, event): - self.sview.populateSkillTree() - sChar = service.Character.getInstance() - charID = self.getActiveCharacter() - if sChar.getCharName(charID) in ("All 0", "All 5"): - self.restrict() - else: - self.unrestrict() - - wx.PostEvent(self, GE.CharChanged()) - if event is not None: - event.Skip() - - def getActiveCharacter(self): - selection = self.charChoice.GetCurrentSelection() - return self.charChoice.GetClientData(selection) if selection is not None else None - - def new(self, event): - sChar = service.Character.getInstance() - charID = sChar.new() - id = self.charChoice.Append(sChar.getCharName(charID), charID) - self.charChoice.SetSelection(id) - self.unrestrict() - self.btnSave.SetLabel("Create") - self.rename(None) - self.charChanged(None) - - def rename(self, event): - if event is not None: - self.btnSave.SetLabel("Rename") - self.charChoice.Hide() - self.characterRename.Show() - self.navSizer.Replace(self.charChoice, self.characterRename) - self.characterRename.SetFocus() - for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete): - btn.Hide() - - self.btnSave.Show() - self.navSizer.Layout() - - sChar = service.Character.getInstance() - currName = sChar.getCharName(self.getActiveCharacter()) - self.characterRename.SetValue(currName) - self.characterRename.SetSelection(0, len(currName)) - - def processRename(self, event): - sChar = service.Character.getInstance() - newName = self.characterRename.GetLineText(0) - - if newName == "All 0" or newName == "All 5": - newName = newName + " bases are belong to us" - - charID = self.getActiveCharacter() - sChar.rename(charID, newName) - - self.charChoice.Show() - self.characterRename.Hide() - self.navSizer.Replace(self.characterRename, self.charChoice) - for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete): - btn.Show() - - self.btnSave.Hide() - self.navSizer.Layout() - self.refreshCharacterList() - - def copy(self, event): - sChar = service.Character.getInstance() - charID = sChar.copy(self.getActiveCharacter()) - id = self.charChoice.Append(sChar.getCharName(charID), charID) - self.charChoice.SetSelection(id) - self.unrestrict() - self.btnSave.SetLabel("Copy") - self.rename(None) - wx.PostEvent(self, GE.CharChanged()) - - def delete(self, event): - dlg = wx.MessageDialog(self, - "Do you really want to delete this character?", - "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) - - if dlg.ShowModal() == wx.ID_YES: - sChar = service.Character.getInstance() - sChar.delete(self.getActiveCharacter()) - sel = self.charChoice.GetSelection() - self.charChoice.Delete(sel) - self.charChoice.SetSelection(sel - 1) - newSelection = self.getActiveCharacter() - if sChar.getCharName(newSelection) in ("All 0", "All 5"): - self.restrict() - - wx.PostEvent(self, GE.CharChanged()) - - def Destroy(self): - sFit = service.Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - if fitID is not None: - sFit.clearFit(fitID) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) - - wx.Frame.Destroy(self) - -class SkillTreeView (wx.Panel): - def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - - pmainSizer = wx.BoxSizer(wx.VERTICAL) - - tree = self.skillTreeListCtrl = wx.gizmos.TreeListCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) - pmainSizer.Add(tree, 1, wx.EXPAND | wx.ALL, 5) - - - self.imageList = wx.ImageList(16, 16) - tree.SetImageList(self.imageList) - self.skillBookImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) - - tree.AddColumn("Skill") - tree.AddColumn("Level") - tree.SetMainColumn(0) - - self.root = tree.AddRoot("Skills") - tree.SetItemText(self.root, "Levels", 1) - - tree.SetColumnWidth(0, 500) - - self.populateSkillTree() - - tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) - tree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.scheduleMenu) - - srcContext = "skillItem" - itemContext = "Skill" - context = (srcContext, itemContext) - self.statsMenu = ContextMenu.getMenu(None, context) - self.levelChangeMenu = ContextMenu.getMenu(None, context) or wx.Menu() - self.levelChangeMenu.AppendSeparator() - self.levelIds = {} - - idUnlearned = wx.NewId() - self.levelIds[idUnlearned] = "Not learned" - self.levelChangeMenu.Append(idUnlearned, "Unlearn") - - for level in xrange(6): - id = wx.NewId() - self.levelIds[id] = level - self.levelChangeMenu.Append(id, "Level %d" % level) - - self.levelChangeMenu.AppendSeparator() - self.revertID = wx.NewId() - self.levelChangeMenu.Append(self.revertID, "Revert") - - - self.saveID = wx.NewId() - self.levelChangeMenu.Append(self.saveID, "Save") - - self.levelChangeMenu.Bind(wx.EVT_MENU, self.changeLevel) - self.SetSizer(pmainSizer) - - self.Layout() - - def populateSkillTree(self): - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - dirtySkills = sChar.getDirtySkills(charID) - dirtyGroups = set([skill.item.group.ID for skill in dirtySkills]) - - groups = sChar.getSkillGroups() - imageId = self.skillBookImageId - root = self.root - tree = self.skillTreeListCtrl - tree.DeleteChildren(root) - - for id, name in groups: - childId = tree.AppendItem(root, name, imageId) - tree.SetPyData(childId, id) - tree.AppendItem(childId, "dummy") - if id in dirtyGroups: - tree.SetItemTextColour(childId, wx.BLUE) - - tree.SortChildren(root) - - def expandLookup(self, event): - root = event.Item - tree = self.skillTreeListCtrl - child, cookie = tree.GetFirstChild(root) - if tree.GetItemText(child) == "dummy": - tree.Delete(child) - - #Get the real intrestin' stuff - sChar = service.Character.getInstance() - char = self.Parent.Parent.getActiveCharacter() - for id, name in sChar.getSkills(tree.GetPyData(root)): - iconId = self.skillBookImageId - childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id)) - level, dirty = sChar.getSkillLevel(char, id) - tree.SetItemText(childId, "Level %d" % level if isinstance(level, int) else level, 1) - if dirty: - tree.SetItemTextColour(childId, wx.BLUE) - - tree.SortChildren(root) - - def scheduleMenu(self, event): - event.Skip() - wx.CallAfter(self.spawnMenu, event.Item) - - def spawnMenu(self, item): - self.skillTreeListCtrl.SelectItem(item) - if self.skillTreeListCtrl.GetChildrenCount(item) > 0: - return - - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - sMkt = service.Market.getInstance() - if sChar.getCharName(charID) not in ("All 0", "All 5"): - self.levelChangeMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item)) - self.PopupMenu(self.levelChangeMenu) - else: - self.statsMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item)) - self.PopupMenu(self.statsMenu) - - def changeLevel(self, event): - level = self.levelIds.get(event.Id) - - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - selection = self.skillTreeListCtrl.GetSelection() - skillID = self.skillTreeListCtrl.GetPyData(selection) - - if level is not None: - self.skillTreeListCtrl.SetItemText(selection, "Level %d" % level if isinstance(level, int) else level, 1) - sChar.changeLevel(charID, skillID, level, persist=True) - elif event.Id == self.revertID: - sChar.revertLevel(charID, skillID) - elif event.Id == self.saveID: - sChar.saveSkill(charID, skillID) - - self.skillTreeListCtrl.SetItemTextColour(selection, None) - - dirtySkills = sChar.getDirtySkills(charID) - dirtyGroups = set([skill.item.group.ID for skill in dirtySkills]) - - parentID = self.skillTreeListCtrl.GetItemParent(selection) - groupID = self.skillTreeListCtrl.GetPyData(parentID) - - if groupID not in dirtyGroups: - self.skillTreeListCtrl.SetItemTextColour(parentID, None) - - wx.PostEvent(self.Parent.Parent, GE.CharListUpdated()) - event.Skip() - -class ImplantsTreeView (wx.Panel): - def addMarketViewImage(self, iconFile): - if iconFile is None: - return -1 - bitmap = BitmapLoader.getBitmap(iconFile, "icons") - if bitmap is None: - return -1 - else: - return self.availableImplantsImageList.Add(bitmap) - - def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) - - pmainSizer = wx.BoxSizer(wx.HORIZONTAL) - - availableSizer = wx.BoxSizer(wx.VERTICAL) - pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) - - self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) - self.availableImplantsSearch.ShowCancelButton(True) - availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2) - - self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) - root = self.availableRoot = self.availableImplantsTree.AddRoot("Available") - self.availableImplantsImageList = wx.ImageList(16, 16) - self.availableImplantsTree.SetImageList(self.availableImplantsImageList) - - availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND) - - buttonSizer = wx.BoxSizer(wx.VERTICAL) - pmainSizer.Add(buttonSizer, 0, wx.TOP, 5) - - self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style = wx.BORDER_NONE) - buttonSizer.Add(self.btnAdd, 0) - self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style = wx.BORDER_NONE) - buttonSizer.Add(self.btnRemove, 0) - - self.pluggedImplantsTree = AvailableImplantsView(self, style=wx.LC_SINGLE_SEL) - - pmainSizer.Add(self.pluggedImplantsTree, 1, wx.ALL | wx.EXPAND, 5) - - self.SetSizer(pmainSizer) - - # Populate the market tree - sMkt = service.Market.getInstance() - for mktGrp in sMkt.getImplantTree(): - iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp)) - childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) - if sMkt.marketGroupHasTypesCheck(mktGrp) is False: - self.availableImplantsTree.AppendItem(childId, "dummy") - - self.availableImplantsTree.SortChildren(self.availableRoot) - - #Bind the event to replace dummies by real data - self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) - - #Bind add & remove buttons - self.btnAdd.Bind(wx.EVT_BUTTON, self.addImplant) - self.btnRemove.Bind(wx.EVT_BUTTON, self.removeImplant) - - #Bind the change of a character* - self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged) - self.Enable(False) - self.Layout() - - def update(self, implants): - self.implants = implants[:] - self.implants.sort(key=lambda i: int(i.getModifiedItemAttr("implantness"))) - self.pluggedImplantsTree.update(self.implants) - - def charChanged(self, event): - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - name = sChar.getCharName(charID) - if name == "All 0" or name == "All 5": - self.Enable(False) - else: - self.Enable(True) - - self.update(sChar.getImplants(charID)) - event.Skip() - - def expandLookup(self, event): - tree = self.availableImplantsTree - root = event.Item - child, cookie = tree.GetFirstChild(root) - text = tree.GetItemText(child) - if text == "dummy" or text == "itemdummy": - sMkt = service.Market.getInstance() - #A DUMMY! Keeeel!!! EBUL DUMMY MUST DIAF! - tree.Delete(child) - - if text == "dummy": - #Add 'real stoof!' instead - for id, name, iconFile, more in sMkt.getChildren(tree.GetPyData(root)): - iconId = self.addMarketViewImage(iconFile) - childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id)) - if more: - tree.AppendItem(childId, "dummy") - else: - tree.AppendItem(childId, "itemdummy") - - if text == "itemdummy": - sMkt = service.Market.getInstance() - data, usedMetas = sMkt.getVariations(tree.GetPyData(root)) - for item in data: - id = item.ID - name = item.name - iconFile = item.icon.iconFile - iconId = self.addMarketViewImage(iconFile) - tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id)) - - tree.SortChildren(root) - - def addImplant(self, event): - root = self.availableImplantsTree.GetSelection() - - if not root.IsOk(): - return - - nchilds = self.availableImplantsTree.GetChildrenCount(root) - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - if nchilds == 0: - itemID = self.availableImplantsTree.GetPyData(root) - sChar.addImplant(charID, itemID) - self.update(sChar.getImplants(charID)) - - def removeImplant(self, event): - pos = self.pluggedImplantsTree.GetFirstSelected() - if pos != -1: - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - sChar.removeImplant(charID, self.implants[pos].slot) - self.update(sChar.getImplants(charID)) - -class AvailableImplantsView(d.Display): - DEFAULT_COLS = ["Base Name", - "attr:implantness"] - - def __init__(self, parent, style): - d.Display.__init__(self, parent, style=style) - -class APIView (wx.Panel): - def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) - self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged) - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - - self.apiUrlCreatePredefined = u"https://community.eveonline.com/support/api-key/CreatePredefined?accessMask=8" - self.apiUrlKeyList = u"https://community.eveonline.com/support/api-key/" - - pmainSizer = wx.BoxSizer(wx.VERTICAL) - - hintSizer = wx.BoxSizer( wx.HORIZONTAL ) - hintSizer.AddStretchSpacer() - self.stDisabledTip = wx.StaticText( self, wx.ID_ANY, u"You cannot add API Details for All 0 and All 5 characters.\n" - u"Please select another character or make a new one.", style=wx.ALIGN_CENTER ) - self.stDisabledTip.Wrap( -1 ) - hintSizer.Add( self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10 ) - self.stDisabledTip.Hide() - hintSizer.AddStretchSpacer() - pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5) - - - fgSizerInput = wx.FlexGridSizer(3, 2, 0, 0) - fgSizerInput.AddGrowableCol(1) - fgSizerInput.SetFlexibleDirection(wx.BOTH) - fgSizerInput.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - - self.m_staticIDText = wx.StaticText(self, wx.ID_ANY, u"keyID:", wx.DefaultPosition, wx.DefaultSize, 0) - self.m_staticIDText.Wrap(-1) - fgSizerInput.Add(self.m_staticIDText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5) - - self.inputID = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) - fgSizerInput.Add(self.inputID, 1, wx.ALL | wx.EXPAND, 5) - - self.m_staticKeyText = wx.StaticText(self, wx.ID_ANY, u"vCode:", wx.DefaultPosition, wx.DefaultSize, 0) - self.m_staticKeyText.Wrap(-1) - fgSizerInput.Add(self.m_staticKeyText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5) - - self.inputKey = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) - fgSizerInput.Add(self.inputKey, 0, wx.ALL | wx.EXPAND, 5) - - self.m_staticCharText = wx.StaticText(self, wx.ID_ANY, u"Character:", wx.DefaultPosition, wx.DefaultSize, 0) - self.m_staticCharText.Wrap(-1) - fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5) - - self.charChoice = wx.Choice(self, wx.ID_ANY, style=0) - self.charChoice.Append("No Selection", 0) - fgSizerInput.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 5) - - self.charChoice.Enable(False) - - pmainSizer.Add(fgSizerInput, 0, wx.EXPAND, 5) - - btnSizer = wx.BoxSizer( wx.HORIZONTAL ) - btnSizer.AddStretchSpacer() - - self.btnFetchCharList = wx.Button(self, wx.ID_ANY, u"Get Characters") - btnSizer.Add(self.btnFetchCharList, 0, wx.ALL, 2) - self.btnFetchCharList.Bind(wx.EVT_BUTTON, self.fetchCharList) - - self.btnFetchSkills = wx.Button(self, wx.ID_ANY, u"Fetch Skills") - btnSizer.Add(self.btnFetchSkills, 0, wx.ALL, 2) - self.btnFetchSkills.Bind(wx.EVT_BUTTON, self.fetchSkills) - self.btnFetchSkills.Enable(False) - - btnSizer.AddStretchSpacer() - pmainSizer.Add(btnSizer, 0, wx.EXPAND, 5) - - self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString) - pmainSizer.Add(self.stStatus, 0, wx.ALL, 5) - - pmainSizer.AddStretchSpacer() - self.stAPITip = wx.StaticText( self, wx.ID_ANY, u"You can create a pre-defined key here (only CharacterSheet is required):", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stAPITip.Wrap( -1 ) - - pmainSizer.Add( self.stAPITip, 0, wx.ALL, 2 ) - - self.hlEveAPI = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlCreatePredefined, self.apiUrlCreatePredefined, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE ) - pmainSizer.Add( self.hlEveAPI, 0, wx.ALL, 2 ) - - self.stAPITip2 = wx.StaticText( self, wx.ID_ANY, u"Or, you can choose an existing key from:", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.stAPITip2.Wrap( -1 ) - pmainSizer.Add( self.stAPITip2, 0, wx.ALL, 2 ) - - self.hlEveAPI2 = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlKeyList, self.apiUrlKeyList, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE ) - pmainSizer.Add( self.hlEveAPI2, 0, wx.ALL, 2 ) - - self.SetSizer(pmainSizer) - self.Layout() - self.charChanged(None) - - def charChanged(self, event): - sChar = service.Character.getInstance() - ID, key, char, chars = sChar.getApiDetails(self.Parent.Parent.getActiveCharacter()) - self.inputID.SetValue(str(ID)) - self.inputKey.SetValue(key) - - self.charChoice.Clear() - - if chars: - for charName in chars: - i = self.charChoice.Append(charName) - self.charChoice.SetStringSelection(char) - self.charChoice.Enable(True) - self.btnFetchSkills.Enable(True) - else: - self.charChoice.Append("No characters...", 0) - self.charChoice.SetSelection(0) - self.charChoice.Enable(False) - self.btnFetchSkills.Enable(False) - - - if event is not None: - event.Skip() - - def fetchCharList(self, event): - self.stStatus.SetLabel("") - if self.inputID.GetLineText(0) == "" or self.inputKey.GetLineText(0) == "": - self.stStatus.SetLabel("Invalid keyID or vCode!") - return - - sChar = service.Character.getInstance() - try: - list = sChar.apiCharList(self.Parent.Parent.getActiveCharacter(), self.inputID.GetLineText(0), self.inputKey.GetLineText(0)) - except service.network.AuthenticationError, e: - self.stStatus.SetLabel("Authentication failure. Please check keyID and vCode combination.") - except service.network.TimeoutError, e: - self.stStatus.SetLabel("Request timed out. Please check network connectivity and/or proxy settings.") - except Exception, e: - self.stStatus.SetLabel("Error:\n%s"%e.message) - else: - self.charChoice.Clear() - for charName in list: - i = self.charChoice.Append(charName) - - self.btnFetchSkills.Enable(True) - self.charChoice.Enable(True) - - self.Layout() - - self.charChoice.SetSelection(0) - - def fetchSkills(self, event): - charName = self.charChoice.GetString(self.charChoice.GetSelection()) - if charName: - try: - sChar = service.Character.getInstance() - sChar.apiFetch(self.Parent.Parent.getActiveCharacter(), charName) - self.stStatus.SetLabel("Successfully fetched %s\'s skills from EVE API." % charName) - except Exception, e: - self.stStatus.SetLabel("Unable to retrieve %s\'s skills. Error message:\n%s" % (charName, e)) - -class SaveCharacterAs(wx.Dialog): - - def __init__(self, parent, charID): - wx.Dialog.__init__(self, parent, title="Save Character As...", size=wx.Size(300, 60)) - self.charID = charID - self.parent = parent - sChar = service.Character.getInstance() - name = sChar.getCharName(charID) - bSizer1 = wx.BoxSizer(wx.HORIZONTAL) - - self.input = wx.TextCtrl(self, wx.ID_ANY, name, style=wx.TE_PROCESS_ENTER) - - bSizer1.Add(self.input, 1, wx.ALL, 5) - self.input.Bind(wx.EVT_TEXT_ENTER, self.change) - self.button = wx.Button(self, wx.ID_OK, u"Save") - bSizer1.Add(self.button, 0, wx.ALL, 5) - - self.SetSizer(bSizer1) - self.Layout() - self.Centre(wx.BOTH) - self.button.Bind(wx.EVT_BUTTON, self.change) - - def change(self, event): - sChar = service.Character.getInstance() - sChar.saveCharacterAs(self.charID, self.input.GetLineText(0)) - wx.PostEvent(self.parent, GE.CharListUpdated()) - - event.Skip() - self.Close() - From 6f1872fb9452bef08d9996465bbabae879d32e35 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sat, 19 Mar 2016 22:10:09 -0400 Subject: [PATCH 12/30] Disable implants for read-only characters --- gui/characterEditor.py | 28 ++++------------------------ service/character.py | 6 ++++++ 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 4473a12bd..5f437dbe3 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -98,18 +98,10 @@ class CharacterEditor(wx.Frame): self.sview = SkillTreeView(self.viewsNBContainer) self.iview = ImplantsTreeView(self.viewsNBContainer) - #======================================================================= - # RC2 - #self.iview.Show(False) - #======================================================================= self.aview = APIView(self.viewsNBContainer) self.viewsNBContainer.AddPage(self.sview, "Skills") - - #======================================================================= - # Disabled for RC2 self.viewsNBContainer.AddPage(self.iview, "Implants") - #======================================================================= self.viewsNBContainer.AddPage(self.aview, "API") mainSizer.Add(self.viewsNBContainer, 1, wx.EXPAND | wx.ALL, 5) @@ -209,25 +201,14 @@ class CharacterEditor(wx.Frame): def restrict(self): self.btnRename.Enable(False) self.btnDelete.Enable(False) - self.aview.stDisabledTip.Show() - self.aview.inputID.Enable(False) - self.aview.inputKey.Enable(False) - self.aview.charChoice.Enable(False) - self.aview.btnFetchCharList.Enable(False) - self.aview.btnFetchSkills.Enable(False) - self.aview.stStatus.SetLabel("") - self.aview.Layout() + self.iview.Enable(False) + self.aview.Enable(False) def unrestrict(self): self.btnRename.Enable(True) self.btnDelete.Enable(True) - self.aview.stDisabledTip.Hide() - self.aview.inputID.Enable(True) - self.aview.inputKey.Enable(True) - self.aview.btnFetchCharList.Enable(True) - self.aview.btnFetchSkills.Enable(True) - self.aview.stStatus.SetLabel("") - self.aview.Layout() + self.iview.Enable(True) + self.aview.Enable(True) def charChanged(self, event): self.sview.populateSkillTree() @@ -566,7 +547,6 @@ class ImplantsTreeView (wx.Panel): #Bind the change of a character* self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged) - #self.Enable(False) # We update with an empty list first to set the initial size for Layout(), then update later with actual # implants for character. This helps with sizing issues. diff --git a/service/character.py b/service/character.py index dccd5c9f7..cf7e03102 100644 --- a/service/character.py +++ b/service/character.py @@ -32,7 +32,9 @@ import eos.db import eos.types import service import config +import logging +logger = logging.getLogger(__name__) class CharacterImportThread(threading.Thread): def __init__(self, paths, callback): @@ -343,6 +345,10 @@ class Character(object): def addImplant(self, charID, itemID): char = eos.db.getCharacter(charID) + if char.ro: + logger.error("Trying to add implant to read-only character") + return + implant = eos.types.Implant(eos.db.getItem(itemID)) char.implants.append(implant) From 36ad31ab2546e09fb088584e8c10f577a8747e2d Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 20 Mar 2016 02:04:55 -0400 Subject: [PATCH 13/30] Get some initial scaffolding up for implant sets sourced from the resist code. --- eos/db/saveddata/__init__.py | 3 +- eos/db/saveddata/implant.py | 4 + eos/db/saveddata/implantSet.py | 45 ++++ eos/db/saveddata/queries.py | 25 ++- eos/saveddata/implantSet.py | 48 +++++ eos/types.py | 1 + gui/mainFrame.py | 8 + gui/mainMenuBar.py | 5 + gui/setEditor.py | 380 +++++++++++++++++++++++++++++++++ service/__init__.py | 1 + service/implantSet.py | 60 ++++++ 11 files changed, 578 insertions(+), 2 deletions(-) create mode 100644 eos/db/saveddata/implantSet.py create mode 100644 eos/saveddata/implantSet.py create mode 100644 gui/setEditor.py create mode 100644 service/implantSet.py diff --git a/eos/db/saveddata/__init__.py b/eos/db/saveddata/__init__.py index 683fb499d..e43c6e601 100644 --- a/eos/db/saveddata/__init__.py +++ b/eos/db/saveddata/__init__.py @@ -13,6 +13,7 @@ __all__ = [ "miscData", "targetResists", "override", - "crest" + "crest", + "implantSet" ] diff --git a/eos/db/saveddata/implant.py b/eos/db/saveddata/implant.py index 60e40bff7..c74def157 100644 --- a/eos/db/saveddata/implant.py +++ b/eos/db/saveddata/implant.py @@ -36,4 +36,8 @@ charImplants_table = Table("charImplants", saveddata_meta, Column("charID", ForeignKey("characters.ID"), index = True), Column("implantID", ForeignKey("implants.ID"), primary_key = True)) +implantsSetMap_table = Table("implantSetMap", saveddata_meta, + Column("setID", ForeignKey("implantSets.ID"), index = True), + Column("implantID", ForeignKey("implants.ID"), primary_key = True)) + mapper(Implant, implants_table) diff --git a/eos/db/saveddata/implantSet.py b/eos/db/saveddata/implantSet.py new file mode 100644 index 000000000..d72b9097a --- /dev/null +++ b/eos/db/saveddata/implantSet.py @@ -0,0 +1,45 @@ +#=============================================================================== +# Copyright (C) 2016 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 sqlalchemy import Table, Column, Integer, ForeignKey, String +from sqlalchemy.orm import relation, mapper + +from eos.db import saveddata_meta +from eos.db.saveddata.implant import implantsSetMap_table +from eos.types import Implant, ImplantSet +from eos.effectHandlerHelpers import HandledImplantBoosterList + +implant_set_table = Table("implantSets", saveddata_meta, + Column("ID", Integer, primary_key = True), + Column("name", String, nullable = False), +) + +mapper(ImplantSet, implant_set_table, + properties = { + "_ImplantSet__implants": relation( + Implant, + collection_class = HandledImplantBoosterList, + cascade='all,delete-orphan', + backref='set', + single_parent=True, + primaryjoin = implantsSetMap_table.c.setID == implant_set_table.c.ID, + secondaryjoin = implantsSetMap_table.c.implantID == Implant.ID, + secondary = implantsSetMap_table), + } +) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index a305994cd..264e3c0da 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -20,7 +20,7 @@ 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, Override, CrestChar +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_ @@ -385,6 +385,29 @@ def getTargetResists(lookfor, eager=None): raise TypeError("Need integer or string as argument") return pattern +@cachedQuery(ImplantSet, 1, "lookfor") +def getImplantSet(lookfor, eager=None): + if isinstance(lookfor, int): + if eager is None: + with sd_lock: + pattern = saveddata_session.query(ImplantSet).get(lookfor) + else: + eager = processEager(eager) + with sd_lock: + pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.ID == lookfor).first() + elif isinstance(lookfor, basestring): + eager = processEager(eager) + with sd_lock: + pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.name == lookfor).first() + elif lookfor is None: + eager = processEager(eager) + with sd_lock: + patterns = saveddata_session.query(ImplantSet).options(*eager).all() + return patterns + else: + raise TypeError("Improper argument") + return pattern + def searchFits(nameLike, where=None, eager=None): if not isinstance(nameLike, basestring): raise TypeError("Need string as argument") diff --git a/eos/saveddata/implantSet.py b/eos/saveddata/implantSet.py new file mode 100644 index 000000000..00df48536 --- /dev/null +++ b/eos/saveddata/implantSet.py @@ -0,0 +1,48 @@ +#=============================================================================== +# Copyright (C) 2016 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.effectHandlerHelpers import HandledImplantBoosterList + +class ImplantSet(object): + DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") + + def __init__(self, name=None): + self.name = name + self.__implants = HandledImplantBoosterList() + + @property + def implants(self): + return self.__implants + + + EXPORT_FORMAT = "ImplantSet = %s,%d,%d,%d,%d\n" + @classmethod + def exportPatterns(cls, *patterns): + out = "# Exported from pyfa\n#\n" + out += "# Values are in following format:\n" + out += "# DamageProfile = [name],[EM amount],[Thermal amount],[Kinetic amount],[Explosive amount]\n\n" + for dp in patterns: + out += cls.EXPORT_FORMAT % (dp.name, dp.emAmount, dp.thermalAmount, dp.kineticAmount, dp.explosiveAmount) + + return out.strip() + + def __deepcopy__(self, memo): + p = ImplantSet(self.name) + p.name = "%s copy" % self.name + return p diff --git a/eos/types.py b/eos/types.py index d1628b4eb..7b03d74a2 100644 --- a/eos/types.py +++ b/eos/types.py @@ -29,6 +29,7 @@ from eos.saveddata.module import Module, State, Slot, Hardpoint, Rack from eos.saveddata.drone import Drone from eos.saveddata.cargo import Cargo from eos.saveddata.implant import Implant +from eos.saveddata.implantSet import ImplantSet from eos.saveddata.booster import SideEffect from eos.saveddata.booster import Booster from eos.saveddata.ship import Ship diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 4c0d39ba7..3b76a13c9 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -50,6 +50,7 @@ from gui.characterEditor import CharacterEditor, SaveCharacterAs from gui.characterSelection import CharacterSelection from gui.patternEditor import DmgPatternEditorDlg from gui.resistsEditor import ResistsEditorDlg +from gui.setEditor import ImplantSetEditorDlg from gui.preferenceDialog import PreferenceDialog from gui.graphFrame import GraphFrame from gui.copySelectDialog import CopySelectDialog @@ -367,6 +368,11 @@ class MainFrame(wx.Frame): dlg.ShowModal() dlg.Destroy() + def showImplantSetEditor(self, event): + dlg=ImplantSetEditorDlg(self) + dlg.ShowModal() + dlg.Destroy() + def showExportDialog(self, event): """ Export active fit """ sFit = service.Fit.getInstance() @@ -418,6 +424,8 @@ class MainFrame(wx.Frame): self.Bind(wx.EVT_MENU, self.showDamagePatternEditor, id=menuBar.damagePatternEditorId) # Target Resists editor self.Bind(wx.EVT_MENU, self.showTargetResistsEditor, id=menuBar.targetResistsEditorId) + # Implant Set editor + self.Bind(wx.EVT_MENU, self.showImplantSetEditor, id=menuBar.implantSetEditorId) # Import dialog self.Bind(wx.EVT_MENU, self.fileImportDialog, id=wx.ID_OPEN) # Export dialog diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 78376fde7..fda1955d9 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -33,6 +33,7 @@ class MainMenuBar(wx.MenuBar): self.characterEditorId = wx.NewId() self.damagePatternEditorId = wx.NewId() self.targetResistsEditorId = wx.NewId() + self.implantSetEditorId = wx.NewId() self.graphFrameId = wx.NewId() self.backupFitsId = wx.NewId() self.exportSkillsNeededId = wx.NewId() @@ -104,6 +105,10 @@ class MainMenuBar(wx.MenuBar): targetResistsEditItem.SetBitmap(BitmapLoader.getBitmap("explosive_big", "gui")) windowMenu.AppendItem(targetResistsEditItem) + implantSetEditItem = wx.MenuItem(windowMenu, self.implantSetEditorId, "Implant Set Editor\tCTRL+I") + implantSetEditItem.SetBitmap(BitmapLoader.getBitmap("damagePattern_small", "gui")) + windowMenu.AppendItem(implantSetEditItem) + graphFrameItem = wx.MenuItem(windowMenu, self.graphFrameId, "Graphs\tCTRL+G") graphFrameItem.SetBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) windowMenu.AppendItem(graphFrameItem) diff --git a/gui/setEditor.py b/gui/setEditor.py new file mode 100644 index 000000000..5af848f42 --- /dev/null +++ b/gui/setEditor.py @@ -0,0 +1,380 @@ +#=============================================================================== +# Copyright (C) 2016 Ryan Holmes +# +# 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 wx +from gui.bitmapLoader import BitmapLoader +import service +from gui.utils.clipboard import toClipboard, fromClipboard +from service.targetResists import ImportError + +class ImplantSetEditorDlg(wx.Dialog): + + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Implant Set Editor", size = wx.Size( 350,240 )) + + self.block = False + self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + + mainSizer = wx.BoxSizer(wx.VERTICAL) + + self.headerSizer = headerSizer = wx.BoxSizer(wx.HORIZONTAL) + + sIS = service.ImplantSets.getInstance() + + self.choices = sIS.getImplantSetList() + + # Sort the remaining list and continue on + self.choices.sort(key=lambda s: s.name) + self.ccSets = wx.Choice(self, choices=map(lambda s: s.name, self.choices)) + self.ccSets.Bind(wx.EVT_CHOICE, self.setChanged) + self.ccSets.SetSelection(0) + + self.namePicker = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER) + self.namePicker.Bind(wx.EVT_TEXT_ENTER, self.processRename) + self.namePicker.Hide() + + size = None + headerSizer.Add(self.ccSets, 1, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 3) + + buttons = (("new", wx.ART_NEW), + ("rename", BitmapLoader.getBitmap("rename", "gui")), + ("copy", wx.ART_COPY), + ("delete", wx.ART_DELETE)) + for name, art in buttons: + bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art + btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) + if size is None: + size = btn.GetSize() + + btn.SetMinSize(size) + btn.SetMaxSize(size) + + btn.Layout() + setattr(self, name, btn) + btn.Enable(True) + #btn.SetToolTipString("%s resist profile" % name.capitalize()) + headerSizer.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL) + + + self.btnSave = wx.Button(self, wx.ID_SAVE) + self.btnSave.Hide() + self.btnSave.Bind(wx.EVT_BUTTON, self.processRename) + headerSizer.Add(self.btnSave, 0, wx.ALIGN_CENTER) + + mainSizer.Add(headerSizer, 0, wx.EXPAND | wx.ALL, 2) + + self.sl = wx.StaticLine(self) + mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + contentSizer = wx.BoxSizer(wx.VERTICAL) + + self.slfooter = wx.StaticLine(self) + contentSizer.Add(self.slfooter, 0, wx.EXPAND | wx.TOP, 5) + + footerSizer = wx.BoxSizer(wx.HORIZONTAL) + perSizer = wx.BoxSizer(wx.VERTICAL) + + self.stNotice = wx.StaticText(self, wx.ID_ANY, u"") + self.stNotice.Wrap(-1) + perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) + + footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) + + self.totSizer = wx.BoxSizer(wx.VERTICAL) + + contentSizer.Add(footerSizer, 0, wx.EXPAND, 5) + + mainSizer.Add(contentSizer, 1, wx.EXPAND, 0) + + if "wxGTK" in wx.PlatformInfo: + self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) + mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) + self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) + + self.SetSizer(mainSizer) + + importExport = (("Import", wx.ART_FILE_OPEN, "from"), + ("Export", wx.ART_FILE_SAVE_AS, "to")) + + for name, art, direction in importExport: + bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) + btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) + + btn.SetMinSize( btn.GetSize() ) + btn.SetMaxSize( btn.GetSize() ) + + btn.Layout() + setattr(self, name, btn) + btn.Enable(True) + btn.SetToolTipString("%s patterns %s clipboard" % (name, direction) ) + footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) + + self.Layout() + bsize = self.GetBestSize() + self.SetSize((-1,bsize.height)) + + self.new.Bind(wx.EVT_BUTTON, self.newPattern) + self.rename.Bind(wx.EVT_BUTTON, self.renamePattern) + self.copy.Bind(wx.EVT_BUTTON, self.copyPattern) + self.delete.Bind(wx.EVT_BUTTON, self.deletePattern) + self.Import.Bind(wx.EVT_BUTTON, self.importPatterns) + self.Export.Bind(wx.EVT_BUTTON, self.exportPatterns) + + def closeEvent(self, event): + self.Destroy() + + def ValuesUpdated(self, event=None): + ''' + Event that is fired when resists values change. Iterates through all + resist edit fields. If blank, sets it to 0.0. If it is not a proper + decimal value, sets text color to red and refuses to save changes until + issue is resolved + ''' + if self.block: + return + + try: + p = self.getActivePattern() + + for type in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit"%type) + + if editObj.GetValue() == "": + # if we are blank, overwrite with 0 + editObj.ChangeValue("0.0") + editObj.SetInsertionPointEnd() + + value = float(editObj.GetValue()) + + # assertion, because they're easy + assert 0 <= value <= 100 + + # if everything checks out, set resist attribute + setattr(p, "%sAmount"%type, value/100) + editObj.SetForegroundColour(self.colorReset) + + self.stNotice.SetLabel("") + self.totSizer.Layout() + + if event is not None: + event.Skip() + + service.TargetResists.getInstance().saveChanges(p) + + except ValueError: + editObj.SetForegroundColour(wx.RED) + self.stNotice.SetLabel("Incorrect Formatting (decimals only)") + except AssertionError: + editObj.SetForegroundColour(wx.RED) + self.stNotice.SetLabel("Incorrect Range (must be 0-100)") + finally: # Refresh for color changes to take effect immediately + self.Refresh() + + def restrict(self): + for type in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit"%type) + editObj.Enable(False) + self.rename.Enable(False) + self.delete.Enable(False) + + def unrestrict(self): + for type in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit"%type) + editObj.Enable() + self.rename.Enable() + self.delete.Enable() + + def getActivePattern(self): + if len(self.choices) == 0: + return None + + return self.choices[self.ccSets.GetSelection()] + + def setChanged(self, event=None): + "Event fired when user selects pattern. Can also be called from script" + p = self.getActivePattern() + if p is None: + # This happens when there are no patterns in the DB. As such, force + # user to create one first or exit dlg. + self.newPattern(None) + return + + #ValuesUpdated() + + def newPattern(self, event): + ''' + Simply does new-pattern specifics: replaces label on button, restricts, + and resets values to default. Hands off to the rename function for + further handling. + ''' + self.btnSave.SetLabel("Create") + self.restrict() + # reset values + for type in self.DAMAGE_TYPES: + editObj = getattr(self, "%sEdit"%type) + editObj.ChangeValue("0.0") + editObj.SetForegroundColour(self.colorReset) + + self.Refresh() + self.renamePattern() + + def renamePattern(self, event=None): + "Changes layout to facilitate naming a pattern" + + self.showInput(True) + + if event is not None: # Rename mode + self.btnSave.SetLabel("Rename") + self.namePicker.SetValue(self.getActivePattern().name) + else: # Create mode + self.namePicker.SetValue("") + + if event is not None: + event.Skip() + + def processRename(self, event): + ''' + Processes rename event (which can be new or old patterns). If new + pattern, creates it; if old, selects it. if checks are valid, rename + saves pattern to DB. + + Also resets to default layout and unrestricts. + ''' + newName = self.namePicker.GetLineText(0) + self.stNotice.SetLabel("") + + if newName == "": + self.stNotice.SetLabel("Invalid name") + return + + sTR = service.TargetResists.getInstance() + if self.btnSave.Label == "Create": + p = sTR.newPattern() + else: + # we are renaming, so get the current selection + p = self.getActivePattern() + + # test for patterns of the same name + for pattern in self.choices: + if pattern.name == newName and p != pattern: + self.stNotice.SetLabel("Name already used, please choose another") + return + + # rename regardless of new or rename + sTR.renamePattern(p, newName) + + self.updateChoices(newName) + self.showInput(False) + sel = self.ccSets.GetSelection() + self.ValuesUpdated() + self.unrestrict() + + def copyPattern(self,event): + sTR = service.TargetResists.getInstance() + p = sTR.copyPattern(self.getActivePattern()) + self.choices.append(p) + id = self.ccSets.Append(p.name) + self.ccSets.SetSelection(id) + self.btnSave.SetLabel("Copy") + self.renamePattern() + self.setChanged() + + def deletePattern(self,event): + sTR = service.TargetResists.getInstance() + sel = self.ccSets.GetSelection() + sTR.deletePattern(self.getActivePattern()) + self.ccSets.Delete(sel) + self.ccSets.SetSelection(max(0, sel - 1)) + del self.choices[sel] + self.setChanged() + + def showInput(self, bool): + if bool and not self.namePicker.IsShown(): + self.ccSets.Hide() + self.namePicker.Show() + self.headerSizer.Replace(self.ccSets, self.namePicker) + self.namePicker.SetFocus() + for btn in (self.new, self.rename, self.delete, self.copy): + btn.Hide() + self.btnSave.Show() + self.restrict() + self.headerSizer.Layout() + elif not bool and self.namePicker.IsShown(): + self.headerSizer.Replace(self.namePicker, self.ccSets) + self.ccSets.Show() + self.namePicker.Hide() + self.btnSave.Hide() + for btn in (self.new, self.rename, self.delete, self.copy): + btn.Show() + self.unrestrict() + self.headerSizer.Layout() + + + def __del__( self ): + pass + + def updateChoices(self, select=None): + "Gathers list of patterns and updates choice selections" + sTR = service.TargetResists.getInstance() + self.choices = sTR.getTargetResistsList() + + if len(self.choices) == 0: + #self.newPattern(None) + return + + # Sort the remaining list and continue on + self.choices.sort(key=lambda p: p.name) + self.ccSets.Clear() + + for i, choice in enumerate(map(lambda p: p.name, self.choices)): + self.ccSets.Append(choice) + + if select is not None and choice == select: + self.ccSets.SetSelection(i) + + if select is None: + self.ccSets.SetSelection(0) + + self.setChanged() + + def importPatterns(self, event): + "Event fired when import from clipboard button is clicked" + + text = fromClipboard() + if text: + sTR = service.TargetResists.getInstance() + try: + sTR.importPatterns(text) + self.stNotice.SetLabel("Patterns successfully imported from clipboard") + self.showInput(False) + except service.targetResists.ImportError, e: + self.stNotice.SetLabel(str(e)) + except Exception, e: + self.stNotice.SetLabel("Could not import from clipboard: unknown errors") + finally: + self.updateChoices() + else: + self.stNotice.SetLabel("Could not import from clipboard") + + def exportPatterns(self, event): + "Event fired when export to clipboard button is clicked" + + sTR = service.TargetResists.getInstance() + toClipboard( sTR.exportPatterns() ) + self.stNotice.SetLabel("Patterns exported to clipboard") diff --git a/service/__init__.py b/service/__init__.py index e7e0286d4..b19c6db17 100644 --- a/service/__init__.py +++ b/service/__init__.py @@ -10,6 +10,7 @@ from service.update import Update from service.price import Price from service.network import Network from service.eveapi import EVEAPIConnection, ParseXML +from service.implantSet import ImplantSets import wx if not 'wxMac' in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0)): diff --git a/service/implantSet.py b/service/implantSet.py new file mode 100644 index 000000000..ff7469536 --- /dev/null +++ b/service/implantSet.py @@ -0,0 +1,60 @@ +#=============================================================================== +# Copyright (C) 2016 Ryan Holmes +# +# 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 eos.db +import eos.types +import copy + +class ImportError(Exception): + pass + +class ImplantSets(): + instance = None + @classmethod + def getInstance(cls): + if cls.instance is None: + cls.instance = ImplantSets() + + return cls.instance + + def getImplantSetList(self): + return eos.db.getImplantSet(None) + + def getImplantSet(self, name): + return eos.db.getImplantSet(name) + + def newSet(self): + p = eos.types.ImplantSet() + p.name = "" + return p + + def renameSet(self, s, newName): + s.name = newName + eos.db.save(s) + + def deleteSet(self, s): + eos.db.remove(s) + + def copySet(self, s): + newS = copy.deepcopy(s) + eos.db.save(newS) + return newS + + def saveChanges(self, s): + eos.db.save(s) From c1653c5f2e2ca2fdecbcbf7d13ff58ae261d649f Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 20 Mar 2016 02:59:09 -0400 Subject: [PATCH 14/30] Abstract the implant editor to work with different sources --- gui/builtinViews/__init__.py | 2 +- gui/builtinViews/implantEditor.py | 254 ++++++++++++++++++++++++++++++ gui/characterEditor.py | 254 ++---------------------------- gui/setEditor.py | 15 +- 4 files changed, 281 insertions(+), 244 deletions(-) create mode 100644 gui/builtinViews/implantEditor.py diff --git a/gui/builtinViews/__init__.py b/gui/builtinViews/__init__.py index 4d1ed63c6..46a500f7b 100644 --- a/gui/builtinViews/__init__.py +++ b/gui/builtinViews/__init__.py @@ -1 +1 @@ -__all__ = ["fittingView", "fleetView"] +__all__ = ["fittingView", "fleetView", "implantEditor"] diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py new file mode 100644 index 000000000..615fc3da4 --- /dev/null +++ b/gui/builtinViews/implantEditor.py @@ -0,0 +1,254 @@ +import wx +import service +import gui.display as d +from gui.bitmapLoader import BitmapLoader +import gui.PFSearchBox as SBox +from gui.marketBrowser import SearchBox +from wx.lib.buttons import GenBitmapButton + +class BaseImplantEditorView (wx.Panel): + def addMarketViewImage(self, iconFile): + if iconFile is None: + return -1 + bitmap = BitmapLoader.getBitmap(iconFile, "icons") + if bitmap is None: + return -1 + else: + return self.availableImplantsImageList.Add(bitmap) + + def __init__(self, parent): + wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) + self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + + pmainSizer = wx.BoxSizer(wx.HORIZONTAL) + + availableSizer = wx.BoxSizer(wx.VERTICAL) + + self.searchBox = SearchBox(self) + self.itemView = ItemView(self) + + self.itemView.Hide() + + availableSizer.Add(self.searchBox, 0, wx.EXPAND) + availableSizer.Add(self.itemView, 1, wx.EXPAND) + + ''' + self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) + self.availableImplantsSearch.ShowCancelButton(True) + + availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2) + ''' + + self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) + root = self.availableRoot = self.availableImplantsTree.AddRoot("Available") + self.availableImplantsImageList = wx.ImageList(16, 16) + self.availableImplantsTree.SetImageList(self.availableImplantsImageList) + + availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND) + + + pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) + + + buttonSizer = wx.BoxSizer(wx.VERTICAL) + buttonSizer.AddSpacer(( 0, 0), 1) + + self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style = wx.BORDER_NONE) + buttonSizer.Add(self.btnAdd, 0) + + self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style = wx.BORDER_NONE) + buttonSizer.Add(self.btnRemove, 0) + + buttonSizer.AddSpacer(( 0, 0), 1) + pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 0) + + characterImplantSizer = wx.BoxSizer(wx.VERTICAL) + self.pluggedImplantsTree = AvailableImplantsView(self) + characterImplantSizer.Add(self.pluggedImplantsTree, 1, wx.ALL|wx.EXPAND, 5) + pmainSizer.Add(characterImplantSizer, 1, wx.EXPAND, 5) + + self.SetSizer(pmainSizer) + + # Populate the market tree + + sMkt = service.Market.getInstance() + for mktGrp in sMkt.getImplantTree(): + iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp)) + childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) + if sMkt.marketGroupHasTypesCheck(mktGrp) is False: + self.availableImplantsTree.AppendItem(childId, "dummy") + + self.availableImplantsTree.SortChildren(self.availableRoot) + + #Bind the event to replace dummies by real data + self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) + self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.itemSelected) + + self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemSelected) + + #Bind add & remove buttons + self.btnAdd.Bind(wx.EVT_BUTTON, self.itemSelected) + self.btnRemove.Bind(wx.EVT_BUTTON, self.removeItem) + + # We update with an empty list first to set the initial size for Layout(), then update later with actual + # implants for character. This helps with sizing issues. + self.pluggedImplantsTree.update([]) + self.bindContext() + self.Layout() + + self.update() + + def bindContext(self): + # Binds self.contextChanged to whatever changes the context + raise NotImplementedError() + + def getImplantsFromContext(self): + """ Gets list of implants from current context """ + raise NotImplementedError() + + def addImplantToContext(self, item): + """ Adds implant to the current context""" + raise NotImplementedError() + + def removeImplantFromContext(self, pos): + """ Removes implant from the current context""" + raise NotImplementedError() + + def update(self): + """Updates implant list based off the current context""" + self.implants = self.getImplantsFromContext() + self.implants.sort(key=lambda i: int(i.getModifiedItemAttr("implantness"))) + self.pluggedImplantsTree.update(self.implants) + + def contextChanged(self, event): + self.update() + event.Skip() + + def expandLookup(self, event): + tree = self.availableImplantsTree + sMkt = service.Market.getInstance() + parent = event.Item + child, _ = tree.GetFirstChild(parent) + text = tree.GetItemText(child) + + if text == "dummy" or text == "itemdummy": + tree.Delete(child) + + # if the dummy item is a market group, replace with actual market groups + if text == "dummy": + #Add 'real stoof!' instead + currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent), eager="children") + for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): + iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(childMktGrp)) + childId = tree.AppendItem(parent, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) + if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: + tree.AppendItem(childId, "dummy") + else: + tree.AppendItem(childId, "itemdummy") + + # replace dummy with actual items + if text == "itemdummy": + currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent)) + items = sMkt.getItemsByMarketGroup(currentMktGrp) + for item in items: + iconId = self.addMarketViewImage(item.icon.iconFile) + tree.AppendItem(parent, item.name, iconId, data=wx.TreeItemData(item)) + + tree.SortChildren(parent) + + def itemSelected(self, event): + if event.EventObject is self.btnAdd: + # janky fix that sets EventObject so that we don't have similar code elsewhere. + if self.itemView.IsShown(): + event.EventObject = self.itemView + else: + event.EventObject = self.availableImplantsTree + + if event.EventObject is self.itemView: + sel = event.EventObject.GetFirstSelected() + item = self.itemView.items[sel] + else: + root = self.availableImplantsTree.GetSelection() + + if not root.IsOk(): + return + + nchilds = self.availableImplantsTree.GetChildrenCount(root) + if nchilds == 0: + item = self.availableImplantsTree.GetPyData(root) + else: + event.Skip() + return + + if item: + self.addImplantToContext(item) + self.update() + + def removeItem(self, event): + pos = self.pluggedImplantsTree.GetFirstSelected() + if pos != -1: + self.removeImplantFromContext(pos) + self.update() + +class AvailableImplantsView(d.Display): + DEFAULT_COLS = ["attr:implantness", + "Base Icon", + "Base Name"] + + def __init__(self, parent): + d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL) + self.Bind(wx.EVT_LEFT_DCLICK, parent.removeItem) + +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) + self.parent = parent + self.searchBox = parent.searchBox + + self.items = [] + + # 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) + + def clearSearch(self, event=None): + if self.IsShown(): + self.parent.availableImplantsTree.Show() + self.Hide() + self.parent.Layout() + + if event: + self.searchBox.Clear() + + self.items = [] + self.update(self.items) + + 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, ["Implant"]) + + def populateSearch(self, items): + if not self.IsShown(): + self.parent.availableImplantsTree.Hide() + self.Show() + self.parent.Layout() + + self.items = sorted(list(items), key=lambda i: i.name) + + self.update(self.items) \ No newline at end of file diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 5f437dbe3..697862146 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -19,18 +19,13 @@ import wx -import gui.mainFrame import wx.lib.newevent import wx.gizmos from gui.bitmapLoader import BitmapLoader import service -import gui.display as d from gui.contextMenu import ContextMenu -from wx.lib.buttons import GenBitmapButton import gui.globalEvents as GE - -import gui.PFSearchBox as SBox -from gui.marketBrowser import SearchBox +from gui.builtinViews.implantEditor import BaseImplantEditorView class CharacterEditor(wx.Frame): def __init__(self, parent): @@ -97,7 +92,7 @@ class CharacterEditor(wx.Frame): self.viewsNBContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) self.sview = SkillTreeView(self.viewsNBContainer) - self.iview = ImplantsTreeView(self.viewsNBContainer) + self.iview = ImplantEditorView(self.viewsNBContainer) self.aview = APIView(self.viewsNBContainer) self.viewsNBContainer.AddPage(self.sview, "Skills") @@ -460,256 +455,31 @@ class SkillTreeView (wx.Panel): wx.PostEvent(self.Parent.Parent, GE.CharListUpdated()) event.Skip() -class ImplantsTreeView (wx.Panel): - def addMarketViewImage(self, iconFile): - if iconFile is None: - return -1 - bitmap = BitmapLoader.getBitmap(iconFile, "icons") - if bitmap is None: - return -1 - else: - return self.availableImplantsImageList.Add(bitmap) +class ImplantEditorView(BaseImplantEditorView): def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + BaseImplantEditorView.__init__ (self, parent) - pmainSizer = wx.BoxSizer(wx.HORIZONTAL) - - availableSizer = wx.BoxSizer(wx.VERTICAL) - - self.searchBox = SearchBox(self) - self.itemView = ItemView(self) - - self.itemView.Hide() - - availableSizer.Add(self.searchBox, 0, wx.EXPAND) - availableSizer.Add(self.itemView, 1, wx.EXPAND) - - ''' - self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) - self.availableImplantsSearch.ShowCancelButton(True) - - availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2) - ''' - - self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) - root = self.availableRoot = self.availableImplantsTree.AddRoot("Available") - self.availableImplantsImageList = wx.ImageList(16, 16) - self.availableImplantsTree.SetImageList(self.availableImplantsImageList) - - availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND) - - - pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) - - - buttonSizer = wx.BoxSizer(wx.VERTICAL) - buttonSizer.AddSpacer(( 0, 0), 1) - #pmainSizer.Add(buttonSizer, 0, wx.TOP, 5) - - self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style = wx.BORDER_NONE) - buttonSizer.Add(self.btnAdd, 0) - - self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style = wx.BORDER_NONE) - buttonSizer.Add(self.btnRemove, 0) - - buttonSizer.AddSpacer(( 0, 0), 1) - pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 0) - - characterImplantSizer = wx.BoxSizer(wx.VERTICAL) - self.pluggedImplantsTree = AvailableImplantsView(self) - characterImplantSizer.Add(self.pluggedImplantsTree, 1, wx.ALL|wx.EXPAND, 5) - pmainSizer.Add(characterImplantSizer, 1, wx.EXPAND, 5) - - self.SetSizer(pmainSizer) - - # Populate the market tree - - sMkt = service.Market.getInstance() - for mktGrp in sMkt.getImplantTree(): - iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp)) - childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) - if sMkt.marketGroupHasTypesCheck(mktGrp) is False: - self.availableImplantsTree.AppendItem(childId, "dummy") - - self.availableImplantsTree.SortChildren(self.availableRoot) - - #Bind the event to replace dummies by real data - self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) - self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.addImplant) - - self.itemView.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.itemActivated) - - #Bind add & remove buttons - self.btnAdd.Bind(wx.EVT_BUTTON, self.addImplant) - self.btnRemove.Bind(wx.EVT_BUTTON, self.removeImplant) - - #Bind the change of a character* - self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged) - - # We update with an empty list first to set the initial size for Layout(), then update later with actual - # implants for character. This helps with sizing issues. - self.update([]) - - self.Layout() + def bindContext(self): + self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.contextChanged) + def getImplantsFromContext(self): sChar = service.Character.getInstance() charID = self.Parent.Parent.getActiveCharacter() - self.update(sChar.getImplants(charID)) - def itemActivated(self, event): - sel = event.EventObject.GetFirstSelected() - item = self.itemView.items[sel] + return sChar.getImplants(charID) - if item: - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - - sChar.addImplant(charID, item.ID) - self.update(sChar.getImplants(charID)) - - def update(self, implants): - self.implants = implants[:] - self.implants.sort(key=lambda i: int(i.getModifiedItemAttr("implantness"))) - self.pluggedImplantsTree.update(self.implants) - - def charChanged(self, event): + def addImplantToContext(self, item): sChar = service.Character.getInstance() charID = self.Parent.Parent.getActiveCharacter() - name = sChar.getCharName(charID) - if name == "All 0" or name == "All 5": - self.Enable(False) - else: - self.Enable(True) - self.update(sChar.getImplants(charID)) - event.Skip() + sChar.addImplant(charID, item.ID) - def expandLookup(self, event): - tree = self.availableImplantsTree - sMkt = service.Market.getInstance() - parent = event.Item - child, _ = tree.GetFirstChild(parent) - text = tree.GetItemText(child) - - if text == "dummy" or text == "itemdummy": - tree.Delete(child) - - # if the dummy item is a market group, replace with actual market groups - if text == "dummy": - #Add 'real stoof!' instead - currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent), eager="children") - for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): - iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(childMktGrp)) - childId = tree.AppendItem(parent, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) - if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: - tree.AppendItem(childId, "dummy") - else: - tree.AppendItem(childId, "itemdummy") - - # replace dummy with actual items - if text == "itemdummy": - currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent)) - items = sMkt.getItemsByMarketGroup(currentMktGrp) - for item in items: - iconId = self.addMarketViewImage(item.icon.iconFile) - tree.AppendItem(parent, item.name, iconId, data=wx.TreeItemData(item.ID)) - - tree.SortChildren(parent) - - def addImplant(self, event): - root = self.availableImplantsTree.GetSelection() - - if not root.IsOk(): - return - - nchilds = self.availableImplantsTree.GetChildrenCount(root) + def removeImplantFromContext(self, pos): sChar = service.Character.getInstance() charID = self.Parent.Parent.getActiveCharacter() - if nchilds == 0: - itemID = self.availableImplantsTree.GetPyData(root) - sChar.addImplant(charID, itemID) - self.update(sChar.getImplants(charID)) - else: - event.Skip() - def removeImplant(self, event): - pos = self.pluggedImplantsTree.GetFirstSelected() - if pos != -1: - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - sChar.removeImplant(charID, self.implants[pos]) - self.update(sChar.getImplants(charID)) - -class AvailableImplantsView(d.Display): - DEFAULT_COLS = ["attr:implantness", - "Base Icon", - "Base Name"] - - def __init__(self, parent): - d.Display.__init__(self, parent, style=wx.LC_SINGLE_SEL) - - self.Bind(wx.EVT_LEFT_DCLICK, parent.removeImplant) - - #if "__WXGTK__" in wx.PlatformInfo: - # self.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) - #else: - # self.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) - -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) - self.parent = parent - self.searchBox = parent.searchBox - - self.items = [] - - # 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) - - def clearSearch(self, event=None): - if self.IsShown(): - self.parent.availableImplantsTree.Show() - self.Hide() - self.parent.Layout() - - if event: - self.searchBox.Clear() - - self.items = [] - self.update(self.items) - - 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, ["Implant"]) - - def populateSearch(self, items): - if not self.IsShown(): - self.parent.availableImplantsTree.Hide() - self.Show() - self.parent.Layout() - - self.items = sorted(list(items), key=lambda i: i.name) - - self.update(self.items) + sChar.removeImplant(charID, self.implants[pos]) class APIView (wx.Panel): diff --git a/gui/setEditor.py b/gui/setEditor.py index 5af848f42..14037b24b 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -19,14 +19,24 @@ import wx from gui.bitmapLoader import BitmapLoader +from gui.builtinViews.implantEditor import BaseImplantEditorView import service from gui.utils.clipboard import toClipboard, fromClipboard from service.targetResists import ImportError +class EditorView(BaseImplantEditorView): + def __init__(self, parent): + BaseImplantEditorView.__init__(self, parent) + self.parent = parent + + def getImplants(self): + return [] + + class ImplantSetEditorDlg(wx.Dialog): def __init__(self, parent): - wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Implant Set Editor", size = wx.Size( 350,240 )) + wx.Dialog.__init__(self, parent, id = wx.ID_ANY, title = u"Implant Set Editor", size = wx.Size(640, 600)) self.block = False self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) @@ -82,6 +92,9 @@ class ImplantSetEditorDlg(wx.Dialog): self.sl = wx.StaticLine(self) mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + self.iview = EditorView(self) + mainSizer.Add(self.iview, 0, wx.EXPAND) + contentSizer = wx.BoxSizer(wx.VERTICAL) self.slfooter = wx.StaticLine(self) From 80e47d51574f64080b4dd8a9cfb029a2bb4c93dd Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 20 Mar 2016 12:49:29 -0400 Subject: [PATCH 15/30] Add some functionality to implantSet service, and get editor working correctly with implant editor view --- eos/db/saveddata/implantSet.py | 2 +- gui/setEditor.py | 37 ++++++++++++++++++++++++++++------ service/character.py | 2 ++ service/implantSet.py | 15 ++++++++++++++ 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/eos/db/saveddata/implantSet.py b/eos/db/saveddata/implantSet.py index d72b9097a..73036de91 100644 --- a/eos/db/saveddata/implantSet.py +++ b/eos/db/saveddata/implantSet.py @@ -35,7 +35,7 @@ mapper(ImplantSet, implant_set_table, "_ImplantSet__implants": relation( Implant, collection_class = HandledImplantBoosterList, - cascade='all,delete-orphan', + cascade='all, delete, delete-orphan', backref='set', single_parent=True, primaryjoin = implantsSetMap_table.c.setID == implant_set_table.c.ID, diff --git a/gui/setEditor.py b/gui/setEditor.py index 14037b24b..935669306 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -24,13 +24,30 @@ import service from gui.utils.clipboard import toClipboard, fromClipboard from service.targetResists import ImportError -class EditorView(BaseImplantEditorView): +class ImplantSetEditor(BaseImplantEditorView): def __init__(self, parent): BaseImplantEditorView.__init__(self, parent) - self.parent = parent - def getImplants(self): - return [] + def bindContext(self): + self.Parent.ccSets.Bind(wx.EVT_CHOICE, self.contextChanged) + + def getImplantsFromContext(self): + sIS = service.ImplantSets.getInstance() + setID = self.Parent.getActiveSet() + + return sIS.getImplants(setID) + + def addImplantToContext(self, item): + sIS = service.ImplantSets.getInstance() + setID = self.Parent.getActiveSet() + + sIS.addImplant(setID, item.ID) + + def removeImplantFromContext(self, pos): + sIS = service.ImplantSets.getInstance() + setID = self.Parent.getActiveSet() + + sIS.removeImplant(setID, self.implants[pos]) class ImplantSetEditorDlg(wx.Dialog): @@ -51,7 +68,11 @@ class ImplantSetEditorDlg(wx.Dialog): # Sort the remaining list and continue on self.choices.sort(key=lambda s: s.name) - self.ccSets = wx.Choice(self, choices=map(lambda s: s.name, self.choices)) + self.ccSets = wx.Choice(self, wx.ID_ANY, style=0) + + for set in self.choices: + i = self.ccSets.Append(set.name, set.ID) + self.ccSets.Bind(wx.EVT_CHOICE, self.setChanged) self.ccSets.SetSelection(0) @@ -92,7 +113,7 @@ class ImplantSetEditorDlg(wx.Dialog): self.sl = wx.StaticLine(self) mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.iview = EditorView(self) + self.iview = ImplantSetEditor(self) mainSizer.Add(self.iview, 0, wx.EXPAND) contentSizer = wx.BoxSizer(wx.VERTICAL) @@ -152,6 +173,10 @@ class ImplantSetEditorDlg(wx.Dialog): def closeEvent(self, event): self.Destroy() + def getActiveSet(self): + selection = self.ccSets.GetCurrentSelection() + return self.ccSets.GetClientData(selection) if selection is not None else None + def ValuesUpdated(self, event=None): ''' Event that is fired when resists values change. Iterates through all diff --git a/service/character.py b/service/character.py index cf7e03102..6078ccd8a 100644 --- a/service/character.py +++ b/service/character.py @@ -351,10 +351,12 @@ class Character(object): implant = eos.types.Implant(eos.db.getItem(itemID)) char.implants.append(implant) + eos.db.commit() def removeImplant(self, charID, implant): char = eos.db.getCharacter(charID) char.implants.remove(implant) + eos.db.commit() def getImplants(self, charID): char = eos.db.getCharacter(charID) diff --git a/service/implantSet.py b/service/implantSet.py index ff7469536..7bb52c921 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -39,6 +39,21 @@ class ImplantSets(): def getImplantSet(self, name): return eos.db.getImplantSet(name) + def getImplants(self, setID): + set = eos.db.getImplantSet(setID) + return set.implants + + def addImplant(self, setID, itemID): + set = eos.db.getImplantSet(setID) + implant = eos.types.Implant(eos.db.getItem(itemID)) + set.implants.append(implant) + eos.db.commit() + + def removeImplant(self, setID, implant): + set = eos.db.getImplantSet(setID) + set.implants.remove(implant) + eos.db.commit() + def newSet(self): p = eos.types.ImplantSet() p.name = "" From 731b54a1f7cb879ec44942a03a916be7c350a06c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 20 Mar 2016 18:48:33 -0400 Subject: [PATCH 16/30] Continued work on implant set editor --- eos/db/saveddata/queries.py | 11 +- eos/saveddata/implantSet.py | 15 ++- gui/builtinViews/implantEditor.py | 2 +- gui/setEditor.py | 162 +++++++++--------------------- service/implantSet.py | 2 +- 5 files changed, 64 insertions(+), 128 deletions(-) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index 264e3c0da..de813ee1c 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -349,6 +349,12 @@ def getTargetResistsList(eager=None): patterns = saveddata_session.query(TargetResists).options(*eager).all() return patterns +def getImplantSetList(eager=None): + eager = processEager(eager) + with sd_lock: + sets = saveddata_session.query(ImplantSet).options(*eager).all() + return sets + @cachedQuery(DamagePattern, 1, "lookfor") def getDamagePattern(lookfor, eager=None): if isinstance(lookfor, int): @@ -399,11 +405,6 @@ def getImplantSet(lookfor, eager=None): eager = processEager(eager) with sd_lock: pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.name == lookfor).first() - elif lookfor is None: - eager = processEager(eager) - with sd_lock: - patterns = saveddata_session.query(ImplantSet).options(*eager).all() - return patterns else: raise TypeError("Improper argument") return pattern diff --git a/eos/saveddata/implantSet.py b/eos/saveddata/implantSet.py index 00df48536..34b4de6cf 100644 --- a/eos/saveddata/implantSet.py +++ b/eos/saveddata/implantSet.py @@ -18,10 +18,9 @@ #=============================================================================== from eos.effectHandlerHelpers import HandledImplantBoosterList +from copy import deepcopy class ImplantSet(object): - DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") - def __init__(self, name=None): self.name = name self.__implants = HandledImplantBoosterList() @@ -43,6 +42,12 @@ class ImplantSet(object): return out.strip() def __deepcopy__(self, memo): - p = ImplantSet(self.name) - p.name = "%s copy" % self.name - return p + copy = ImplantSet(self.name) + copy.name = "%s copy" % self.name + + orig = getattr(self, 'implants') + c = getattr(copy, 'implants') + for i in orig: + c.append(deepcopy(i, memo)) + + return copy diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index 615fc3da4..12da861c8 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -17,7 +17,7 @@ class BaseImplantEditorView (wx.Panel): return self.availableImplantsImageList.Add(bitmap) def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) + wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.HORIZONTAL) diff --git a/gui/setEditor.py b/gui/setEditor.py index 935669306..e9a0b8ce1 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -27,27 +27,29 @@ from service.targetResists import ImportError class ImplantSetEditor(BaseImplantEditorView): def __init__(self, parent): BaseImplantEditorView.__init__(self, parent) + if 'wxMSW' in wx.PlatformInfo: + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) def bindContext(self): self.Parent.ccSets.Bind(wx.EVT_CHOICE, self.contextChanged) def getImplantsFromContext(self): sIS = service.ImplantSets.getInstance() - setID = self.Parent.getActiveSet() + set = self.Parent.getActiveSet() - return sIS.getImplants(setID) + return sIS.getImplants(set.ID) def addImplantToContext(self, item): sIS = service.ImplantSets.getInstance() - setID = self.Parent.getActiveSet() + set = self.Parent.getActiveSet() - sIS.addImplant(setID, item.ID) + sIS.addImplant(set.ID, item.ID) def removeImplantFromContext(self, pos): sIS = service.ImplantSets.getInstance() - setID = self.Parent.getActiveSet() + set = self.Parent.getActiveSet() - sIS.removeImplant(setID, self.implants[pos]) + sIS.removeImplant(set.ID, self.implants[pos]) class ImplantSetEditorDlg(wx.Dialog): @@ -87,6 +89,7 @@ class ImplantSetEditorDlg(wx.Dialog): ("rename", BitmapLoader.getBitmap("rename", "gui")), ("copy", wx.ART_COPY), ("delete", wx.ART_DELETE)) + for name, art in buttons: bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) @@ -99,10 +102,9 @@ class ImplantSetEditorDlg(wx.Dialog): btn.Layout() setattr(self, name, btn) btn.Enable(True) - #btn.SetToolTipString("%s resist profile" % name.capitalize()) + btn.SetToolTipString("%s implant set" % name.capitalize()) headerSizer.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL) - self.btnSave = wx.Button(self, wx.ID_SAVE) self.btnSave.Hide() self.btnSave.Bind(wx.EVT_BUTTON, self.processRename) @@ -114,35 +116,22 @@ class ImplantSetEditorDlg(wx.Dialog): mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) self.iview = ImplantSetEditor(self) - mainSizer.Add(self.iview, 0, wx.EXPAND) - - contentSizer = wx.BoxSizer(wx.VERTICAL) + mainSizer.Add(self.iview, 1, wx.ALL | wx.EXPAND, 5) self.slfooter = wx.StaticLine(self) - contentSizer.Add(self.slfooter, 0, wx.EXPAND | wx.TOP, 5) + mainSizer.Add(self.slfooter, 0, wx.EXPAND | wx.TOP, 5) footerSizer = wx.BoxSizer(wx.HORIZONTAL) - perSizer = wx.BoxSizer(wx.VERTICAL) self.stNotice = wx.StaticText(self, wx.ID_ANY, u"") self.stNotice.Wrap(-1) - perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) - - footerSizer.Add(perSizer, 1, wx.ALIGN_CENTER_VERTICAL, 5) - - self.totSizer = wx.BoxSizer(wx.VERTICAL) - - contentSizer.Add(footerSizer, 0, wx.EXPAND, 5) - - mainSizer.Add(contentSizer, 1, wx.EXPAND, 0) + footerSizer.Add(self.stNotice, 1, wx.BOTTOM | wx.TOP | wx.LEFT, 5) if "wxGTK" in wx.PlatformInfo: self.closeBtn = wx.Button( self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0 ) mainSizer.Add( self.closeBtn, 0, wx.ALL|wx.ALIGN_RIGHT, 5 ) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) - self.SetSizer(mainSizer) - importExport = (("Import", wx.ART_FILE_OPEN, "from"), ("Export", wx.ART_FILE_SAVE_AS, "to")) @@ -156,17 +145,18 @@ class ImplantSetEditorDlg(wx.Dialog): btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction) ) + btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction) ) footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) - self.Layout() - bsize = self.GetBestSize() - self.SetSize((-1,bsize.height)) + mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5) - self.new.Bind(wx.EVT_BUTTON, self.newPattern) - self.rename.Bind(wx.EVT_BUTTON, self.renamePattern) - self.copy.Bind(wx.EVT_BUTTON, self.copyPattern) - self.delete.Bind(wx.EVT_BUTTON, self.deletePattern) + self.SetSizer(mainSizer) + self.Layout() + + self.new.Bind(wx.EVT_BUTTON, self.newSet) + self.rename.Bind(wx.EVT_BUTTON, self.renameSet) + self.copy.Bind(wx.EVT_BUTTON, self.copySet) + self.delete.Bind(wx.EVT_BUTTON, self.deleteSet) self.Import.Bind(wx.EVT_BUTTON, self.importPatterns) self.Export.Bind(wx.EVT_BUTTON, self.exportPatterns) @@ -177,68 +167,15 @@ class ImplantSetEditorDlg(wx.Dialog): selection = self.ccSets.GetCurrentSelection() return self.ccSets.GetClientData(selection) if selection is not None else None - def ValuesUpdated(self, event=None): - ''' - Event that is fired when resists values change. Iterates through all - resist edit fields. If blank, sets it to 0.0. If it is not a proper - decimal value, sets text color to red and refuses to save changes until - issue is resolved - ''' - if self.block: - return - - try: - p = self.getActivePattern() - - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - - if editObj.GetValue() == "": - # if we are blank, overwrite with 0 - editObj.ChangeValue("0.0") - editObj.SetInsertionPointEnd() - - value = float(editObj.GetValue()) - - # assertion, because they're easy - assert 0 <= value <= 100 - - # if everything checks out, set resist attribute - setattr(p, "%sAmount"%type, value/100) - editObj.SetForegroundColour(self.colorReset) - - self.stNotice.SetLabel("") - self.totSizer.Layout() - - if event is not None: - event.Skip() - - service.TargetResists.getInstance().saveChanges(p) - - except ValueError: - editObj.SetForegroundColour(wx.RED) - self.stNotice.SetLabel("Incorrect Formatting (decimals only)") - except AssertionError: - editObj.SetForegroundColour(wx.RED) - self.stNotice.SetLabel("Incorrect Range (must be 0-100)") - finally: # Refresh for color changes to take effect immediately - self.Refresh() - def restrict(self): - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - editObj.Enable(False) self.rename.Enable(False) self.delete.Enable(False) def unrestrict(self): - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - editObj.Enable() self.rename.Enable() self.delete.Enable() - def getActivePattern(self): + def getActiveSet(self): if len(self.choices) == 0: return None @@ -246,40 +183,34 @@ class ImplantSetEditorDlg(wx.Dialog): def setChanged(self, event=None): "Event fired when user selects pattern. Can also be called from script" - p = self.getActivePattern() + p = self.getActiveSet() + self.iview.update() if p is None: # This happens when there are no patterns in the DB. As such, force # user to create one first or exit dlg. - self.newPattern(None) + self.newSet(None) return - #ValuesUpdated() - - def newPattern(self, event): + def newSet(self, event): ''' - Simply does new-pattern specifics: replaces label on button, restricts, + Simply does new-set specifics: replaces label on button, restricts, and resets values to default. Hands off to the rename function for further handling. ''' self.btnSave.SetLabel("Create") self.restrict() - # reset values - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - editObj.ChangeValue("0.0") - editObj.SetForegroundColour(self.colorReset) self.Refresh() - self.renamePattern() + self.renameSet() - def renamePattern(self, event=None): + def renameSet(self, event=None): "Changes layout to facilitate naming a pattern" self.showInput(True) if event is not None: # Rename mode self.btnSave.SetLabel("Rename") - self.namePicker.SetValue(self.getActivePattern().name) + self.namePicker.SetValue(self.getActiveSet().name) else: # Create mode self.namePicker.SetValue("") @@ -301,42 +232,41 @@ class ImplantSetEditorDlg(wx.Dialog): self.stNotice.SetLabel("Invalid name") return - sTR = service.TargetResists.getInstance() + sIS = service.ImplantSets.getInstance() if self.btnSave.Label == "Create": - p = sTR.newPattern() + s = sIS.newSet() else: # we are renaming, so get the current selection - p = self.getActivePattern() + s = self.getActiveSet() # test for patterns of the same name - for pattern in self.choices: - if pattern.name == newName and p != pattern: + for set in self.choices: + if set.name == newName and s != set: self.stNotice.SetLabel("Name already used, please choose another") return # rename regardless of new or rename - sTR.renamePattern(p, newName) + sIS.renameSet(s, newName) self.updateChoices(newName) self.showInput(False) sel = self.ccSets.GetSelection() - self.ValuesUpdated() self.unrestrict() - def copyPattern(self,event): - sTR = service.TargetResists.getInstance() - p = sTR.copyPattern(self.getActivePattern()) + def copySet(self,event): + sIS = service.ImplantSets.getInstance() + p = sIS.copySet(self.getActiveSet()) self.choices.append(p) id = self.ccSets.Append(p.name) self.ccSets.SetSelection(id) self.btnSave.SetLabel("Copy") - self.renamePattern() + self.renameSet() self.setChanged() - def deletePattern(self,event): - sTR = service.TargetResists.getInstance() + def deleteSet(self,event): + sIS = service.ImplantSets.getInstance() sel = self.ccSets.GetSelection() - sTR.deletePattern(self.getActivePattern()) + sIS.deleteSet(self.getActiveSet()) self.ccSets.Delete(sel) self.ccSets.SetSelection(max(0, sel - 1)) del self.choices[sel] @@ -369,8 +299,8 @@ class ImplantSetEditorDlg(wx.Dialog): def updateChoices(self, select=None): "Gathers list of patterns and updates choice selections" - sTR = service.TargetResists.getInstance() - self.choices = sTR.getTargetResistsList() + sIS = service.ImplantSets.getInstance() + self.choices = sIS.getImplantSetList() if len(self.choices) == 0: #self.newPattern(None) diff --git a/service/implantSet.py b/service/implantSet.py index 7bb52c921..1b1b500a5 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -34,7 +34,7 @@ class ImplantSets(): return cls.instance def getImplantSetList(self): - return eos.db.getImplantSet(None) + return eos.db.getImplantSetList(None) def getImplantSet(self, name): return eos.db.getImplantSet(name) From 145f252ca674b7f4b6daf70ece4432c64347c0d3 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 20 Mar 2016 19:49:53 -0400 Subject: [PATCH 17/30] Add icon for implant set editor --- gui/mainMenuBar.py | 4 ++-- imgs/gui/hardwire_small.png | Bin 0 -> 762 bytes 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 imgs/gui/hardwire_small.png diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index fda1955d9..e8c3cb614 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -102,11 +102,11 @@ class MainMenuBar(wx.MenuBar): windowMenu.AppendItem(damagePatternEditItem) targetResistsEditItem = wx.MenuItem(windowMenu, self.targetResistsEditorId, "Target Resists Editor\tCTRL+R") - targetResistsEditItem.SetBitmap(BitmapLoader.getBitmap("explosive_big", "gui")) + targetResistsEditItem.SetBitmap(BitmapLoader.getBitmap("explosive_small", "gui")) windowMenu.AppendItem(targetResistsEditItem) implantSetEditItem = wx.MenuItem(windowMenu, self.implantSetEditorId, "Implant Set Editor\tCTRL+I") - implantSetEditItem.SetBitmap(BitmapLoader.getBitmap("damagePattern_small", "gui")) + implantSetEditItem.SetBitmap(BitmapLoader.getBitmap("hardwire_small", "gui")) windowMenu.AppendItem(implantSetEditItem) graphFrameItem = wx.MenuItem(windowMenu, self.graphFrameId, "Graphs\tCTRL+G") diff --git a/imgs/gui/hardwire_small.png b/imgs/gui/hardwire_small.png new file mode 100644 index 0000000000000000000000000000000000000000..58b63b045d781a0aad1db48a53ba8e0c3f104f5a GIT binary patch literal 762 zcmVCz>z(8Ps1{{r2Zu+T)~ zTQMb#QG>)*X!_8i6|ENJIWx=v_qq_8Hr8)dvD&j`fzIMlV{D^cKG(O z!nXbEVOwQQA2_u8`r-BAJ3F2JIk=RK{xI?sb14cw56e41biE=)p5_GYt%PqT|?dDv%&SysRLYBevbA|#A1QA+- zXc`k_LI}iS(bCxH2g;QzqQ*H^m{(FZ!$sl8cIK6j_J9K+5fc9Z{C>Xx08KNThEau~ zD8x)A*c_{1x7g9Q?Ff`##*)DW4;B})EZfT!4$gqKHY zG~4R{I0Jx4Bmw|<^yHPzA}W}fo`qy&G5cu}@puy1`FSX+S_i;XRb2r<24Gn@ArSbB z?B7yX#}n_lmYc@-`!`}__%$|eSj{D7;(~zzun_5X0PO&302<8`0sta#`1aWxjWXM@ zwkA!ai`3umA%o1ObuI_gqNV`oRTRSuU<`n^;6Wg8nghVsIl5vovun2>rY@jf-JQ*q z5`%+%rnm7?|EpO1Er5YW14RIzTS07YATTs^%Evf0B$-KlAMu3m=( sz}DQL)`GOuiYOZx*xR#QXqM^4AJvUCyO?n*(*OVf07*qoM6N<$f=a@3?f?J) literal 0 HcmV?d00001 From d4632b90598d709bbf3e39e38f3e8d3f510cebb1 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 20 Mar 2016 22:28:18 -0400 Subject: [PATCH 18/30] Add context menu to apply implant set to fit --- gui/builtinContextMenus/__init__.py | 1 + gui/builtinContextMenus/implantSets.py | 53 ++++++++++++++++++++++++++ gui/implantView.py | 17 ++++++++- 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 gui/builtinContextMenus/implantSets.py diff --git a/gui/builtinContextMenus/__init__.py b/gui/builtinContextMenus/__init__.py index 785e3eebf..c2a39b179 100644 --- a/gui/builtinContextMenus/__init__.py +++ b/gui/builtinContextMenus/__init__.py @@ -20,4 +20,5 @@ __all__ = [ "priceClear", "amount", "metaSwap", + "implantSets", ] diff --git a/gui/builtinContextMenus/implantSets.py b/gui/builtinContextMenus/implantSets.py new file mode 100644 index 000000000..c96080983 --- /dev/null +++ b/gui/builtinContextMenus/implantSets.py @@ -0,0 +1,53 @@ +from gui.contextMenu import ContextMenu +from gui.itemStats import ItemStatsDialog +import eos.types +import gui.mainFrame +import service +import gui.globalEvents as GE +import wx + +class ImplantSets(ContextMenu): + def __init__(self): + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + + def display(self, srcContext, selection): + return srcContext == "implantView" + + def getText(self, itmContext, selection): + return "Add Implant Set" + + def getSubMenu(self, context, selection, rootMenu, i, pitem): + m = wx.Menu() + bindmenu = rootMenu if "wxMSW" in wx.PlatformInfo else m + + sIS = service.ImplantSets.getInstance() + implantSets = sIS.getImplantSetList() + + self.idmap = {} + + for set in implantSets: + id = wx.NewId() + mitem = wx.MenuItem(rootMenu, id, set.name) + bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem) + self.idmap[id] = set + m.AppendItem(mitem) + + return m + + def handleSelection(self, event): + set = self.idmap.get(event.Id, None) + + if set is None: + event.Skip() + return + + sFit = service.Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + for implant in set.implants: + print implant.item.ID, implant.item.name + sFit.addImplant(fitID, implant.item.ID) + + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + + +ImplantSets.register() diff --git a/gui/implantView.py b/gui/implantView.py index 5c065094a..4b4755252 100644 --- a/gui/implantView.py +++ b/gui/implantView.py @@ -172,9 +172,15 @@ class ImplantDisplay(d.Display): def spawnMenu(self): sel = self.GetFirstSelected() + menu = None + + sFit = service.Fit.getInstance() + fit = sFit.getFit(self.mainFrame.getActiveFit()) + + if not fit: + return + if sel != -1: - sFit = service.Fit.getInstance() - fit = sFit.getFit(self.mainFrame.getActiveFit()) implant = fit.appliedImplants[sel] sMkt = service.Market.getInstance() @@ -182,4 +188,11 @@ class ImplantDisplay(d.Display): itemContext = sMkt.getCategoryByItem(implant.item).name menu = ContextMenu.getMenu((implant,), (sourceContext, itemContext)) + elif sel == -1 and fit.implantSource == ImplantLocation.FIT: + fitID = self.mainFrame.getActiveFit() + if fitID is None: + return + context = (("implantView",),) + menu = ContextMenu.getMenu([], *context) + if menu is not None: self.PopupMenu(menu) From 94f73241eaaf02785908cce395612d6ae5e2c401 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 20 Mar 2016 23:45:42 -0400 Subject: [PATCH 19/30] Implement implant set application in character implants. --- gui/builtinContextMenus/implantSets.py | 43 ++++++++++++++++++++------ gui/builtinContextMenus/itemStats.py | 1 + gui/characterEditor.py | 16 ++++++++++ service/fit.py | 5 +-- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/gui/builtinContextMenus/implantSets.py b/gui/builtinContextMenus/implantSets.py index c96080983..6ae43039b 100644 --- a/gui/builtinContextMenus/implantSets.py +++ b/gui/builtinContextMenus/implantSets.py @@ -1,6 +1,4 @@ from gui.contextMenu import ContextMenu -from gui.itemStats import ItemStatsDialog -import eos.types import gui.mainFrame import service import gui.globalEvents as GE @@ -11,18 +9,36 @@ class ImplantSets(ContextMenu): self.mainFrame = gui.mainFrame.MainFrame.getInstance() def display(self, srcContext, selection): - return srcContext == "implantView" + return srcContext in ("implantView", "implantEditor") def getText(self, itmContext, selection): return "Add Implant Set" def getSubMenu(self, context, selection, rootMenu, i, pitem): + """ + A note on the selection here: Most context menus act on a fit, so it's easy enough to get the active fit from + the MainFrame instance. There's never been a reason to get info from another window, so there's not common + way of doing this. However, we use this context menu within the Character Editor to apply implant sets to a + character, so we need to access the character editor. + + It is for these reasons that I hijack the selection parameter when calling the menu and pass a pointer to the + Character Editor. This way we can use it to get current editing character ID and apply the implants. + + It would probably be better to have a function on the MainFrame to get the currently open Character Editor (as + we do with the item stats window). Eventually... Until then, this long ass note will remain to remind me why + stupid shit like this is even happening. + """ + m = wx.Menu() bindmenu = rootMenu if "wxMSW" in wx.PlatformInfo else m sIS = service.ImplantSets.getInstance() implantSets = sIS.getImplantSetList() + self.context = context + if len(selection) == 1: + self.selection = selection[0] # dirty hack here + self.idmap = {} for set in implantSets: @@ -41,13 +57,22 @@ class ImplantSets(ContextMenu): event.Skip() return - sFit = service.Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - for implant in set.implants: - print implant.item.ID, implant.item.name - sFit.addImplant(fitID, implant.item.ID) + if self.context == "implantEditor": + # we are calling from character editor, the implant source is different + sChar = service.Character.getInstance() + charID = self.selection.getActiveCharacter() - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + for implant in set.implants: + sChar.addImplant(charID, implant.item.ID) + + wx.PostEvent(self.selection, GE.CharChanged()) + else: + sFit = service.Fit.getInstance() + fitID = self.mainFrame.getActiveFit() + for implant in set.implants: + sFit.addImplant(fitID, implant.item.ID, recalc=implant == set.implants[-1]) + + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) ImplantSets.register() diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index 40e94164c..3fde45c97 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -9,6 +9,7 @@ class ItemStats(ContextMenu): self.mainFrame = gui.mainFrame.MainFrame.getInstance() def display(self, srcContext, selection): + return srcContext in ("marketItemGroup", "marketItemMisc", "fittingModule", "fittingCharge", "fittingShip", "baseShip", diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 697862146..4be9fc5ee 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -460,6 +460,11 @@ class ImplantEditorView(BaseImplantEditorView): def __init__(self, parent): BaseImplantEditorView.__init__ (self, parent) + if "__WXGTK__" in wx.PlatformInfo: + self.pluggedImplantsTree.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) + else: + self.pluggedImplantsTree.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) + def bindContext(self): self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.contextChanged) @@ -481,6 +486,17 @@ class ImplantEditorView(BaseImplantEditorView): sChar.removeImplant(charID, self.implants[pos]) + def scheduleMenu(self, event): + event.Skip() + wx.CallAfter(self.spawnMenu) + + def spawnMenu(self): + context = (("implantEditor",),) + # fuck good coding practices, passing a pointer to the character editor here for [reasons] =D + # (see implantSets context class for info) + menu = ContextMenu.getMenu((self.Parent.Parent,), *context) + self.PopupMenu(menu) + class APIView (wx.Panel): def __init__(self, parent): diff --git a/service/fit.py b/service/fit.py index ae1d4ec40..f2ddd7523 100644 --- a/service/fit.py +++ b/service/fit.py @@ -277,7 +277,7 @@ class Fit(object): fit.timestamp)) return fits - def addImplant(self, fitID, itemID): + def addImplant(self, fitID, itemID, recalc=True): if fitID is None: return False @@ -289,7 +289,8 @@ class Fit(object): return False fit.implants.append(implant) - self.recalc(fit) + if recalc: + self.recalc(fit) return True def removeImplant(self, fitID, position): From 3ae312db377fc5b37465e42e8a6db0af6314d35c Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 21 Mar 2016 23:43:45 -0400 Subject: [PATCH 20/30] Import / export implants sets --- eos/saveddata/implantSet.py | 21 ++++++++++------ gui/setEditor.py | 18 ++++++++------ service/implantSet.py | 49 +++++++++++++++++++++++++++++++++++++ service/market.py | 36 ++++++++++++++++----------- 4 files changed, 95 insertions(+), 29 deletions(-) diff --git a/eos/saveddata/implantSet.py b/eos/saveddata/implantSet.py index 34b4de6cf..8e495c5ea 100644 --- a/eos/saveddata/implantSet.py +++ b/eos/saveddata/implantSet.py @@ -29,15 +29,20 @@ class ImplantSet(object): def implants(self): return self.__implants - - EXPORT_FORMAT = "ImplantSet = %s,%d,%d,%d,%d\n" @classmethod - def exportPatterns(cls, *patterns): - out = "# Exported from pyfa\n#\n" - out += "# Values are in following format:\n" - out += "# DamageProfile = [name],[EM amount],[Thermal amount],[Kinetic amount],[Explosive amount]\n\n" - for dp in patterns: - out += cls.EXPORT_FORMAT % (dp.name, dp.emAmount, dp.thermalAmount, dp.kineticAmount, dp.explosiveAmount) + def exportSets(cls, *sets): + out = "# Exported from pyfa\n#\n" \ + "# Values are in following format:\n" \ + "# [Implant Set name]\n" \ + "# [Implant name]\n" \ + "# [Implant name]\n" \ + "# ...\n\n" + + for set in sets: + out += "[{}]\n".format(set.name) + for implant in set.implants: + out += "{}\n".format(implant.item.name) + out += "\n" return out.strip() diff --git a/gui/setEditor.py b/gui/setEditor.py index e9a0b8ce1..eed2afa1e 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -22,7 +22,10 @@ from gui.bitmapLoader import BitmapLoader from gui.builtinViews.implantEditor import BaseImplantEditorView import service from gui.utils.clipboard import toClipboard, fromClipboard -from service.targetResists import ImportError +from service.implantSet import ImportError +import logging + +logger = logging.getLogger(__name__) class ImplantSetEditor(BaseImplantEditorView): def __init__(self, parent): @@ -326,14 +329,15 @@ class ImplantSetEditorDlg(wx.Dialog): text = fromClipboard() if text: - sTR = service.TargetResists.getInstance() + sIS = service.ImplantSets.getInstance() try: - sTR.importPatterns(text) + sIS.importSets(text) self.stNotice.SetLabel("Patterns successfully imported from clipboard") self.showInput(False) - except service.targetResists.ImportError, e: + except ImportError, e: self.stNotice.SetLabel(str(e)) except Exception, e: + logging.exception("Unhandled Exception") self.stNotice.SetLabel("Could not import from clipboard: unknown errors") finally: self.updateChoices() @@ -343,6 +347,6 @@ class ImplantSetEditorDlg(wx.Dialog): def exportPatterns(self, event): "Event fired when export to clipboard button is clicked" - sTR = service.TargetResists.getInstance() - toClipboard( sTR.exportPatterns() ) - self.stNotice.SetLabel("Patterns exported to clipboard") + sIS = service.ImplantSets.getInstance() + toClipboard(sIS.exportSets()) + self.stNotice.SetLabel("Sets exported to clipboard") diff --git a/service/implantSet.py b/service/implantSet.py index 1b1b500a5..4a517f0ca 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -20,6 +20,7 @@ import eos.db import eos.types import copy +import service.market class ImportError(Exception): pass @@ -73,3 +74,51 @@ class ImplantSets(): def saveChanges(self, s): eos.db.save(s) + + def importSets(self, text): + sMkt = service.Market.getInstance() + lines = text.splitlines() + newSets = [] + errors = 0 + current = None + lookup = {} + + for i, line in enumerate(lines): + line = line.strip() + try: + if line == '' or line[0] == "#": # comments / empty string + continue + if line[:1] == "[" and line[-1:] == "]": + current = eos.types.ImplantSet(line[1:-1]) + newSets.append(current) + else: + item = sMkt.getItem(line) + current.implants.append(eos.types.Implant(item)) + except: + errors += 1 + continue + + for set in self.getImplantSetList(): + lookup[set.name] = set + + for set in newSets: + if set.name in lookup: + match = lookup[set.name] + for implant in set.implants: + match.implants.append(eos.types.Implant(implant.item)) + else: + eos.db.save(set) + + eos.db.commit() + + lenImports = len(newSets) + if lenImports == 0: + raise ImportError("No patterns found for import") + if errors > 0: + raise ImportError("%d sets imported from clipboard; %d errors"%(lenImports, errors)) + + def exportSets(self): + patterns = self.getImplantSetList() + patterns.sort(key=lambda p: p.name) + return eos.types.ImplantSet.exportSets(*patterns) + diff --git a/service/market.py b/service/market.py index b8c0ab341..8d8fe3810 100644 --- a/service/market.py +++ b/service/market.py @@ -29,12 +29,15 @@ import eos.types from service.settings import SettingsProvider, NetworkSettings import service import service.conversions as conversions +import logging try: from collections import OrderedDict except ImportError: from utils.compat import OrderedDict +logger = logging.getLogger(__name__) + # Event which tells threads dependent on Market that it's initialized mktRdy = threading.Event() @@ -349,20 +352,25 @@ class Market(): def getItem(self, identity, *args, **kwargs): """Get item by its ID or name""" - if isinstance(identity, eos.types.Item): - item = identity - elif isinstance(identity, int): - item = eos.db.getItem(identity, *args, **kwargs) - elif isinstance(identity, basestring): - # We normally lookup with string when we are using import/export - # features. Check against overrides - identity = conversions.all.get(identity, identity) - item = eos.db.getItem(identity, *args, **kwargs) - elif isinstance(identity, float): - id = int(identity) - item = eos.db.getItem(id, *args, **kwargs) - else: - raise TypeError("Need Item object, integer, float or string as argument") + try: + if isinstance(identity, eos.types.Item): + item = identity + elif isinstance(identity, int): + item = eos.db.getItem(identity, *args, **kwargs) + elif isinstance(identity, basestring): + # We normally lookup with string when we are using import/export + # features. Check against overrides + identity = conversions.all.get(identity, identity) + item = eos.db.getItem(identity, *args, **kwargs) + elif isinstance(identity, float): + id = int(identity) + item = eos.db.getItem(id, *args, **kwargs) + else: + raise TypeError("Need Item object, integer, float or string as argument") + except: + logger.error("Could not get item: %s", identity) + raise + return item def getGroup(self, identity, *args, **kwargs): From 24bb0ff39a13915eff9dedad66910e4307677823 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 21 Mar 2016 23:49:01 -0400 Subject: [PATCH 21/30] Fix issue in which implant editor did not spawn if no implant sets --- gui/setEditor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gui/setEditor.py b/gui/setEditor.py index eed2afa1e..293ff5c76 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -39,8 +39,9 @@ class ImplantSetEditor(BaseImplantEditorView): def getImplantsFromContext(self): sIS = service.ImplantSets.getInstance() set = self.Parent.getActiveSet() - - return sIS.getImplants(set.ID) + if set: + return sIS.getImplants(set.ID) + return [] def addImplantToContext(self, item): sIS = service.ImplantSets.getInstance() From 9b1c543eb7d999ba128579de60fbe85cfe602f33 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 21 Mar 2016 23:56:00 -0400 Subject: [PATCH 22/30] Support multi-select --- gui/builtinViews/implantEditor.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index 12da861c8..f12b5fcc1 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -165,8 +165,13 @@ class BaseImplantEditorView (wx.Panel): event.EventObject = self.availableImplantsTree if event.EventObject is self.itemView: - sel = event.EventObject.GetFirstSelected() - item = self.itemView.items[sel] + curr = event.EventObject.GetFirstSelected() + + while curr != -1: + item = self.itemView.items[curr] + self.addImplantToContext(item) + + curr = event.EventObject.GetNextSelected(curr) else: root = self.availableImplantsTree.GetSelection() @@ -176,13 +181,12 @@ class BaseImplantEditorView (wx.Panel): nchilds = self.availableImplantsTree.GetChildrenCount(root) if nchilds == 0: item = self.availableImplantsTree.GetPyData(root) + self.addImplantToContext(item) else: event.Skip() return - if item: - self.addImplantToContext(item) - self.update() + self.update() def removeItem(self, event): pos = self.pluggedImplantsTree.GetFirstSelected() From 71d33f34291fd668487ee2bba627fe0da84d72ff Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 22 Mar 2016 23:16:31 -0400 Subject: [PATCH 23/30] Fit implant effects (now applies to whichever implant source is selected) --- eos/effects/angelsetbonus.py | 2 +- eos/effects/caldarisetbonus3.py | 2 +- eos/effects/caldarisetlgbonus.py | 2 +- eos/effects/federationsetbonus3.py | 2 +- eos/effects/federationsetlgbonus.py | 2 +- eos/effects/imperialsetbonus3.py | 2 +- eos/effects/imperialsetlgbonus.py | 2 +- eos/effects/implantsetwarpspeed.py | 2 +- eos/effects/republicsetbonus3.py | 2 +- eos/effects/republicsetlgbonus.py | 2 +- eos/effects/setbonusbloodraider.py | 2 +- eos/effects/setbonuschristmasagilitybonus.py | 2 +- eos/effects/setbonuschristmasarmorhpbonus2.py | 2 +- eos/effects/setbonuschristmasbonusvelocity.py | 2 +- eos/effects/setbonuschristmascapacitorcapacity.py | 2 +- eos/effects/setbonuschristmascapacitorrecharge2.py | 2 +- eos/effects/setbonuschristmascpuoutput.py | 2 +- eos/effects/setbonuschristmaspowergrid.py | 2 +- eos/effects/setbonuschristmasshieldcapacitybonus.py | 2 +- eos/effects/setbonusguristas.py | 2 +- eos/effects/setbonusmordus.py | 2 +- eos/effects/setbonusore.py | 2 +- eos/effects/setbonussansha.py | 3 ++- eos/effects/setbonusserpentis.py | 2 +- eos/effects/setbonussisters.py | 2 +- eos/effects/setbonussyndicate.py | 2 +- eos/effects/setbonusthukker.py | 2 +- 27 files changed, 28 insertions(+), 27 deletions(-) diff --git a/eos/effects/angelsetbonus.py b/eos/effects/angelsetbonus.py index b2e5f449b..711d99c9d 100644 --- a/eos/effects/angelsetbonus.py +++ b/eos/effects/angelsetbonus.py @@ -5,7 +5,7 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply( + fit.appliedImplants.filteredItemMultiply( lambda implant: "signatureRadiusBonus" in implant.itemModifiedAttributes and "implantSetAngel" in implant.itemModifiedAttributes, "signatureRadiusBonus", implant.getModifiedItemAttr("implantSetAngel")) \ No newline at end of file diff --git a/eos/effects/caldarisetbonus3.py b/eos/effects/caldarisetbonus3.py index 40aade2ac..a31e831e6 100644 --- a/eos/effects/caldarisetbonus3.py +++ b/eos/effects/caldarisetbonus3.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), + fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "scanGravimetricStrengthPercent", implant.getModifiedItemAttr("implantSetCaldariNavy")) diff --git a/eos/effects/caldarisetlgbonus.py b/eos/effects/caldarisetlgbonus.py index abf7ecd8e..04d1c6f74 100644 --- a/eos/effects/caldarisetlgbonus.py +++ b/eos/effects/caldarisetlgbonus.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), + fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "scanGravimetricStrengthModifier", implant.getModifiedItemAttr("implantSetLGCaldariNavy")) diff --git a/eos/effects/federationsetbonus3.py b/eos/effects/federationsetbonus3.py index aa25dfcc4..b97317b82 100644 --- a/eos/effects/federationsetbonus3.py +++ b/eos/effects/federationsetbonus3.py @@ -5,5 +5,5 @@ type = "passive" runTime = "early" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), + fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "scanMagnetometricStrengthPercent", implant.getModifiedItemAttr("implantSetFederationNavy")) diff --git a/eos/effects/federationsetlgbonus.py b/eos/effects/federationsetlgbonus.py index 27c320be9..4328417cd 100644 --- a/eos/effects/federationsetlgbonus.py +++ b/eos/effects/federationsetlgbonus.py @@ -5,5 +5,5 @@ type = "passive" runTime = "early" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), + fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "scanMagnetometricStrengthModifier", implant.getModifiedItemAttr("implantSetLGFederationNavy")) diff --git a/eos/effects/imperialsetbonus3.py b/eos/effects/imperialsetbonus3.py index 7c764dcad..d239dcc35 100644 --- a/eos/effects/imperialsetbonus3.py +++ b/eos/effects/imperialsetbonus3.py @@ -5,5 +5,5 @@ type = "passive" runTime = "early" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), + fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "scanRadarStrengthPercent", implant.getModifiedItemAttr("implantSetImperialNavy")) diff --git a/eos/effects/imperialsetlgbonus.py b/eos/effects/imperialsetlgbonus.py index 6495b9b81..56369bf22 100644 --- a/eos/effects/imperialsetlgbonus.py +++ b/eos/effects/imperialsetlgbonus.py @@ -5,5 +5,5 @@ type = "passive" runTime = "early" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), + fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "scanRadarStrengthModifier", implant.getModifiedItemAttr("implantSetLGImperialNavy")) diff --git a/eos/effects/implantsetwarpspeed.py b/eos/effects/implantsetwarpspeed.py index a6410ed77..29fd50e49 100644 --- a/eos/effects/implantsetwarpspeed.py +++ b/eos/effects/implantsetwarpspeed.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "WarpSBonus", implant.getModifiedItemAttr("implantSetWarpSpeed")) diff --git a/eos/effects/republicsetbonus3.py b/eos/effects/republicsetbonus3.py index 1f3cad903..5709959ae 100644 --- a/eos/effects/republicsetbonus3.py +++ b/eos/effects/republicsetbonus3.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), + fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "scanLadarStrengthPercent", implant.getModifiedItemAttr("implantSetRepublicFleet")) diff --git a/eos/effects/republicsetlgbonus.py b/eos/effects/republicsetlgbonus.py index a80ae38e7..bf79499ea 100644 --- a/eos/effects/republicsetlgbonus.py +++ b/eos/effects/republicsetlgbonus.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), + fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "scanLadarStrengthModifier", implant.getModifiedItemAttr("implantSetLGRepublicFleet")) diff --git a/eos/effects/setbonusbloodraider.py b/eos/effects/setbonusbloodraider.py index 718be78e6..73fb305ec 100644 --- a/eos/effects/setbonusbloodraider.py +++ b/eos/effects/setbonusbloodraider.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "durationBonus", implant.getModifiedItemAttr("implantSetBloodraider")) diff --git a/eos/effects/setbonuschristmasagilitybonus.py b/eos/effects/setbonuschristmasagilitybonus.py index 857cad355..f0ca5c03f 100644 --- a/eos/effects/setbonuschristmasagilitybonus.py +++ b/eos/effects/setbonuschristmasagilitybonus.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "agilityBonus", implant.getModifiedItemAttr("implantSetChristmas")) diff --git a/eos/effects/setbonuschristmasarmorhpbonus2.py b/eos/effects/setbonuschristmasarmorhpbonus2.py index 971644d02..9f5c52fd4 100644 --- a/eos/effects/setbonuschristmasarmorhpbonus2.py +++ b/eos/effects/setbonuschristmasarmorhpbonus2.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "armorHpBonus2", implant.getModifiedItemAttr("implantSetChristmas")) diff --git a/eos/effects/setbonuschristmasbonusvelocity.py b/eos/effects/setbonuschristmasbonusvelocity.py index 1a0d7ffcd..09b1ef21f 100644 --- a/eos/effects/setbonuschristmasbonusvelocity.py +++ b/eos/effects/setbonuschristmasbonusvelocity.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "implantBonusVelocity", implant.getModifiedItemAttr("implantSetChristmas")) diff --git a/eos/effects/setbonuschristmascapacitorcapacity.py b/eos/effects/setbonuschristmascapacitorcapacity.py index a0ba4bdd0..cc949b255 100644 --- a/eos/effects/setbonuschristmascapacitorcapacity.py +++ b/eos/effects/setbonuschristmascapacitorcapacity.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "capacitorCapacityBonus", implant.getModifiedItemAttr("implantSetChristmas")) diff --git a/eos/effects/setbonuschristmascapacitorrecharge2.py b/eos/effects/setbonuschristmascapacitorrecharge2.py index 7f31e1818..a526176e4 100644 --- a/eos/effects/setbonuschristmascapacitorrecharge2.py +++ b/eos/effects/setbonuschristmascapacitorrecharge2.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "capRechargeBonus", implant.getModifiedItemAttr("implantSetChristmas")) diff --git a/eos/effects/setbonuschristmascpuoutput.py b/eos/effects/setbonuschristmascpuoutput.py index 5ba1d538a..e264c8dd5 100644 --- a/eos/effects/setbonuschristmascpuoutput.py +++ b/eos/effects/setbonuschristmascpuoutput.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "cpuOutputBonus2", implant.getModifiedItemAttr("implantSetChristmas")) diff --git a/eos/effects/setbonuschristmaspowergrid.py b/eos/effects/setbonuschristmaspowergrid.py index b0fc46366..9c39919be 100644 --- a/eos/effects/setbonuschristmaspowergrid.py +++ b/eos/effects/setbonuschristmaspowergrid.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "powerEngineeringOutputBonus", implant.getModifiedItemAttr("implantSetChristmas")) diff --git a/eos/effects/setbonuschristmasshieldcapacitybonus.py b/eos/effects/setbonuschristmasshieldcapacitybonus.py index 5841a565e..c604d0281 100644 --- a/eos/effects/setbonuschristmasshieldcapacitybonus.py +++ b/eos/effects/setbonuschristmasshieldcapacitybonus.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "shieldCapacityBonus", implant.getModifiedItemAttr("implantSetChristmas")) diff --git a/eos/effects/setbonusguristas.py b/eos/effects/setbonusguristas.py index e0e6a14bf..34bbfacc2 100644 --- a/eos/effects/setbonusguristas.py +++ b/eos/effects/setbonusguristas.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "shieldBoostMultiplier", implant.getModifiedItemAttr("implantSetGuristas")) diff --git a/eos/effects/setbonusmordus.py b/eos/effects/setbonusmordus.py index 3bcdb2c07..eef3675f9 100644 --- a/eos/effects/setbonusmordus.py +++ b/eos/effects/setbonusmordus.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "rangeSkillBonus", implant.getModifiedItemAttr("implantSetMordus")) diff --git a/eos/effects/setbonusore.py b/eos/effects/setbonusore.py index 8e540b81c..a7342a8fa 100644 --- a/eos/effects/setbonusore.py +++ b/eos/effects/setbonusore.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "maxRangeBonus", implant.getModifiedItemAttr("implantSetORE")) diff --git a/eos/effects/setbonussansha.py b/eos/effects/setbonussansha.py index e7e0d261c..6aad859b9 100644 --- a/eos/effects/setbonussansha.py +++ b/eos/effects/setbonussansha.py @@ -6,5 +6,6 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), + print "applying set bonus to implants on ", fit, fit.appliedImplants, " from: ", implant, implant.item.name + fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "armorHpBonus", implant.getModifiedItemAttr("implantSetSansha") or 1) diff --git a/eos/effects/setbonusserpentis.py b/eos/effects/setbonusserpentis.py index 4775993c0..f4de04dcc 100644 --- a/eos/effects/setbonusserpentis.py +++ b/eos/effects/setbonusserpentis.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "velocityBonus", implant.getModifiedItemAttr("implantSetSerpentis")) diff --git a/eos/effects/setbonussisters.py b/eos/effects/setbonussisters.py index cf67fda48..a43478d52 100644 --- a/eos/effects/setbonussisters.py +++ b/eos/effects/setbonussisters.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "scanStrengthBonus", implant.getModifiedItemAttr("implantSetSisters")) diff --git a/eos/effects/setbonussyndicate.py b/eos/effects/setbonussyndicate.py index 594c4e90a..61fc59591 100644 --- a/eos/effects/setbonussyndicate.py +++ b/eos/effects/setbonussyndicate.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "boosterAttributeModifier", implant.getModifiedItemAttr("implantSetSyndicate")) diff --git a/eos/effects/setbonusthukker.py b/eos/effects/setbonusthukker.py index 073b46c96..8594ddcfe 100644 --- a/eos/effects/setbonusthukker.py +++ b/eos/effects/setbonusthukker.py @@ -5,5 +5,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - fit.implants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", + fit.appliedImplants.filteredItemMultiply(lambda mod: mod.item.group.name == "Cyberimplant", "agilityBonus", implant.getModifiedItemAttr("implantSetThukker")) From 65483309ab6b14fe0a09c3023fcf4530c8f60280 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 22 Mar 2016 23:17:05 -0400 Subject: [PATCH 24/30] Clear character implants during recalculation --- eos/saveddata/character.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index df06aee4c..902e17da8 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -19,6 +19,7 @@ from sqlalchemy.orm import validates, reconstructor +from itertools import chain from eos.effectHandlerHelpers import HandledItem, HandledImplantBoosterList import eos.db @@ -200,8 +201,13 @@ class Character(object): skill.calculateModifiedAttributes(fit, runTime) def clear(self): - for skill in self.skills: - skill.clear() + c = chain( + self.skills, + self.implants + ) + for stuff in c: + if stuff is not None and stuff != self: + stuff.clear() def __deepcopy__(self, memo): copy = Character("%s copy" % self.name, initSkills=False) From ca91f6bd921dfb8ed57a07d0de45ab65658d306d Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 22 Mar 2016 23:21:39 -0400 Subject: [PATCH 25/30] Remove debugging print --- eos/effects/setbonussansha.py | 1 - 1 file changed, 1 deletion(-) diff --git a/eos/effects/setbonussansha.py b/eos/effects/setbonussansha.py index 6aad859b9..df7ae7be3 100644 --- a/eos/effects/setbonussansha.py +++ b/eos/effects/setbonussansha.py @@ -6,6 +6,5 @@ runTime = "early" type = "passive" def handler(fit, implant, context): - print "applying set bonus to implants on ", fit, fit.appliedImplants, " from: ", implant, implant.item.name fit.appliedImplants.filteredItemMultiply(lambda target: target.item.requiresSkill("Cybernetics"), "armorHpBonus", implant.getModifiedItemAttr("implantSetSansha") or 1) From c3dcdb06869d14a8f06fab7a4b3a5e88d8be44aa Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 27 Mar 2016 21:48:19 -0400 Subject: [PATCH 26/30] Entity = characters, damage profiles... anything with the generic drop down list of entities along with new/rename/etc buttons. This commit starts refactoring how we handle this, hopefully simplifying the process to a series of dialogs rather than trying to manage multiple control in the window. --- gui/builtinViews/entityEditor.py | 151 ++++++++++++++++++ gui/patternEditor.py | 252 +++++++++---------------------- service/damagePattern.py | 5 +- 3 files changed, 225 insertions(+), 183 deletions(-) create mode 100644 gui/builtinViews/entityEditor.py diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py new file mode 100644 index 000000000..7eb439fb1 --- /dev/null +++ b/gui/builtinViews/entityEditor.py @@ -0,0 +1,151 @@ +import wx +from gui.bitmapLoader import BitmapLoader +import service + +class BaseValidator(wx.PyValidator): + def __init__(self): + wx.PyValidator.__init__(self) + + def Validate(self, win): + raise NotImplementedError() + + def TransferToWindow(self): + return True + + def TransferFromWindow(self): + return True + +class TextEntryValidatedDialog(wx.TextEntryDialog): + def __init__(self, parent, validator=None, *args, **kargs): + wx.TextEntryDialog.__init__(self, parent, *args, **kargs) + self.txtctrl = self.FindWindowById(3000) + if validator: + self.txtctrl.SetValidator(validator()) + +class EntityEditor (wx.Panel): + """ + Entity Editor is a panel that takes some sort of list as a source and populates a drop down with options to add/ + rename/clone/delete an entity. Comes with dialogs that take user input. Classes that derive this class must override + functions that get the list from the source, what to do when user does an action, and how to validate the input. + """ + + def __init__(self, parent, entityName): + wx.Panel.__init__(self, parent, id=wx.ID_ANY, style=wx.TAB_TRAVERSAL) + self.entityName = entityName + self.validator = None + self.navSizer = wx.BoxSizer(wx.HORIZONTAL) + + self.choices = self.getEntitiesFromContext() + self.choices.sort(key=lambda p: p.name) + self.entityChoices = wx.Choice(self, choices=map(lambda p: p.name, self.choices)) + self.navSizer.Add(self.entityChoices, 1, wx.ALL, 5) + + buttons = (("new", wx.ART_NEW, self.OnNew), + ("rename", BitmapLoader.getBitmap("rename", "gui"), self.OnRename), + ("copy", wx.ART_COPY, self.OnCopy), + ("delete", wx.ART_DELETE, self.OnDelete)) + + size = None + for name, art, func in buttons: + bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art + btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) + if size is None: + size = btn.GetSize() + + btn.SetMinSize(size) + btn.SetMaxSize(size) + + btn.SetToolTipString("{} {}".format(name.capitalize(), self.entityName)) + btn.Bind(wx.EVT_BUTTON, func) + setattr(self, "btn%s" % name.capitalize(), btn) + self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2) + + self.entityChoices.SetSelection(0) + self.SetSizer(self.navSizer) + self.Layout() + + def SetEditorValidator(self, validator=None): + """ Sets validator class (not an instance of the class) """ + self.validator = validator + + def getEntitiesFromContext(self): + """ Gets list of entities from current context """ + raise NotImplementedError() + + def DoNew(self, name): + """Override method to do new entity logic. Must return the new entity""" + raise NotImplementedError() + + def DoCopy(self, name): + """Override method to copy entity. Must return the copy""" + raise NotImplementedError() + + def DoRename(self, name): + """Override method to rename an entity""" + raise NotImplementedError() + + def DoDelete(self, name): + """Override method to delete entity""" + raise NotImplementedError() + + def OnNew(self, event): + dlg = TextEntryValidatedDialog(self, self.validator, + "Enter a name for your new {}:".format(self.entityName), + "New {}".format(self.entityName)) + + if dlg.ShowModal() == wx.ID_OK: + new = self.DoNew(dlg.GetValue().strip()) + self.refreshEntityList(new) + wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED)) + + def OnCopy(self, event): + dlg = TextEntryValidatedDialog(self, self.validator, + "Enter a name for your {} copy:".format(self.entityName), + "Copy {}".format(self.entityName)) + active = self.getActiveEntity() + dlg.SetValue("{} Copy".format(active.name)) + dlg.txtctrl.SetInsertionPointEnd() + + if dlg.ShowModal() == wx.ID_OK: + copy = self.DoCopy(active, dlg.GetValue().strip()) + self.refreshEntityList(copy) + wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED)) + + def OnRename(self, event): + dlg = TextEntryValidatedDialog(self, self.validator, + "Enter a new name for your {}:".format(self.entityName), + "Rename {}".format(self.entityName)) + active = self.getActiveEntity() + dlg.SetValue(active.name) + dlg.txtctrl.SetInsertionPointEnd() + + if dlg.ShowModal() == wx.ID_OK: + self.DoRename(active, dlg.GetValue().strip()) + self.refreshEntityList(active) + wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED)) + + def OnDelete(self, event): + dlg = wx.MessageDialog(self, + "Do you really want to delete the {} {}?".format(self.getActiveEntity().name, self.entityName), + "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + + if dlg.ShowModal() == wx.ID_YES: + self.DoDelete(self.getActiveEntity()) + self.refreshEntityList() + wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED)) + + def refreshEntityList(self, selected=None): + self.choices = self.getEntitiesFromContext() + self.entityChoices.Clear() + self.entityChoices.AppendItems(map(lambda p: p.name, self.choices)) + if selected: + idx = self.choices.index(selected) + self.entityChoices.SetSelection(idx) + else: + self.entityChoices.SetSelection(0) + + def getActiveEntity(self): + if len(self.choices) == 0: + return None + + return self.choices[self.entityChoices.GetSelection()] \ No newline at end of file diff --git a/gui/patternEditor.py b/gui/patternEditor.py index 40b8e45aa..88f24c17c 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -23,11 +23,65 @@ import service from wx.lib.intctrl import IntCtrl from gui.utils.clipboard import toClipboard, fromClipboard from service.damagePattern import ImportError - +from gui.builtinViews.entityEditor import EntityEditor, BaseValidator ########################################################################### ## Class DmgPatternEditorDlg ########################################################################### +class DmgPatternTextValidor(BaseValidator): + def __init__(self): + BaseValidator.__init__(self) + + def Clone(self): + return DmgPatternTextValidor() + + def Validate(self, win): + profileEditor = win.Parent + textCtrl = self.GetWindow() + text = textCtrl.GetValue().strip() + + try: + if len(text) == 0: + raise ValueError("You must supply a name for your Damage Profile!") + elif text in [x.name for x in profileEditor.choices]: + raise ValueError("Damage Profile name already in use, please choose another.") + + return True + except ValueError, e: + wx.MessageBox(u"{}".format(e), "Error") + textCtrl.SetFocus() + return False + + +class DmgPatternEntityEditor(EntityEditor): + def __init__(self, parent): + EntityEditor.__init__(self, parent, "Damage Profile") + self.SetEditorValidator(DmgPatternTextValidor) + + def getEntitiesFromContext(self): + """ Gets list of entities from current context """ + sDP = service.DamagePattern.getInstance() + choices = sorted(sDP.getDamagePatternList(), key=lambda p: p.name) + return [c for c in choices if c.name != "Selected Ammo"] + + def DoNew(self, name): + sDP = service.DamagePattern.getInstance() + return sDP.newPattern(name) + + def DoRename(self, entity, name): + sDP = service.DamagePattern.getInstance() + sDP.renamePattern(entity, name) + + def DoCopy(self, entity, name): + sDP = service.DamagePattern.getInstance() + copy = sDP.copyPattern(entity) + sDP.renamePattern(copy, name) + return copy + + def DoDelete(self, entity): + sDP = service.DamagePattern.getInstance() + sDP.deletePattern(entity) + class DmgPatternEditorDlg(wx.Dialog): DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") @@ -39,52 +93,12 @@ class DmgPatternEditorDlg(wx.Dialog): mainSizer = wx.BoxSizer(wx.VERTICAL) - self.headerSizer = headerSizer = wx.BoxSizer(wx.HORIZONTAL) - sDP = service.DamagePattern.getInstance() self.choices = sDP.getDamagePatternList() - # Remove "Selected Ammo" Damage Pattern - for dp in self.choices: - if dp.name == "Selected Ammo": - self.choices.remove(dp) - # Sort the remaining list and continue on - self.choices.sort(key=lambda p: p.name) - self.ccDmgPattern = wx.Choice(self, choices=map(lambda p: p.name, self.choices)) - self.ccDmgPattern.Bind(wx.EVT_CHOICE, self.patternChanged) - self.ccDmgPattern.SetSelection(0) - self.namePicker = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER) - self.namePicker.Bind(wx.EVT_TEXT_ENTER, self.processRename) - self.namePicker.Hide() - - size = None - headerSizer.Add(self.ccDmgPattern, 1, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT|wx.LEFT, 3) - buttons = (("new", wx.ART_NEW), - ("rename", BitmapLoader.getBitmap("rename", "gui")), - ("copy", wx.ART_COPY), - ("delete", wx.ART_DELETE)) - for name, art in buttons: - bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art - btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - if size is None: - size = btn.GetSize() - - btn.SetMinSize(size) - btn.SetMaxSize(size) - - btn.Layout() - setattr(self, name, btn) - btn.Enable(True) - btn.SetToolTipString("%s pattern" % name.capitalize()) - headerSizer.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL) - - self.btnSave = wx.Button(self, wx.ID_SAVE) - self.btnSave.Hide() - self.btnSave.Bind(wx.EVT_BUTTON, self.processRename) - self.headerSizer.Add(self.btnSave, 0, wx.ALIGN_CENTER) - - mainSizer.Add(headerSizer, 0, wx.EXPAND | wx.ALL, 2) + self.entityEditor = DmgPatternEntityEditor(self) + mainSizer.Add(self.entityEditor, 0, wx.ALL | wx.EXPAND, 2) self.sl = wx.StaticLine(self) mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) @@ -108,7 +122,7 @@ class DmgPatternEditorDlg(wx.Dialog): bmp = wx.StaticBitmap(self, wx.ID_ANY, BitmapLoader.getBitmap("%s_big"%type, "gui")) if i%2: style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT - border = 10 + border = 20 else: style = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT border = 5 @@ -155,6 +169,7 @@ class DmgPatternEditorDlg(wx.Dialog): importExport = (("Import", wx.ART_FILE_OPEN, "from"), ("Export", wx.ART_FILE_SAVE_AS, "to")) + for name, art, direction in importExport: bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) @@ -170,14 +185,9 @@ class DmgPatternEditorDlg(wx.Dialog): self.Layout() bsize = self.GetBestSize() - self.SetSize((-1,bsize.height)) + self.SetSize((-1, bsize.height)) - self.new.Bind(wx.EVT_BUTTON, self.newPattern) - self.rename.Bind(wx.EVT_BUTTON, self.renamePattern) - self.copy.Bind(wx.EVT_BUTTON, self.copyPattern) - self.delete.Bind(wx.EVT_BUTTON, self.deletePattern) - self.Import.Bind(wx.EVT_BUTTON, self.importPatterns) - self.Export.Bind(wx.EVT_BUTTON, self.exportPatterns) + self.Bind(wx.EVT_CHOICE, self.patternChanged) self.patternChanged() @@ -188,7 +198,7 @@ class DmgPatternEditorDlg(wx.Dialog): if self.block: return - p = self.getActivePattern() + p = self.entityEditor.getActiveEntity() total = sum(map(lambda attr: getattr(self, "%sEdit"%attr).GetValue(), self.DAMAGE_TYPES)) for type in self.DAMAGE_TYPES: editObj = getattr(self, "%sEdit"%type) @@ -207,24 +217,18 @@ class DmgPatternEditorDlg(wx.Dialog): for type in self.DAMAGE_TYPES: editObj = getattr(self, "%sEdit"%type) editObj.Enable(False) - self.rename.Enable(False) - self.delete.Enable(False) + self.entityEditor.btnRename.Enable(False) + self.entityEditor.btnDelete.Enable(False) def unrestrict(self): for type in self.DAMAGE_TYPES: editObj = getattr(self, "%sEdit"%type) editObj.Enable() - self.rename.Enable() - self.delete.Enable() - - def getActivePattern(self): - if len(self.choices) == 0: - return None - - return self.choices[self.ccDmgPattern.GetSelection()] + self.entityEditor.btnRename.Enable() + self.entityEditor.btnDelete.Enable() def patternChanged(self, event=None): - p = self.getActivePattern() + p = self.entityEditor.getActiveEntity() if p is None: return @@ -244,126 +248,9 @@ class DmgPatternEditorDlg(wx.Dialog): self.block = False self.ValuesUpdated() - def newPattern(self, event): - self.restrict() - - self.block = True - # reset values - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - editObj.SetValue(0) - - self.block = False - - self.btnSave.SetLabel("Create") - self.Refresh() - self.renamePattern() - - def renamePattern(self, event=None): - if event is not None: - self.btnSave.SetLabel("Rename") - - self.ccDmgPattern.Hide() - self.namePicker.Show() - self.headerSizer.Replace(self.ccDmgPattern, self.namePicker) - self.namePicker.SetFocus() - - if event is not None: # Rename mode - self.btnSave.SetLabel("Rename") - self.namePicker.SetValue(self.getActivePattern().name) - else: # Create mode - self.namePicker.SetValue("") - - for btn in (self.new, self.rename, self.delete, self.copy): - btn.Hide() - - self.btnSave.Show() - self.headerSizer.Layout() - if event is not None: - event.Skip() - - def processRename(self, event): - newName = self.namePicker.GetLineText(0) - self.stNotice.SetLabel("") - - if newName == "": - self.stNotice.SetLabel("Invalid name.") - return - - sDP = service.DamagePattern.getInstance() - if self.btnSave.Label == "Create": - p = sDP.newPattern() - else: - # we are renaming, so get the current selection - p = self.getActivePattern() - - for pattern in self.choices: - if pattern.name == newName and p != pattern: - self.stNotice.SetLabel("Name already used, please choose another") - return - - sDP.renamePattern(p, newName) - - self.updateChoices(newName) - self.headerSizer.Replace(self.namePicker, self.ccDmgPattern) - self.ccDmgPattern.Show() - self.namePicker.Hide() - self.btnSave.Hide() - for btn in (self.new, self.rename, self.delete, self.copy): - btn.Show() - - sel = self.ccDmgPattern.GetSelection() - self.ccDmgPattern.Delete(sel) - self.ccDmgPattern.Insert(newName, sel) - self.ccDmgPattern.SetSelection(sel) - self.ValuesUpdated() - self.unrestrict() - - def copyPattern(self,event): - sDP = service.DamagePattern.getInstance() - p = sDP.copyPattern(self.getActivePattern()) - self.choices.append(p) - id = self.ccDmgPattern.Append(p.name) - self.ccDmgPattern.SetSelection(id) - self.btnSave.SetLabel("Copy") - self.renamePattern() - self.patternChanged() - - def deletePattern(self,event): - sDP = service.DamagePattern.getInstance() - sel = self.ccDmgPattern.GetSelection() - sDP.deletePattern(self.getActivePattern()) - self.ccDmgPattern.Delete(sel) - self.ccDmgPattern.SetSelection(max(0, sel - 1)) - del self.choices[sel] - self.patternChanged() - - def __del__( self ): + def __del__(self): pass - def updateChoices(self, select=None): - "Gathers list of patterns and updates choice selections" - sDP = service.DamagePattern.getInstance() - self.choices = sDP.getDamagePatternList() - - for dp in self.choices: - if dp.name == "Selected Ammo": # don't include this special butterfly - self.choices.remove(dp) - - # Sort the remaining list and continue on - self.choices.sort(key=lambda p: p.name) - self.ccDmgPattern.Clear() - - for i, choice in enumerate(map(lambda p: p.name, self.choices)): - self.ccDmgPattern.Append(choice) - - if select is not None and choice == select: - self.ccDmgPattern.SetSelection(i) - - if select is None: - self.ccDmgPattern.SetSelection(0) - self.patternChanged() - def importPatterns(self, event): text = fromClipboard() if text: @@ -384,3 +271,6 @@ class DmgPatternEditorDlg(wx.Dialog): sDP = service.DamagePattern.getInstance() toClipboard( sDP.exportPatterns() ) self.stNotice.SetLabel("Patterns exported to clipboard") + + def contextChanged(self, event): + print "lol" \ No newline at end of file diff --git a/service/damagePattern.py b/service/damagePattern.py index 374940e0c..b67b10956 100644 --- a/service/damagePattern.py +++ b/service/damagePattern.py @@ -46,9 +46,10 @@ class DamagePattern(): def getDamagePattern(self, name): return eos.db.getDamagePattern(name) - def newPattern(self): + def newPattern(self, name): p = eos.types.DamagePattern(0, 0, 0, 0) - p.name = "" + p.name = name + eos.db.save(p) return p def renamePattern(self, p, newName): From 3315d27d45404915b6996a8c8d5d34a3bcffe981 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Sun, 27 Mar 2016 22:12:59 -0400 Subject: [PATCH 27/30] Applied entity editor to resist editor. Fixed issue with checking for same name in validator. Center dialogs on parents to make it look nicer --- gui/builtinViews/entityEditor.py | 4 + gui/patternEditor.py | 8 +- gui/resistsEditor.py | 272 +++++++------------------------ service/targetResists.py | 5 +- 4 files changed, 71 insertions(+), 218 deletions(-) diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index 7eb439fb1..e7eb11771 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -92,6 +92,7 @@ class EntityEditor (wx.Panel): dlg = TextEntryValidatedDialog(self, self.validator, "Enter a name for your new {}:".format(self.entityName), "New {}".format(self.entityName)) + dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: new = self.DoNew(dlg.GetValue().strip()) @@ -105,6 +106,7 @@ class EntityEditor (wx.Panel): active = self.getActiveEntity() dlg.SetValue("{} Copy".format(active.name)) dlg.txtctrl.SetInsertionPointEnd() + dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: copy = self.DoCopy(active, dlg.GetValue().strip()) @@ -118,6 +120,7 @@ class EntityEditor (wx.Panel): active = self.getActiveEntity() dlg.SetValue(active.name) dlg.txtctrl.SetInsertionPointEnd() + dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: self.DoRename(active, dlg.GetValue().strip()) @@ -128,6 +131,7 @@ class EntityEditor (wx.Panel): dlg = wx.MessageDialog(self, "Do you really want to delete the {} {}?".format(self.getActiveEntity().name, self.entityName), "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_YES: self.DoDelete(self.getActiveEntity()) diff --git a/gui/patternEditor.py b/gui/patternEditor.py index 88f24c17c..92454d750 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -43,7 +43,7 @@ class DmgPatternTextValidor(BaseValidator): try: if len(text) == 0: raise ValueError("You must supply a name for your Damage Profile!") - elif text in [x.name for x in profileEditor.choices]: + elif text in [x.name for x in profileEditor.entityEditor.choices]: raise ValueError("Damage Profile name already in use, please choose another.") return True @@ -59,7 +59,6 @@ class DmgPatternEntityEditor(EntityEditor): self.SetEditorValidator(DmgPatternTextValidor) def getEntitiesFromContext(self): - """ Gets list of entities from current context """ sDP = service.DamagePattern.getInstance() choices = sorted(sDP.getDamagePatternList(), key=lambda p: p.name) return [c for c in choices if c.name != "Selected Ammo"] @@ -93,10 +92,6 @@ class DmgPatternEditorDlg(wx.Dialog): mainSizer = wx.BoxSizer(wx.VERTICAL) - sDP = service.DamagePattern.getInstance() - - self.choices = sDP.getDamagePatternList() - self.entityEditor = DmgPatternEntityEditor(self) mainSizer.Add(self.entityEditor, 0, wx.ALL | wx.EXPAND, 2) @@ -186,6 +181,7 @@ class DmgPatternEditorDlg(wx.Dialog): self.Layout() bsize = self.GetBestSize() self.SetSize((-1, bsize.height)) + self.CenterOnParent() self.Bind(wx.EVT_CHOICE, self.patternChanged) diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index 3d8ac6ef2..57b438dc5 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -22,6 +22,61 @@ from gui.bitmapLoader import BitmapLoader import service from gui.utils.clipboard import toClipboard, fromClipboard from service.targetResists import ImportError +from gui.builtinViews.entityEditor import EntityEditor, BaseValidator + + +class TargetResistsTextValidor(BaseValidator): + def __init__(self): + BaseValidator.__init__(self) + + def Clone(self): + return TargetResistsTextValidor() + + def Validate(self, win): + profileEditor = win.Parent + textCtrl = self.GetWindow() + text = textCtrl.GetValue().strip() + + try: + if len(text) == 0: + raise ValueError("You must supply a name for your Target Resist Profile!") + elif text in [x.name for x in profileEditor.entityEditor.choices]: + raise ValueError("Target Resist Profile name already in use, please choose another.") + + return True + except ValueError, e: + wx.MessageBox(u"{}".format(e), "Error") + textCtrl.SetFocus() + return False + + +class TargetResistsEntityEditor(EntityEditor): + def __init__(self, parent): + EntityEditor.__init__(self, parent, "Target Resist Profile") + self.SetEditorValidator(TargetResistsTextValidor) + + def getEntitiesFromContext(self): + sTR = service.TargetResists.getInstance() + choices = sorted(sTR.getTargetResistsList(), key=lambda p: p.name) + return choices + + def DoNew(self, name): + sTR = service.TargetResists.getInstance() + return sTR.newPattern(name) + + def DoRename(self, entity, name): + sTR = service.TargetResists.getInstance() + sTR.renamePattern(entity, name) + + def DoCopy(self, entity, name): + sTR = service.TargetResists.getInstance() + copy = sTR.copyPattern(entity) + sTR.renamePattern(copy, name) + return copy + + def DoDelete(self, entity): + sTR = service.TargetResists.getInstance() + sTR.deletePattern(entity) class ResistsEditorDlg(wx.Dialog): @@ -35,51 +90,8 @@ class ResistsEditorDlg(wx.Dialog): mainSizer = wx.BoxSizer(wx.VERTICAL) - self.headerSizer = headerSizer = wx.BoxSizer(wx.HORIZONTAL) - - sTR = service.TargetResists.getInstance() - - self.choices = sTR.getTargetResistsList() - - # Sort the remaining list and continue on - self.choices.sort(key=lambda p: p.name) - self.ccResists = wx.Choice(self, choices=map(lambda p: p.name, self.choices)) - self.ccResists.Bind(wx.EVT_CHOICE, self.patternChanged) - self.ccResists.SetSelection(0) - - self.namePicker = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER) - self.namePicker.Bind(wx.EVT_TEXT_ENTER, self.processRename) - self.namePicker.Hide() - - size = None - headerSizer.Add(self.ccResists, 1, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 3) - - buttons = (("new", wx.ART_NEW), - ("rename", BitmapLoader.getBitmap("rename", "gui")), - ("copy", wx.ART_COPY), - ("delete", wx.ART_DELETE)) - for name, art in buttons: - bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art - btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - if size is None: - size = btn.GetSize() - - btn.SetMinSize(size) - btn.SetMaxSize(size) - - btn.Layout() - setattr(self, name, btn) - btn.Enable(True) - btn.SetToolTipString("%s resist profile" % name.capitalize()) - headerSizer.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL) - - - self.btnSave = wx.Button(self, wx.ID_SAVE) - self.btnSave.Hide() - self.btnSave.Bind(wx.EVT_BUTTON, self.processRename) - headerSizer.Add(self.btnSave, 0, wx.ALIGN_CENTER) - - mainSizer.Add(headerSizer, 0, wx.EXPAND | wx.ALL, 2) + self.entityEditor = TargetResistsEntityEditor(self) + mainSizer.Add(self.entityEditor, 0, wx.ALL | wx.EXPAND, 2) self.sl = wx.StaticLine(self) mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) @@ -159,14 +171,10 @@ class ResistsEditorDlg(wx.Dialog): self.Layout() bsize = self.GetBestSize() - self.SetSize((-1,bsize.height)) + self.SetSize((-1, bsize.height)) + self.CenterOnParent() - self.new.Bind(wx.EVT_BUTTON, self.newPattern) - self.rename.Bind(wx.EVT_BUTTON, self.renamePattern) - self.copy.Bind(wx.EVT_BUTTON, self.copyPattern) - self.delete.Bind(wx.EVT_BUTTON, self.deletePattern) - self.Import.Bind(wx.EVT_BUTTON, self.importPatterns) - self.Export.Bind(wx.EVT_BUTTON, self.exportPatterns) + self.Bind(wx.EVT_CHOICE, self.patternChanged) self.patternChanged() @@ -184,7 +192,7 @@ class ResistsEditorDlg(wx.Dialog): return try: - p = self.getActivePattern() + p = self.entityEditor.getActiveEntity() for type in self.DAMAGE_TYPES: editObj = getattr(self, "%sEdit"%type) @@ -220,33 +228,10 @@ class ResistsEditorDlg(wx.Dialog): finally: # Refresh for color changes to take effect immediately self.Refresh() - def restrict(self): - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - editObj.Enable(False) - self.rename.Enable(False) - self.delete.Enable(False) - - def unrestrict(self): - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - editObj.Enable() - self.rename.Enable() - self.delete.Enable() - - def getActivePattern(self): - if len(self.choices) == 0: - return None - - return self.choices[self.ccResists.GetSelection()] - def patternChanged(self, event=None): "Event fired when user selects pattern. Can also be called from script" - p = self.getActivePattern() + p = self.entityEditor.getActiveEntity() if p is None: - # This happens when there are no patterns in the DB. As such, force - # user to create one first or exit dlg. - self.newPattern(None) return self.block = True @@ -259,142 +244,9 @@ class ResistsEditorDlg(wx.Dialog): self.block = False self.ValuesUpdated() - def newPattern(self, event): - ''' - Simply does new-pattern specifics: replaces label on button, restricts, - and resets values to default. Hands off to the rename function for - further handling. - ''' - self.btnSave.SetLabel("Create") - self.restrict() - # reset values - for type in self.DAMAGE_TYPES: - editObj = getattr(self, "%sEdit"%type) - editObj.ChangeValue("0.0") - editObj.SetForegroundColour(self.colorReset) - - self.Refresh() - self.renamePattern() - - def renamePattern(self, event=None): - "Changes layout to facilitate naming a pattern" - - self.showInput(True) - - if event is not None: # Rename mode - self.btnSave.SetLabel("Rename") - self.namePicker.SetValue(self.getActivePattern().name) - else: # Create mode - self.namePicker.SetValue("") - - if event is not None: - event.Skip() - - def processRename(self, event): - ''' - Processes rename event (which can be new or old patterns). If new - pattern, creates it; if old, selects it. if checks are valid, rename - saves pattern to DB. - - Also resets to default layout and unrestricts. - ''' - newName = self.namePicker.GetLineText(0) - self.stNotice.SetLabel("") - - if newName == "": - self.stNotice.SetLabel("Invalid name") - return - - sTR = service.TargetResists.getInstance() - if self.btnSave.Label == "Create": - p = sTR.newPattern() - else: - # we are renaming, so get the current selection - p = self.getActivePattern() - - # test for patterns of the same name - for pattern in self.choices: - if pattern.name == newName and p != pattern: - self.stNotice.SetLabel("Name already used, please choose another") - return - - # rename regardless of new or rename - sTR.renamePattern(p, newName) - - self.updateChoices(newName) - self.showInput(False) - sel = self.ccResists.GetSelection() - self.ValuesUpdated() - self.unrestrict() - - def copyPattern(self,event): - sTR = service.TargetResists.getInstance() - p = sTR.copyPattern(self.getActivePattern()) - self.choices.append(p) - id = self.ccResists.Append(p.name) - self.ccResists.SetSelection(id) - self.btnSave.SetLabel("Copy") - self.renamePattern() - self.patternChanged() - - def deletePattern(self,event): - sTR = service.TargetResists.getInstance() - sel = self.ccResists.GetSelection() - sTR.deletePattern(self.getActivePattern()) - self.ccResists.Delete(sel) - self.ccResists.SetSelection(max(0, sel - 1)) - del self.choices[sel] - self.patternChanged() - - def showInput(self, bool): - if bool and not self.namePicker.IsShown(): - self.ccResists.Hide() - self.namePicker.Show() - self.headerSizer.Replace(self.ccResists, self.namePicker) - self.namePicker.SetFocus() - for btn in (self.new, self.rename, self.delete, self.copy): - btn.Hide() - self.btnSave.Show() - self.restrict() - self.headerSizer.Layout() - elif not bool and self.namePicker.IsShown(): - self.headerSizer.Replace(self.namePicker, self.ccResists) - self.ccResists.Show() - self.namePicker.Hide() - self.btnSave.Hide() - for btn in (self.new, self.rename, self.delete, self.copy): - btn.Show() - self.unrestrict() - self.headerSizer.Layout() - - def __del__( self ): pass - def updateChoices(self, select=None): - "Gathers list of patterns and updates choice selections" - sTR = service.TargetResists.getInstance() - self.choices = sTR.getTargetResistsList() - - if len(self.choices) == 0: - #self.newPattern(None) - return - - # Sort the remaining list and continue on - self.choices.sort(key=lambda p: p.name) - self.ccResists.Clear() - - for i, choice in enumerate(map(lambda p: p.name, self.choices)): - self.ccResists.Append(choice) - - if select is not None and choice == select: - self.ccResists.SetSelection(i) - - if select is None: - self.ccResists.SetSelection(0) - - self.patternChanged() - def importPatterns(self, event): "Event fired when import from clipboard button is clicked" diff --git a/service/targetResists.py b/service/targetResists.py index a8f61b9f9..2640551f4 100644 --- a/service/targetResists.py +++ b/service/targetResists.py @@ -39,9 +39,10 @@ class TargetResists(): def getTargetResists(self, name): return eos.db.getTargetResists(name) - def newPattern(self): + def newPattern(self, name): p = eos.types.TargetResists(0.0, 0.0, 0.0, 0.0) - p.name = "" + p.name = name + eos.db.save(p) return p def renamePattern(self, p, newName): From a3d9d5cca8e30d389be6498599e96fefffa5df83 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Mon, 28 Mar 2016 21:23:27 -0400 Subject: [PATCH 28/30] Migrate character editor to use new entity editor. --- eos/db/saveddata/character.py | 1 + eos/db/saveddata/queries.py | 2 +- eos/saveddata/character.py | 10 +- gui/builtinViews/entityEditor.py | 5 +- gui/builtinViews/implantEditor.py | 4 +- gui/characterEditor.py | 354 +++++++++++++----------------- gui/characterSelection.py | 6 +- gui/gangView.py | 5 +- service/character.py | 38 ++-- 9 files changed, 194 insertions(+), 231 deletions(-) diff --git a/eos/db/saveddata/character.py b/eos/db/saveddata/character.py index f3d4d1e13..e6be52535 100644 --- a/eos/db/saveddata/character.py +++ b/eos/db/saveddata/character.py @@ -37,6 +37,7 @@ characters_table = Table("characters", saveddata_meta, mapper(Character, characters_table, properties = { + "savedName": characters_table.c.name, "_Character__owner": relation( User, backref = "characters"), diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index de813ee1c..5a6dd1bfc 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -154,7 +154,7 @@ def getCharacter(lookfor, eager=None): elif isinstance(lookfor, basestring): eager = processEager(eager) with sd_lock: - character = saveddata_session.query(Character).options(*eager).filter(Character.name == lookfor).first() + character = saveddata_session.query(Character).options(*eager).filter(Character.savedName == lookfor).first() else: raise TypeError("Need integer or string as argument") return character diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 902e17da8..24fa08694 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -90,7 +90,7 @@ class Character(object): return all0 def __init__(self, name, defaultLevel=None, initSkills=True): - self.name = name + self.savedName = name self.__owner = None self.defaultLevel = defaultLevel self.__skills = [] @@ -129,6 +129,14 @@ class Character(object): def owner(self, owner): self.__owner = owner + @property + def name(self): + return self.savedName if not self.isDirty else "{} *".format(self.savedName) + + @name.setter + def name(self, name): + self.savedName = name + @property def skills(self): return self.__skills diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index e7eb11771..acba2b28b 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -152,4 +152,7 @@ class EntityEditor (wx.Panel): if len(self.choices) == 0: return None - return self.choices[self.entityChoices.GetSelection()] \ No newline at end of file + return self.choices[self.entityChoices.GetSelection()] + + def setActiveEntity(self, entity): + self.entityChoices.SetSelection(self.choices.index(entity)) \ No newline at end of file diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index f12b5fcc1..bcdb96c7b 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -110,7 +110,7 @@ class BaseImplantEditorView (wx.Panel): """ Adds implant to the current context""" raise NotImplementedError() - def removeImplantFromContext(self, pos): + def removeImplantFromContext(self, implant): """ Removes implant from the current context""" raise NotImplementedError() @@ -191,7 +191,7 @@ class BaseImplantEditorView (wx.Panel): def removeItem(self, event): pos = self.pluggedImplantsTree.GetFirstSelected() if pos != -1: - self.removeImplantFromContext(pos) + self.removeImplantFromContext(self.implants[pos]) self.update() class AvailableImplantsView(d.Display): diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 4be9fc5ee..72fc0a333 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -26,6 +26,73 @@ import service from gui.contextMenu import ContextMenu import gui.globalEvents as GE from gui.builtinViews.implantEditor import BaseImplantEditorView +from gui.builtinViews.entityEditor import EntityEditor, BaseValidator + + +class CharacterTextValidor(BaseValidator): + def __init__(self): + BaseValidator.__init__(self) + + def Clone(self): + return CharacterTextValidor() + + def Validate(self, win): + profileEditor = win.Parent + textCtrl = self.GetWindow() + text = textCtrl.GetValue().strip() + + try: + if len(text) == 0: + raise ValueError("You must supply a name for the Character!") + elif text in [x.name for x in profileEditor.entityEditor.choices]: + raise ValueError("Character name already in use, please choose another.") + + return True + except ValueError, e: + wx.MessageBox(u"{}".format(e), "Error") + textCtrl.SetFocus() + return False + + +class CharacterEntityEditor(EntityEditor): + def __init__(self, parent): + EntityEditor.__init__(self, parent, "Character") + self.SetEditorValidator(CharacterTextValidor) + + def getEntitiesFromContext(self): + sChar = service.Character.getInstance() + charList = sorted(sChar.getCharacterList(), key=lambda c: c.name) + + # Do some processing to ensure that we have All 0 and All 5 at the top + all5 = sChar.all5() + all0 = sChar.all0() + + charList.remove(all5) + charList.remove(all0) + + charList.insert(0, all5) + charList.insert(0, all0) + + return charList + + def DoNew(self, name): + sChar = service.Character.getInstance() + return sChar.new(name) + + def DoRename(self, entity, name): + sChar = service.Character.getInstance() + sChar.rename(entity, name) + + def DoCopy(self, entity, name): + sChar = service.Character.getInstance() + copy = sChar.copy(entity) + sChar.rename(copy, name) + return copy + + def DoDelete(self, entity): + sChar = service.Character.getInstance() + sChar.delete(entity) + class CharacterEditor(wx.Frame): def __init__(self, parent): @@ -37,57 +104,16 @@ class CharacterEditor(wx.Frame): self.mainFrame = parent #self.disableWin = wx.WindowDisabler(self) + sFit = service.Fit.getInstance() - self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) ) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) mainSizer = wx.BoxSizer(wx.VERTICAL) - self.navSizer = wx.BoxSizer(wx.HORIZONTAL) - sChar = service.Character.getInstance() - - self.btnSave = wx.Button(self, wx.ID_SAVE) - self.btnSave.Hide() - self.btnSave.Bind(wx.EVT_BUTTON, self.processRename) - - self.characterRename = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) - self.characterRename.Hide() - self.characterRename.Bind(wx.EVT_TEXT_ENTER, self.processRename) - - self.charChoice = wx.Choice(self, wx.ID_ANY, style=0) - self.navSizer.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 5) - - charList = sChar.getCharacterList() - - for id, name, active in charList: - i = self.charChoice.Append(name, id) - if active: - self.charChoice.SetSelection(i) - - self.navSizer.Add(self.btnSave, 0, wx.ALL , 5) - - - buttons = (("new", wx.ART_NEW), - ("rename", BitmapLoader.getBitmap("rename", "gui")), - ("copy", wx.ART_COPY), - ("delete", wx.ART_DELETE)) - - size = None - for name, art in buttons: - bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art - btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - if size is None: - size = btn.GetSize() - - btn.SetMinSize(size) - btn.SetMaxSize(size) - - btn.SetToolTipString("%s character" % name.capitalize()) - btn.Bind(wx.EVT_BUTTON, getattr(self, name)) - setattr(self, "btn%s" % name.capitalize(), btn) - self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2) - - - mainSizer.Add(self.navSizer, 0, wx.ALL | wx.EXPAND, 5) + self.entityEditor = CharacterEntityEditor(self) + mainSizer.Add(self.entityEditor, 0, wx.ALL | wx.EXPAND, 2) + # Default drop down to current fit's character + self.entityEditor.setActiveEntity(sFit.character) self.viewsNBContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0) @@ -114,7 +140,6 @@ class CharacterEditor(wx.Frame): bSizerButtons.AddStretchSpacer() bSizerButtons.Add(self.btnOK, 0, wx.ALL, 5) - self.btnSaveChar.Bind(wx.EVT_BUTTON, self.saveChar) self.btnSaveAs.Bind(wx.EVT_BUTTON, self.saveCharAs) self.btnRevert.Bind(wx.EVT_BUTTON, self.revertChar) @@ -129,16 +154,12 @@ class CharacterEditor(wx.Frame): self.Centre(wx.BOTH) - charID = self.getActiveCharacter() - if sChar.getCharName(charID) in ("All 0", "All 5"): - self.restrict() - - self.registerEvents() + self.Bind(wx.EVT_CLOSE, self.closeEvent) + self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList) + self.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged) def btnRestrict(self): - sChar = service.Character.getInstance() - charID = self.getActiveCharacter() - char = sChar.getCharacter(charID) + char = self.entityEditor.getActiveEntity() # enable/disable character saving stuff self.btnSaveChar.Enable(not char.ro and char.isDirty) @@ -146,46 +167,34 @@ class CharacterEditor(wx.Frame): self.btnRevert.Enable(char.isDirty) def refreshCharacterList(self, event=None): - sChar = service.Character.getInstance() - charList = sChar.getCharacterList() - active = self.getActiveCharacter() - self.charChoice.Clear() - - for id, name, _ in charList: - i = self.charChoice.Append(name, id) - if active == id: - self.charChoice.SetSelection(i) - + """This is only called when we save a modified character""" + active = self.entityEditor.getActiveEntity() + self.entityEditor.refreshEntityList(active) self.btnRestrict() + if event: + event.Skip() + def editingFinished(self, event): #del self.disableWin wx.PostEvent(self.mainFrame, GE.CharListUpdated()) self.Destroy() - def registerEvents(self): - self.Bind(wx.EVT_CLOSE, self.closeEvent) - self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList) - self.charChoice.Bind(wx.EVT_CHOICE, self.charChanged) - def saveChar(self, event): sChr = service.Character.getInstance() - charID = self.getActiveCharacter() - sChr.saveCharacter(charID) - self.sview.populateSkillTree() + char = self.entityEditor.getActiveEntity() + sChr.saveCharacter(char.ID) wx.PostEvent(self, GE.CharListUpdated()) def saveCharAs(self, event): - charID = self.getActiveCharacter() - dlg = SaveCharacterAs(self, charID) + char = self.entityEditor.getActiveEntity() + dlg = SaveCharacterAs(self, char.ID) dlg.ShowModal() - self.sview.populateSkillTree() def revertChar(self, event): sChr = service.Character.getInstance() - charID = self.getActiveCharacter() - sChr.revertCharacter(charID) - self.sview.populateSkillTree() + char = self.entityEditor.getActiveEntity() + sChr.revertCharacter(char.ID) wx.PostEvent(self, GE.CharListUpdated()) def closeEvent(self, event): @@ -194,109 +203,25 @@ class CharacterEditor(wx.Frame): self.Destroy() def restrict(self): - self.btnRename.Enable(False) - self.btnDelete.Enable(False) - self.iview.Enable(False) - self.aview.Enable(False) + self.entityEditor.btnRename.Enable(False) + self.entityEditor.btnDelete.Enable(False) def unrestrict(self): - self.btnRename.Enable(True) - self.btnDelete.Enable(True) - self.iview.Enable(True) - self.aview.Enable(True) + self.entityEditor.btnRename.Enable() + self.entityEditor.btnDelete.Enable() def charChanged(self, event): - self.sview.populateSkillTree() - sChar = service.Character.getInstance() - charID = self.getActiveCharacter() - if sChar.getCharName(charID) in ("All 0", "All 5"): + char = self.entityEditor.getActiveEntity() + if char.name in ("All 0", "All 5"): self.restrict() else: self.unrestrict() - wx.PostEvent(self, GE.CharChanged()) + self.btnRestrict() + if event is not None: event.Skip() - def getActiveCharacter(self): - selection = self.charChoice.GetCurrentSelection() - return self.charChoice.GetClientData(selection) if selection is not None else None - - def new(self, event): - sChar = service.Character.getInstance() - charID = sChar.new() - id = self.charChoice.Append(sChar.getCharName(charID), charID) - self.charChoice.SetSelection(id) - self.unrestrict() - self.btnSave.SetLabel("Create") - self.rename(None) - self.charChanged(None) - - def rename(self, event): - if event is not None: - self.btnSave.SetLabel("Rename") - self.charChoice.Hide() - self.characterRename.Show() - self.navSizer.Replace(self.charChoice, self.characterRename) - self.characterRename.SetFocus() - for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete): - btn.Hide() - - self.btnSave.Show() - self.navSizer.Layout() - - sChar = service.Character.getInstance() - currName = sChar.getCharName(self.getActiveCharacter()) - self.characterRename.SetValue(currName) - self.characterRename.SetSelection(0, len(currName)) - - def processRename(self, event): - sChar = service.Character.getInstance() - newName = self.characterRename.GetLineText(0) - - if newName == "All 0" or newName == "All 5": - newName = newName + " bases are belong to us" - - charID = self.getActiveCharacter() - sChar.rename(charID, newName) - - self.charChoice.Show() - self.characterRename.Hide() - self.navSizer.Replace(self.characterRename, self.charChoice) - for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete): - btn.Show() - - self.btnSave.Hide() - self.navSizer.Layout() - self.refreshCharacterList() - - def copy(self, event): - sChar = service.Character.getInstance() - charID = sChar.copy(self.getActiveCharacter()) - id = self.charChoice.Append(sChar.getCharName(charID), charID) - self.charChoice.SetSelection(id) - self.unrestrict() - self.btnSave.SetLabel("Copy") - self.rename(None) - wx.PostEvent(self, GE.CharChanged()) - - def delete(self, event): - dlg = wx.MessageDialog(self, - "Do you really want to delete this character?", - "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) - - if dlg.ShowModal() == wx.ID_YES: - sChar = service.Character.getInstance() - sChar.delete(self.getActiveCharacter()) - sel = self.charChoice.GetSelection() - self.charChoice.Delete(sel) - self.charChoice.SetSelection(sel - 1) - newSelection = self.getActiveCharacter() - if sChar.getCharName(newSelection) in ("All 0", "All 5"): - self.restrict() - - wx.PostEvent(self, GE.CharChanged()) - def Destroy(self): sFit = service.Fit.getInstance() fitID = self.mainFrame.getActiveFit() @@ -308,7 +233,8 @@ class CharacterEditor(wx.Frame): class SkillTreeView (wx.Panel): def __init__(self, parent): - wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) + wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) + self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.VERTICAL) @@ -335,6 +261,10 @@ class SkillTreeView (wx.Panel): tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) tree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.scheduleMenu) + # bind the Character selection event + self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.populateSkillTree) + self.charEditor.Bind(GE.CHAR_LIST_UPDATED, self.populateSkillTree) + srcContext = "skillItem" itemContext = "Skill" context = (srcContext, itemContext) @@ -365,11 +295,10 @@ class SkillTreeView (wx.Panel): self.Layout() - def populateSkillTree(self): + def populateSkillTree(self, event=None): sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() - dirtySkills = sChar.getDirtySkills(charID) - dirtyGroups = set([skill.item.group.ID for skill in dirtySkills]) + char = self.charEditor.entityEditor.getActiveEntity() + dirtyGroups = set([skill.item.group.ID for skill in char.dirtySkills]) groups = sChar.getSkillGroups() imageId = self.skillBookImageId @@ -386,6 +315,9 @@ class SkillTreeView (wx.Panel): tree.SortChildren(root) + if event: + event.Skip() + def expandLookup(self, event): root = event.Item tree = self.skillTreeListCtrl @@ -395,11 +327,11 @@ class SkillTreeView (wx.Panel): #Get the real intrestin' stuff sChar = service.Character.getInstance() - char = self.Parent.Parent.getActiveCharacter() + char = self.charEditor.entityEditor.getActiveEntity() for id, name in sChar.getSkills(tree.GetPyData(root)): iconId = self.skillBookImageId childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id)) - level, dirty = sChar.getSkillLevel(char, id) + level, dirty = sChar.getSkillLevel(char.ID, id) tree.SetItemText(childId, "Level %d" % level if isinstance(level, int) else level, 1) if dirty: tree.SetItemTextColour(childId, wx.BLUE) @@ -415,10 +347,9 @@ class SkillTreeView (wx.Panel): if self.skillTreeListCtrl.GetChildrenCount(item) > 0: return - sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() + char = self.charEditor.entityEditor.getActiveEntity() sMkt = service.Market.getInstance() - if sChar.getCharName(charID) not in ("All 0", "All 5"): + if char.name not in ("All 0", "All 5"): self.levelChangeMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item)) self.PopupMenu(self.levelChangeMenu) else: @@ -429,21 +360,21 @@ class SkillTreeView (wx.Panel): level = self.levelIds.get(event.Id) sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() + char = self.charEditor.entityEditor.getActiveEntity() selection = self.skillTreeListCtrl.GetSelection() skillID = self.skillTreeListCtrl.GetPyData(selection) if level is not None: self.skillTreeListCtrl.SetItemText(selection, "Level %d" % level if isinstance(level, int) else level, 1) - sChar.changeLevel(charID, skillID, level, persist=True) + sChar.changeLevel(char.ID, skillID, level, persist=True) elif event.Id == self.revertID: - sChar.revertLevel(charID, skillID) + sChar.revertLevel(char.ID, skillID) elif event.Id == self.saveID: - sChar.saveSkill(charID, skillID) + sChar.saveSkill(char.ID, skillID) self.skillTreeListCtrl.SetItemTextColour(selection, None) - dirtySkills = sChar.getDirtySkills(charID) + dirtySkills = sChar.getDirtySkills(char.ID) dirtyGroups = set([skill.item.group.ID for skill in dirtySkills]) parentID = self.skillTreeListCtrl.GetItemParent(selection) @@ -452,7 +383,6 @@ class SkillTreeView (wx.Panel): if groupID not in dirtyGroups: self.skillTreeListCtrl.SetItemTextColour(parentID, None) - wx.PostEvent(self.Parent.Parent, GE.CharListUpdated()) event.Skip() @@ -460,31 +390,37 @@ class ImplantEditorView(BaseImplantEditorView): def __init__(self, parent): BaseImplantEditorView.__init__ (self, parent) - if "__WXGTK__" in wx.PlatformInfo: + self.determineEnabled() + + if "__WXGTK__" in wx.PlatformInfo: self.pluggedImplantsTree.Bind(wx.EVT_RIGHT_UP, self.scheduleMenu) else: self.pluggedImplantsTree.Bind(wx.EVT_RIGHT_DOWN, self.scheduleMenu) def bindContext(self): - self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.contextChanged) + self.Parent.Parent.entityEditor.Bind(wx.EVT_CHOICE, self.contextChanged) + + def contextChanged(self, event): + BaseImplantEditorView.contextChanged(self, event) + self.determineEnabled() def getImplantsFromContext(self): sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() + char = self.Parent.Parent.entityEditor.getActiveEntity() - return sChar.getImplants(charID) + return sChar.getImplants(char.ID) def addImplantToContext(self, item): sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() + char = self.Parent.Parent.entityEditor.getActiveEntity() - sChar.addImplant(charID, item.ID) + sChar.addImplant(char.ID, item.ID) - def removeImplantFromContext(self, pos): + def removeImplantFromContext(self, implant): sChar = service.Character.getInstance() - charID = self.Parent.Parent.getActiveCharacter() + char = self.Parent.Parent.entityEditor.getActiveEntity() - sChar.removeImplant(charID, self.implants[pos]) + sChar.removeImplant(char.ID, implant) def scheduleMenu(self, event): event.Skip() @@ -497,11 +433,19 @@ class ImplantEditorView(BaseImplantEditorView): menu = ContextMenu.getMenu((self.Parent.Parent,), *context) self.PopupMenu(menu) + def determineEnabled(self): + char = self.Parent.Parent.entityEditor.getActiveEntity() + + if char.name in ("All 0", "All 5"): + self.Enable(False) + else: + self.Enable() + class APIView (wx.Panel): def __init__(self, parent): wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) - self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged) + self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) self.apiUrlCreatePredefined = u"https://community.eveonline.com/support/api-key/CreatePredefined?accessMask=8" @@ -585,13 +529,17 @@ class APIView (wx.Panel): self.hlEveAPI2 = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlKeyList, self.apiUrlKeyList, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE ) pmainSizer.Add( self.hlEveAPI2, 0, wx.ALL, 2 ) + self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged) + self.SetSizer(pmainSizer) self.Layout() self.charChanged(None) def charChanged(self, event): sChar = service.Character.getInstance() - ID, key, char, chars = sChar.getApiDetails(self.Parent.Parent.getActiveCharacter()) + activeChar = self.charEditor.entityEditor.getActiveEntity() + + ID, key, char, chars = sChar.getApiDetails(activeChar.ID) self.inputID.SetValue(str(ID)) self.inputKey.SetValue(key) @@ -609,6 +557,14 @@ class APIView (wx.Panel): self.charChoice.Enable(False) self.btnFetchSkills.Enable(False) + if activeChar.name in ("All 0", "All 5"): + self.Enable(False) + self.stDisabledTip.Show() + self.Layout() + else: + self.Enable() + self.stDisabledTip.Hide() + self.Layout() if event is not None: event.Skip() diff --git a/gui/characterSelection.py b/gui/characterSelection.py index 990dc8bf5..47df7e018 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -83,9 +83,9 @@ class CharacterSelection(wx.Panel): charList = sChar.getCharacterList() picked = False - for id, name, active in charList: - currId = choice.Append(name, id) - if id == activeChar: + for char in charList: + currId = choice.Append(char.name, char.ID) + if char.ID == activeChar: choice.SetSelection(currId) self.charChanged(None) picked = True diff --git a/gui/gangView.py b/gui/gangView.py index 2e6d7b1da..25a457301 100644 --- a/gui/gangView.py +++ b/gui/gangView.py @@ -346,9 +346,8 @@ class GangView ( ScrolledPanel ): choice.Clear() currSelFound = False for char in charList: - id,name,_ = char - choice.Append(name, id) - if chCurrData == id: + choice.Append(char.name, char.ID) + if chCurrData == char.ID: currSelFound = True if chCurrSelection == -1: diff --git a/service/character.py b/service/character.py index 6078ccd8a..a4d68c98a 100644 --- a/service/character.py +++ b/service/character.py @@ -50,9 +50,8 @@ class CharacterImportThread(threading.Thread): # we try to parse api XML data first with open(path, mode='r') as charFile: sheet = service.ParseXML(charFile) - charID = sCharacter.new() - sCharacter.rename(charID, sheet.name+" (imported)") - sCharacter.apiUpdateCharSheet(charID, sheet.skills) + char = sCharacter.new(sheet.name+" (imported)") + sCharacter.apiUpdateCharSheet(char.ID, sheet.skills) except: # if it's not api XML data, try this # this is a horrible logic flow, but whatever @@ -69,9 +68,8 @@ class CharacterImportThread(threading.Thread): "typeID": int(skill.getAttribute("typeID")), "level": int(skill.getAttribute("level")), }) - charID = sCharacter.new() - sCharacter.rename(charID, name+" (EVEMon)") - sCharacter.apiUpdateCharSheet(charID, skills) + char = sCharacter.new(name+" (EVEMon)") + sCharacter.apiUpdateCharSheet(char.ID, skills) except: continue @@ -116,6 +114,11 @@ class Character(object): return cls.instance + def __init__(self): + # Simply initializes default characters in case they aren't in the database yet + self.all0() + self.all5() + def exportText(self): data = "Pyfa exported plan for \""+self.skillReqsDict['charname']+"\"\n" data += "=" * 79 + "\n" @@ -185,10 +188,7 @@ class Character(object): return self.all5().ID def getCharacterList(self): - baseChars = [eos.types.Character.getAll0(), eos.types.Character.getAll5()] - sFit = service.Fit.getInstance() - - return map(lambda c: (c.ID, c.name if not c.isDirty else "{} *".format(c.name), c == sFit.character), eos.db.getCharacterList()) + return eos.db.getCharacterList() def getCharacter(self, charID): char = eos.db.getCharacter(charID) @@ -248,25 +248,21 @@ class Character(object): def getCharName(self, charID): return eos.db.getCharacter(charID).name - def new(self): - char = eos.types.Character("New Character") + def new(self, name="New Character"): + char = eos.types.Character(name) eos.db.save(char) - return char.ID + return char - def rename(self, charID, newName): - char = eos.db.getCharacter(charID) + def rename(self, char, newName): char.name = newName eos.db.commit() - def copy(self, charID): - char = eos.db.getCharacter(charID) + def copy(self, char): newChar = copy.deepcopy(char) eos.db.save(newChar) - return newChar.ID + return newChar - def delete(self, charID): - char = eos.db.getCharacter(charID) - eos.db.commit() + def delete(self, char): eos.db.remove(char) def getApiDetails(self, charID): From 11610370921daf08225e8487cc1fe9ae4b634c91 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Tue, 29 Mar 2016 21:51:39 -0400 Subject: [PATCH 29/30] Migrate implant set editor to new entity editor --- gui/builtinViews/entityEditor.py | 6 +- gui/setEditor.py | 277 +++++++------------------------ service/implantSet.py | 9 +- 3 files changed, 70 insertions(+), 222 deletions(-) diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index acba2b28b..faaf1e3bb 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -76,15 +76,15 @@ class EntityEditor (wx.Panel): """Override method to do new entity logic. Must return the new entity""" raise NotImplementedError() - def DoCopy(self, name): + def DoCopy(self, entity, name): """Override method to copy entity. Must return the copy""" raise NotImplementedError() - def DoRename(self, name): + def DoRename(self, entity, name): """Override method to rename an entity""" raise NotImplementedError() - def DoDelete(self, name): + def DoDelete(self, entity): """Override method to delete entity""" raise NotImplementedError() diff --git a/gui/setEditor.py b/gui/setEditor.py index 293ff5c76..3b283d601 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -24,9 +24,63 @@ import service from gui.utils.clipboard import toClipboard, fromClipboard from service.implantSet import ImportError import logging +from gui.builtinViews.entityEditor import EntityEditor, BaseValidator logger = logging.getLogger(__name__) +class ImplantTextValidor(BaseValidator): + def __init__(self): + BaseValidator.__init__(self) + + def Clone(self): + return ImplantTextValidor() + + def Validate(self, win): + profileEditor = win.Parent + textCtrl = self.GetWindow() + text = textCtrl.GetValue().strip() + + try: + if len(text) == 0: + raise ValueError("You must supply a name for the Implant Set!") + elif text in [x.name for x in profileEditor.entityEditor.choices]: + raise ValueError("Imlplant Set name already in use, please choose another.") + + return True + except ValueError, e: + wx.MessageBox(u"{}".format(e), "Error") + textCtrl.SetFocus() + return False + + +class ImplantSetEntityEditor(EntityEditor): + def __init__(self, parent): + EntityEditor.__init__(self, parent, "Implant Set") + self.SetEditorValidator(ImplantTextValidor) + + def getEntitiesFromContext(self): + sIS = service.ImplantSets.getInstance() + return sorted(sIS.getImplantSetList(), key=lambda c: c.name) + + def DoNew(self, name): + sIS = service.ImplantSets.getInstance() + return sIS.newSet(name) + + def DoRename(self, entity, name): + sIS = service.ImplantSets.getInstance() + sIS.renameSet(entity, name) + + def DoCopy(self, entity, name): + sIS = service.ImplantSets.getInstance() + copy = sIS.copySet(entity) + sIS.renameSet(copy, name) + return copy + + def DoDelete(self, entity): + sIS = service.ImplantSets.getInstance() + sIS.deleteSet(entity) + + class ImplantSetEditor(BaseImplantEditorView): def __init__(self, parent): BaseImplantEditorView.__init__(self, parent) @@ -34,27 +88,26 @@ class ImplantSetEditor(BaseImplantEditorView): self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) def bindContext(self): - self.Parent.ccSets.Bind(wx.EVT_CHOICE, self.contextChanged) + self.Parent.entityEditor.Bind(wx.EVT_CHOICE, self.contextChanged) def getImplantsFromContext(self): sIS = service.ImplantSets.getInstance() - set = self.Parent.getActiveSet() + set = self.Parent.entityEditor.getActiveEntity() if set: return sIS.getImplants(set.ID) return [] def addImplantToContext(self, item): sIS = service.ImplantSets.getInstance() - set = self.Parent.getActiveSet() + set = self.Parent.entityEditor.getActiveEntity() sIS.addImplant(set.ID, item.ID) - def removeImplantFromContext(self, pos): + def removeImplantFromContext(self, implant): sIS = service.ImplantSets.getInstance() - set = self.Parent.getActiveSet() - - sIS.removeImplant(set.ID, self.implants[pos]) + set = self.Parent.entityEditor.getActiveEntity() + sIS.removeImplant(set.ID, implant) class ImplantSetEditorDlg(wx.Dialog): @@ -66,55 +119,8 @@ class ImplantSetEditorDlg(wx.Dialog): mainSizer = wx.BoxSizer(wx.VERTICAL) - self.headerSizer = headerSizer = wx.BoxSizer(wx.HORIZONTAL) - - sIS = service.ImplantSets.getInstance() - - self.choices = sIS.getImplantSetList() - - # Sort the remaining list and continue on - self.choices.sort(key=lambda s: s.name) - self.ccSets = wx.Choice(self, wx.ID_ANY, style=0) - - for set in self.choices: - i = self.ccSets.Append(set.name, set.ID) - - self.ccSets.Bind(wx.EVT_CHOICE, self.setChanged) - self.ccSets.SetSelection(0) - - self.namePicker = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER) - self.namePicker.Bind(wx.EVT_TEXT_ENTER, self.processRename) - self.namePicker.Hide() - - size = None - headerSizer.Add(self.ccSets, 1, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 3) - - buttons = (("new", wx.ART_NEW), - ("rename", BitmapLoader.getBitmap("rename", "gui")), - ("copy", wx.ART_COPY), - ("delete", wx.ART_DELETE)) - - for name, art in buttons: - bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art - btn = wx.BitmapButton(self, wx.ID_ANY, bitmap) - if size is None: - size = btn.GetSize() - - btn.SetMinSize(size) - btn.SetMaxSize(size) - - btn.Layout() - setattr(self, name, btn) - btn.Enable(True) - btn.SetToolTipString("%s implant set" % name.capitalize()) - headerSizer.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL) - - self.btnSave = wx.Button(self, wx.ID_SAVE) - self.btnSave.Hide() - self.btnSave.Bind(wx.EVT_BUTTON, self.processRename) - headerSizer.Add(self.btnSave, 0, wx.ALIGN_CENTER) - - mainSizer.Add(headerSizer, 0, wx.EXPAND | wx.ALL, 2) + self.entityEditor = ImplantSetEntityEditor(self) + mainSizer.Add(self.entityEditor, 0, wx.ALL | wx.EXPAND, 2) self.sl = wx.StaticLine(self) mainSizer.Add(self.sl, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) @@ -157,174 +163,15 @@ class ImplantSetEditorDlg(wx.Dialog): self.SetSizer(mainSizer) self.Layout() - self.new.Bind(wx.EVT_BUTTON, self.newSet) - self.rename.Bind(wx.EVT_BUTTON, self.renameSet) - self.copy.Bind(wx.EVT_BUTTON, self.copySet) - self.delete.Bind(wx.EVT_BUTTON, self.deleteSet) self.Import.Bind(wx.EVT_BUTTON, self.importPatterns) self.Export.Bind(wx.EVT_BUTTON, self.exportPatterns) def closeEvent(self, event): self.Destroy() - def getActiveSet(self): - selection = self.ccSets.GetCurrentSelection() - return self.ccSets.GetClientData(selection) if selection is not None else None - - def restrict(self): - self.rename.Enable(False) - self.delete.Enable(False) - - def unrestrict(self): - self.rename.Enable() - self.delete.Enable() - - def getActiveSet(self): - if len(self.choices) == 0: - return None - - return self.choices[self.ccSets.GetSelection()] - - def setChanged(self, event=None): - "Event fired when user selects pattern. Can also be called from script" - p = self.getActiveSet() - self.iview.update() - if p is None: - # This happens when there are no patterns in the DB. As such, force - # user to create one first or exit dlg. - self.newSet(None) - return - - def newSet(self, event): - ''' - Simply does new-set specifics: replaces label on button, restricts, - and resets values to default. Hands off to the rename function for - further handling. - ''' - self.btnSave.SetLabel("Create") - self.restrict() - - self.Refresh() - self.renameSet() - - def renameSet(self, event=None): - "Changes layout to facilitate naming a pattern" - - self.showInput(True) - - if event is not None: # Rename mode - self.btnSave.SetLabel("Rename") - self.namePicker.SetValue(self.getActiveSet().name) - else: # Create mode - self.namePicker.SetValue("") - - if event is not None: - event.Skip() - - def processRename(self, event): - ''' - Processes rename event (which can be new or old patterns). If new - pattern, creates it; if old, selects it. if checks are valid, rename - saves pattern to DB. - - Also resets to default layout and unrestricts. - ''' - newName = self.namePicker.GetLineText(0) - self.stNotice.SetLabel("") - - if newName == "": - self.stNotice.SetLabel("Invalid name") - return - - sIS = service.ImplantSets.getInstance() - if self.btnSave.Label == "Create": - s = sIS.newSet() - else: - # we are renaming, so get the current selection - s = self.getActiveSet() - - # test for patterns of the same name - for set in self.choices: - if set.name == newName and s != set: - self.stNotice.SetLabel("Name already used, please choose another") - return - - # rename regardless of new or rename - sIS.renameSet(s, newName) - - self.updateChoices(newName) - self.showInput(False) - sel = self.ccSets.GetSelection() - self.unrestrict() - - def copySet(self,event): - sIS = service.ImplantSets.getInstance() - p = sIS.copySet(self.getActiveSet()) - self.choices.append(p) - id = self.ccSets.Append(p.name) - self.ccSets.SetSelection(id) - self.btnSave.SetLabel("Copy") - self.renameSet() - self.setChanged() - - def deleteSet(self,event): - sIS = service.ImplantSets.getInstance() - sel = self.ccSets.GetSelection() - sIS.deleteSet(self.getActiveSet()) - self.ccSets.Delete(sel) - self.ccSets.SetSelection(max(0, sel - 1)) - del self.choices[sel] - self.setChanged() - - def showInput(self, bool): - if bool and not self.namePicker.IsShown(): - self.ccSets.Hide() - self.namePicker.Show() - self.headerSizer.Replace(self.ccSets, self.namePicker) - self.namePicker.SetFocus() - for btn in (self.new, self.rename, self.delete, self.copy): - btn.Hide() - self.btnSave.Show() - self.restrict() - self.headerSizer.Layout() - elif not bool and self.namePicker.IsShown(): - self.headerSizer.Replace(self.namePicker, self.ccSets) - self.ccSets.Show() - self.namePicker.Hide() - self.btnSave.Hide() - for btn in (self.new, self.rename, self.delete, self.copy): - btn.Show() - self.unrestrict() - self.headerSizer.Layout() - - def __del__( self ): pass - def updateChoices(self, select=None): - "Gathers list of patterns and updates choice selections" - sIS = service.ImplantSets.getInstance() - self.choices = sIS.getImplantSetList() - - if len(self.choices) == 0: - #self.newPattern(None) - return - - # Sort the remaining list and continue on - self.choices.sort(key=lambda p: p.name) - self.ccSets.Clear() - - for i, choice in enumerate(map(lambda p: p.name, self.choices)): - self.ccSets.Append(choice) - - if select is not None and choice == select: - self.ccSets.SetSelection(i) - - if select is None: - self.ccSets.SetSelection(0) - - self.setChanged() - def importPatterns(self, event): "Event fired when import from clipboard button is clicked" diff --git a/service/implantSet.py b/service/implantSet.py index 4a517f0ca..0d0df5808 100644 --- a/service/implantSet.py +++ b/service/implantSet.py @@ -55,10 +55,11 @@ class ImplantSets(): set.implants.remove(implant) eos.db.commit() - def newSet(self): - p = eos.types.ImplantSet() - p.name = "" - return p + def newSet(self, name): + s = eos.types.ImplantSet() + s.name = name + eos.db.save(s) + return s def renameSet(self, s, newName): s.name = newName From 39b2f87194ed724528fd94f5384b51326d9be2c2 Mon Sep 17 00:00:00 2001 From: blitzmann Date: Wed, 30 Mar 2016 00:03:48 -0400 Subject: [PATCH 30/30] Add in logic that required an entity for implant sets and target resists (not needed for character or damage profiles as we have read-only defaults) --- gui/builtinViews/entityEditor.py | 21 ++++++++++++++++++--- gui/mainFrame.py | 8 ++------ gui/resistsEditor.py | 13 ++++++++++++- gui/setEditor.py | 16 +++++++++++++++- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index faaf1e3bb..112c1c63e 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -18,6 +18,8 @@ class BaseValidator(wx.PyValidator): class TextEntryValidatedDialog(wx.TextEntryDialog): def __init__(self, parent, validator=None, *args, **kargs): wx.TextEntryDialog.__init__(self, parent, *args, **kargs) + self.parent = parent + self.txtctrl = self.FindWindowById(3000) if validator: self.txtctrl.SetValidator(validator()) @@ -35,7 +37,7 @@ class EntityEditor (wx.Panel): self.validator = None self.navSizer = wx.BoxSizer(wx.HORIZONTAL) - self.choices = self.getEntitiesFromContext() + self.choices = [] self.choices.sort(key=lambda p: p.name) self.entityChoices = wx.Choice(self, choices=map(lambda p: p.name, self.choices)) self.navSizer.Add(self.entityChoices, 1, wx.ALL, 5) @@ -60,10 +62,11 @@ class EntityEditor (wx.Panel): setattr(self, "btn%s" % name.capitalize(), btn) self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2) - self.entityChoices.SetSelection(0) self.SetSizer(self.navSizer) self.Layout() + self.refreshEntityList() + def SetEditorValidator(self, validator=None): """ Sets validator class (not an instance of the class) """ self.validator = validator @@ -98,6 +101,8 @@ class EntityEditor (wx.Panel): new = self.DoNew(dlg.GetValue().strip()) self.refreshEntityList(new) wx.PostEvent(self.entityChoices, wx.CommandEvent(wx.wxEVT_COMMAND_CHOICE_SELECTED)) + else: + return False def OnCopy(self, event): dlg = TextEntryValidatedDialog(self, self.validator, @@ -141,6 +146,7 @@ class EntityEditor (wx.Panel): def refreshEntityList(self, selected=None): self.choices = self.getEntitiesFromContext() self.entityChoices.Clear() + self.entityChoices.AppendItems(map(lambda p: p.name, self.choices)) if selected: idx = self.choices.index(selected) @@ -155,4 +161,13 @@ class EntityEditor (wx.Panel): return self.choices[self.entityChoices.GetSelection()] def setActiveEntity(self, entity): - self.entityChoices.SetSelection(self.choices.index(entity)) \ No newline at end of file + self.entityChoices.SetSelection(self.choices.index(entity)) + + def checkEntitiesExist(self): + if len(self.choices) == 0: + self.Parent.Hide() + if self.OnNew(None) is False: + return False + self.Parent.Show() + + return True \ No newline at end of file diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 3b76a13c9..7557813db 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -359,9 +359,7 @@ class MainFrame(wx.Frame): dlg.Show() def showTargetResistsEditor(self, event): - dlg=ResistsEditorDlg(self) - dlg.ShowModal() - dlg.Destroy() + ResistsEditorDlg(self) def showDamagePatternEditor(self, event): dlg=DmgPatternEditorDlg(self) @@ -369,9 +367,7 @@ class MainFrame(wx.Frame): dlg.Destroy() def showImplantSetEditor(self, event): - dlg=ImplantSetEditorDlg(self) - dlg.ShowModal() - dlg.Destroy() + ImplantSetEditorDlg(self) def showExportDialog(self, event): """ Export active fit """ diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index 57b438dc5..374f9c1fc 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -33,7 +33,7 @@ class TargetResistsTextValidor(BaseValidator): return TargetResistsTextValidor() def Validate(self, win): - profileEditor = win.Parent + profileEditor = win.parent.Parent textCtrl = self.GetWindow() text = textCtrl.GetValue().strip() @@ -169,6 +169,10 @@ class ResistsEditorDlg(wx.Dialog): btn.SetToolTipString("%s patterns %s clipboard" % (name, direction) ) footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) + if not self.entityEditor.checkEntitiesExist(): + self.Destroy() + return + self.Layout() bsize = self.GetBestSize() self.SetSize((-1, bsize.height)) @@ -178,6 +182,8 @@ class ResistsEditorDlg(wx.Dialog): self.patternChanged() + self.ShowModal() + def closeEvent(self, event): self.Destroy() @@ -230,6 +236,11 @@ class ResistsEditorDlg(wx.Dialog): def patternChanged(self, event=None): "Event fired when user selects pattern. Can also be called from script" + + if not self.entityEditor.checkEntitiesExist(): + self.Destroy() + return + p = self.entityEditor.getActiveEntity() if p is None: return diff --git a/gui/setEditor.py b/gui/setEditor.py index 3b283d601..4e8baa9ed 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -36,7 +36,7 @@ class ImplantTextValidor(BaseValidator): return ImplantTextValidor() def Validate(self, win): - profileEditor = win.Parent + profileEditor = win.parent.Parent textCtrl = self.GetWindow() text = textCtrl.GetValue().strip() @@ -163,9 +163,23 @@ class ImplantSetEditorDlg(wx.Dialog): self.SetSizer(mainSizer) self.Layout() + if not self.entityEditor.checkEntitiesExist(): + self.Destroy() + return + + self.Bind(wx.EVT_CHOICE, self.entityChanged) + self.Import.Bind(wx.EVT_BUTTON, self.importPatterns) self.Export.Bind(wx.EVT_BUTTON, self.exportPatterns) + self.CenterOnParent() + self.ShowModal() + + def entityChanged(self, event): + if not self.entityEditor.checkEntitiesExist(): + self.Destroy() + return + def closeEvent(self, event): self.Destroy()