Store wrappers in graph lists
This commit is contained in:
@@ -247,15 +247,20 @@ class GraphFrame(wx.Frame):
|
||||
|
||||
mainInput, miscInputs = self.ctrlPanel.getValues()
|
||||
view = self.getView()
|
||||
fits = self.ctrlPanel.fits
|
||||
sources = self.ctrlPanel.sources
|
||||
if view.hasTargets:
|
||||
targets = self.ctrlPanel.targets
|
||||
iterList = tuple(itertools.product(fits, targets))
|
||||
iterList = tuple(itertools.product(sources, self.ctrlPanel.targets))
|
||||
else:
|
||||
iterList = tuple((f, None) for f in fits)
|
||||
for fit, target in iterList:
|
||||
iterList = tuple((f, None) for f in sources)
|
||||
for source, target in iterList:
|
||||
try:
|
||||
xs, ys = view.getPlotPoints(mainInput, miscInputs, chosenX, chosenY, fit, target)
|
||||
xs, ys = view.getPlotPoints(
|
||||
mainInput=mainInput,
|
||||
miscInputs=miscInputs,
|
||||
xSpec=chosenX,
|
||||
ySpec=chosenY,
|
||||
src=source,
|
||||
tgt=target)
|
||||
|
||||
# Figure out min and max Y
|
||||
min_y_this = min(ys, default=None)
|
||||
@@ -275,11 +280,11 @@ class GraphFrame(wx.Frame):
|
||||
self.subplot.plot(xs, ys)
|
||||
|
||||
if target is None:
|
||||
legend.append(self.getObjName(fit))
|
||||
legend.append(source.shortName)
|
||||
else:
|
||||
legend.append('{} vs {}'.format(self.getObjName(fit), self.getObjName(target)))
|
||||
legend.append('{} vs {}'.format(source.shortName, target.shortName))
|
||||
except Exception as ex:
|
||||
pyfalog.warning('Invalid values in "{0}"', fit.name)
|
||||
pyfalog.warning('Invalid values in "{0}"', source.name)
|
||||
self.canvas.draw()
|
||||
self.Refresh()
|
||||
return
|
||||
@@ -330,12 +335,3 @@ class GraphFrame(wx.Frame):
|
||||
|
||||
self.canvas.draw()
|
||||
self.Refresh()
|
||||
|
||||
@staticmethod
|
||||
def getObjName(thing):
|
||||
if isinstance(thing, Fit):
|
||||
return '{} ({})'.format(thing.name, thing.ship.item.getShortName())
|
||||
elif isinstance(thing, TargetProfile):
|
||||
return thing.name
|
||||
return ''
|
||||
|
||||
|
||||
@@ -23,12 +23,13 @@ import wx
|
||||
|
||||
import gui.display
|
||||
from eos.saveddata.targetProfile import TargetProfile
|
||||
from graphs.wrapper import SourceWrapper, TargetWrapper
|
||||
from gui.contextMenu import ContextMenu
|
||||
from service.const import GraphCacheCleanupReason
|
||||
from service.fit import Fit
|
||||
|
||||
|
||||
class BaseList(gui.display.Display):
|
||||
class BaseWrapperList(gui.display.Display):
|
||||
|
||||
DEFAULT_COLS = (
|
||||
'Base Icon',
|
||||
@@ -37,7 +38,7 @@ class BaseList(gui.display.Display):
|
||||
def __init__(self, graphFrame, parent):
|
||||
super().__init__(parent)
|
||||
self.graphFrame = graphFrame
|
||||
self.fits = []
|
||||
self._wrappers = []
|
||||
|
||||
self.hoveredRow = None
|
||||
self.hoveredColumn = None
|
||||
@@ -47,6 +48,15 @@ class BaseList(gui.display.Display):
|
||||
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
|
||||
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
|
||||
|
||||
@property
|
||||
def wrappers(self):
|
||||
return sorted(self._wrappers, key=lambda w: w.isFit)
|
||||
|
||||
# UI-related stuff
|
||||
@property
|
||||
def defaultTTText(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def refreshExtraColumns(self, extraColSpecs):
|
||||
baseColNames = set()
|
||||
for baseColName in self.DEFAULT_COLS:
|
||||
@@ -63,31 +73,13 @@ class BaseList(gui.display.Display):
|
||||
self.appendColumnBySpec(colSpec)
|
||||
self.refreshView()
|
||||
|
||||
def handleDrag(self, type, fitID):
|
||||
if type == 'fit':
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit not in self.fits:
|
||||
self.fits.append(fit)
|
||||
self.updateView()
|
||||
self.graphFrame.draw()
|
||||
def refreshView(self):
|
||||
self.refresh(self.wrappers)
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.removeListItems(self.getSelectedListItems())
|
||||
event.Skip()
|
||||
|
||||
def OnLeftDClick(self, event):
|
||||
row, _ = self.HitTest(event.Position)
|
||||
item = self.getListItem(row)
|
||||
if item is None:
|
||||
return
|
||||
self.removeListItems([item])
|
||||
def updateView(self):
|
||||
self.update(self.wrappers)
|
||||
|
||||
# UI event handling
|
||||
def OnMouseMove(self, event):
|
||||
row, _, col = self.HitTestSubItem(event.Position)
|
||||
if row != self.hoveredRow or col != self.hoveredColumn:
|
||||
@@ -97,7 +89,7 @@ class BaseList(gui.display.Display):
|
||||
self.hoveredRow = row
|
||||
self.hoveredColumn = col
|
||||
if row != -1 and col != -1 and col < self.ColumnCount:
|
||||
item = self.getListItem(row)
|
||||
item = self.getWrapper(row)
|
||||
if item is None:
|
||||
return
|
||||
tooltip = self.activeColumns[col].getToolTip(item)
|
||||
@@ -115,70 +107,138 @@ class BaseList(gui.display.Display):
|
||||
self.hoveredColumn = None
|
||||
event.Skip()
|
||||
|
||||
# Fit events
|
||||
def handleDrag(self, type, fitID):
|
||||
if type == 'fit' and not self.containsFitID(fitID):
|
||||
sFit = Fit.getInstance()
|
||||
fit = sFit.getFit(fitID)
|
||||
self.appendItem(fit)
|
||||
self.updateView()
|
||||
self.graphFrame.draw()
|
||||
|
||||
def OnLeftDClick(self, event):
|
||||
row, _ = self.HitTest(event.Position)
|
||||
wrapper = self.getWrapper(row)
|
||||
if wrapper is None:
|
||||
return
|
||||
self.removeWrappers([wrapper])
|
||||
|
||||
def kbEvent(self, event):
|
||||
keycode = event.GetKeyCode()
|
||||
mstate = wx.GetMouseState()
|
||||
if keycode == 65 and mstate.GetModifiers() == wx.MOD_CONTROL:
|
||||
self.selectAll()
|
||||
elif keycode in (wx.WXK_DELETE, wx.WXK_NUMPAD_DELETE) and mstate.GetModifiers() == wx.MOD_NONE:
|
||||
self.removeWrappers(self.getSelectedWrappers())
|
||||
event.Skip()
|
||||
|
||||
# Wrapper-related methods
|
||||
@property
|
||||
def wrapperClass(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def getWrapper(self, row):
|
||||
if row == -1:
|
||||
return None
|
||||
try:
|
||||
return self._wrappers[row]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def removeWrappers(self, wrappers):
|
||||
wrappers = set(wrappers).union(self._wrappers)
|
||||
if not wrappers:
|
||||
return
|
||||
for wrapper in wrappers:
|
||||
self._wrappers.remove(wrapper)
|
||||
self.updateView()
|
||||
for wrapper in wrappers:
|
||||
if wrapper.isFit:
|
||||
self.graphFrame.clearCache(reason=GraphCacheCleanupReason.fitRemoved, extraData=wrapper.fitID)
|
||||
elif wrapper.isProfile:
|
||||
self.graphFrame.clearCache(reason=GraphCacheCleanupReason.profileRemoved, extraData=wrapper.profileID)
|
||||
self.graphFrame.draw()
|
||||
|
||||
def getSelectedWrappers(self):
|
||||
wrappers = []
|
||||
for row in self.getSelectedRows():
|
||||
wrapper = self.getWrapper(row)
|
||||
if wrapper is None:
|
||||
continue
|
||||
wrappers.append(wrapper)
|
||||
return wrappers
|
||||
|
||||
def appendItem(self, item):
|
||||
self._wrappers.append(self.wrapperClass(item))
|
||||
|
||||
def containsFitID(self, fitID):
|
||||
for wrapper in self._wrappers:
|
||||
if wrapper.isFit and wrapper.itemID == fitID:
|
||||
return True
|
||||
return False
|
||||
|
||||
def containsProfileID(self, profileID):
|
||||
for wrapper in self._wrappers:
|
||||
if wrapper.isProfile and wrapper.itemID == profileID:
|
||||
return True
|
||||
return False
|
||||
|
||||
# Wrapper-related events
|
||||
def OnFitRenamed(self, event):
|
||||
if event.fitID in [f.ID for f in self.fits]:
|
||||
if self.containsFitID(event.fitID):
|
||||
self.updateView()
|
||||
|
||||
def OnFitChanged(self, event):
|
||||
if set(event.fitIDs).union(f.ID for f in self.fits):
|
||||
if set(event.fitIDs).union(w.itemID for w in self._wrappers if w.isFit):
|
||||
self.updateView()
|
||||
|
||||
def OnFitRemoved(self, event):
|
||||
fit = next((f for f in self.fits if f.ID == event.fitID), None)
|
||||
if fit is not None:
|
||||
self.fits.remove(fit)
|
||||
wrapper = next((w for w in self._wrappers if w.isFit and w.itemID == event.fitID), None)
|
||||
if wrapper is not None:
|
||||
self._wrappers.remove(wrapper)
|
||||
self.updateView()
|
||||
|
||||
@property
|
||||
def defaultTTText(self):
|
||||
raise NotImplementedError
|
||||
def OnProfileRenamed(self, event):
|
||||
if self.containsProfileID(event.profileID):
|
||||
self.updateView()
|
||||
|
||||
def refreshView(self):
|
||||
raise NotImplementedError
|
||||
def OnProfileChanged(self, event):
|
||||
if self.containsProfileID(event.profileID):
|
||||
self.updateView()
|
||||
|
||||
def updateView(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def getListItem(self, row):
|
||||
raise NotImplementedError
|
||||
|
||||
def removeListItems(self, items):
|
||||
raise NotImplementedError
|
||||
|
||||
def getSelectedListItems(self):
|
||||
items = []
|
||||
for row in self.getSelectedRows():
|
||||
item = self.getListItem(row)
|
||||
if item is None:
|
||||
continue
|
||||
items.append(item)
|
||||
return items
|
||||
def OnProfileRemoved(self, event):
|
||||
wrapper = next((w for w in self._wrappers if w.isProfile and w.itemID == event.profileID), None)
|
||||
if wrapper is not None:
|
||||
self._wrappers.remove(wrapper)
|
||||
self.updateView()
|
||||
|
||||
# Context menu handlers
|
||||
def addFit(self, fit):
|
||||
if fit is None:
|
||||
return
|
||||
if fit in self.fits:
|
||||
if self.containsFitID(fit.ID):
|
||||
return
|
||||
self.fits.append(fit)
|
||||
self.appendItem(fit)
|
||||
self.updateView()
|
||||
self.graphFrame.draw()
|
||||
|
||||
def getExistingFitIDs(self):
|
||||
return [f.ID for f in self.fits]
|
||||
return [w.itemID for w in self._wrappers if w.isFit]
|
||||
|
||||
def addFitsByIDs(self, fitIDs):
|
||||
sFit = Fit.getInstance()
|
||||
for fitID in fitIDs:
|
||||
if self.containsFitID(fitID):
|
||||
continue
|
||||
fit = sFit.getFit(fitID)
|
||||
if fit is not None:
|
||||
self.fits.append(fit)
|
||||
self.appendItem(fit)
|
||||
self.updateView()
|
||||
self.graphFrame.draw()
|
||||
|
||||
|
||||
class FitList(BaseList):
|
||||
class SourceWrapperList(BaseWrapperList):
|
||||
|
||||
wrapperClass = SourceWrapper
|
||||
|
||||
def __init__(self, graphFrame, parent):
|
||||
super().__init__(graphFrame, parent)
|
||||
@@ -187,19 +247,13 @@ class FitList(BaseList):
|
||||
|
||||
fit = Fit.getInstance().getFit(self.graphFrame.mainFrame.getActiveFit())
|
||||
if fit is not None:
|
||||
self.fits.append(fit)
|
||||
self.appendItem(fit)
|
||||
self.updateView()
|
||||
|
||||
def refreshView(self):
|
||||
self.refresh(self.fits)
|
||||
|
||||
def updateView(self):
|
||||
self.update(self.fits)
|
||||
|
||||
def spawnMenu(self, event):
|
||||
selection = self.getSelectedListItems()
|
||||
selection = self.getSelectedWrappers()
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainItem = self.getListItem(clickedPos)
|
||||
mainItem = self.getWrapper(clickedPos)
|
||||
|
||||
sourceContext = 'graphFitList'
|
||||
itemContext = None if mainItem is None else 'Fit'
|
||||
@@ -207,51 +261,27 @@ class FitList(BaseList):
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def getListItem(self, row):
|
||||
if row == -1:
|
||||
return None
|
||||
try:
|
||||
return self.fits[row]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def removeListItems(self, items):
|
||||
toRemove = [i for i in items if i in self.fits]
|
||||
if not toRemove:
|
||||
return
|
||||
for fit in toRemove:
|
||||
self.fits.remove(fit)
|
||||
self.updateView()
|
||||
for fit in toRemove:
|
||||
self.graphFrame.clearCache(reason=GraphCacheCleanupReason.fitRemoved, extraData=fit.ID)
|
||||
self.graphFrame.draw()
|
||||
|
||||
@property
|
||||
def defaultTTText(self):
|
||||
return 'Drag a fit into this list to graph it'
|
||||
|
||||
|
||||
class TargetList(BaseList):
|
||||
class TargetWrapperList(BaseWrapperList):
|
||||
|
||||
wrapperClass = TargetWrapper
|
||||
|
||||
def __init__(self, graphFrame, parent):
|
||||
super().__init__(graphFrame, parent)
|
||||
|
||||
self.Bind(wx.EVT_CONTEXT_MENU, self.spawnMenu)
|
||||
|
||||
self.profiles = []
|
||||
self.profiles.append(TargetProfile.getIdeal())
|
||||
self.appendItem(TargetProfile.getIdeal())
|
||||
self.updateView()
|
||||
|
||||
def refreshView(self):
|
||||
self.refresh(self.targets)
|
||||
|
||||
def updateView(self):
|
||||
self.update(self.targets)
|
||||
|
||||
def spawnMenu(self, event):
|
||||
selection = self.getSelectedListItems()
|
||||
selection = self.getSelectedWrappers()
|
||||
clickedPos = self.getRowByAbs(event.Position)
|
||||
mainItem = self.getListItem(clickedPos)
|
||||
mainItem = self.getWrapper(clickedPos)
|
||||
|
||||
sourceContext = 'graphTgtList'
|
||||
itemContext = None if mainItem is None else 'Target'
|
||||
@@ -259,56 +289,6 @@ class TargetList(BaseList):
|
||||
if menu:
|
||||
self.PopupMenu(menu)
|
||||
|
||||
def getListItem(self, row):
|
||||
if row == -1:
|
||||
return None
|
||||
|
||||
numFits = len(self.fits)
|
||||
numProfiles = len(self.profiles)
|
||||
|
||||
if (numFits + numProfiles) == 0:
|
||||
return None
|
||||
|
||||
if row < numFits:
|
||||
return self.fits[row]
|
||||
else:
|
||||
return self.profiles[row - numFits]
|
||||
|
||||
def removeListItems(self, items):
|
||||
fitsToRemove = [i for i in items if i in self.fits]
|
||||
profilesToRemove = [i for i in items if i in self.profiles]
|
||||
if not fitsToRemove and not profilesToRemove:
|
||||
return
|
||||
for fit in fitsToRemove:
|
||||
self.fits.remove(fit)
|
||||
for profile in profilesToRemove:
|
||||
self.profiles.remove(profile)
|
||||
self.updateView()
|
||||
for fit in fitsToRemove:
|
||||
self.graphFrame.clearCache(reason=GraphCacheCleanupReason.fitRemoved, extraData=fit.ID)
|
||||
for profile in profilesToRemove:
|
||||
self.graphFrame.clearCache(reason=GraphCacheCleanupReason.profileRemoved, extraData=profile.ID)
|
||||
self.graphFrame.draw()
|
||||
|
||||
# Target profile events
|
||||
def OnProfileRenamed(self, event):
|
||||
if event.profileID in [tp.ID for tp in self.profiles]:
|
||||
self.updateView()
|
||||
|
||||
def OnProfileChanged(self, event):
|
||||
if event.profileID in [tp.ID for tp in self.profiles]:
|
||||
self.updateView()
|
||||
|
||||
def OnProfileRemoved(self, event):
|
||||
profile = next((tp for tp in self.profiles if tp.ID == event.profileID), None)
|
||||
if profile is not None:
|
||||
self.profiles.remove(profile)
|
||||
self.updateView()
|
||||
|
||||
@property
|
||||
def targets(self):
|
||||
return self.fits + self.profiles
|
||||
|
||||
@property
|
||||
def defaultTTText(self):
|
||||
return 'Drag a fit into this list to have your fits graphed against it'
|
||||
@@ -317,8 +297,8 @@ class TargetList(BaseList):
|
||||
def addProfile(self, profile):
|
||||
if profile is None:
|
||||
return
|
||||
if profile in self.profiles:
|
||||
if self.containsProfileID(profile.ID):
|
||||
return
|
||||
self.profiles.append(profile)
|
||||
self.appendItem(profile)
|
||||
self.updateView()
|
||||
self.graphFrame.draw()
|
||||
|
||||
@@ -28,7 +28,7 @@ from gui.contextMenu import ContextMenu
|
||||
from gui.utils.inputs import FloatBox, FloatRangeBox
|
||||
from service.const import GraphCacheCleanupReason
|
||||
from service.fit import Fit
|
||||
from .lists import FitList, TargetList
|
||||
from .lists import SourceWrapperList, TargetWrapperList
|
||||
from .vector import VectorPicker
|
||||
|
||||
|
||||
@@ -114,10 +114,10 @@ class GraphControlPanel(wx.Panel):
|
||||
mainSizer.Add(optsSizer, 0, wx.EXPAND | wx.ALL, 10)
|
||||
|
||||
srcTgtSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.fitList = FitList(graphFrame, self)
|
||||
self.fitList.SetMinSize((270, -1))
|
||||
srcTgtSizer.Add(self.fitList, 1, wx.EXPAND | wx.ALL, 0)
|
||||
self.targetList = TargetList(graphFrame, self)
|
||||
self.sourceList = SourceWrapperList(graphFrame, self)
|
||||
self.sourceList.SetMinSize((270, -1))
|
||||
srcTgtSizer.Add(self.sourceList, 1, wx.EXPAND | wx.ALL, 0)
|
||||
self.targetList = TargetWrapperList(graphFrame, self)
|
||||
self.targetList.SetMinSize((270, -1))
|
||||
srcTgtSizer.Add(self.targetList, 1, wx.EXPAND | wx.LEFT, 10)
|
||||
mainSizer.Add(srcTgtSizer, 1, wx.EXPAND | wx.LEFT | wx.BOTTOM | wx.RIGHT, 10)
|
||||
@@ -162,7 +162,7 @@ class GraphControlPanel(wx.Panel):
|
||||
self.tgtVectorLabel.Show(False)
|
||||
|
||||
# Source and target list
|
||||
self.fitList.refreshExtraColumns(view.srcExtraCols)
|
||||
self.sourceList.refreshExtraColumns(view.srcExtraCols)
|
||||
self.targetList.refreshExtraColumns(view.tgtExtraCols)
|
||||
self.targetList.Show(view.hasTargets)
|
||||
|
||||
@@ -333,34 +333,37 @@ class GraphControlPanel(wx.Panel):
|
||||
return self.xSubSelection.GetClientData(self.xSubSelection.GetSelection())
|
||||
|
||||
@property
|
||||
def fits(self):
|
||||
return self.fitList.fits
|
||||
def sources(self):
|
||||
return self.sourceList.wrappers
|
||||
|
||||
@property
|
||||
def targets(self):
|
||||
return self.targetList.targets
|
||||
return self.targetList.wrappers
|
||||
|
||||
# Fit events
|
||||
def OnFitRenamed(self, event):
|
||||
self.fitList.OnFitRenamed(event)
|
||||
self.sourceList.OnFitRenamed(event)
|
||||
self.targetList.OnFitRenamed(event)
|
||||
|
||||
def OnFitChanged(self, event):
|
||||
self.fitList.OnFitChanged(event)
|
||||
self.sourceList.OnFitChanged(event)
|
||||
self.targetList.OnFitChanged(event)
|
||||
|
||||
def OnFitRemoved(self, event):
|
||||
self.fitList.OnFitRemoved(event)
|
||||
self.sourceList.OnFitRemoved(event)
|
||||
self.targetList.OnFitRemoved(event)
|
||||
|
||||
# Target profile events
|
||||
def OnProfileRenamed(self, event):
|
||||
self.sourceList.OnProfileRenamed(event)
|
||||
self.targetList.OnProfileRenamed(event)
|
||||
|
||||
def OnProfileChanged(self, event):
|
||||
self.sourceList.OnProfileChanged(event)
|
||||
self.targetList.OnProfileChanged(event)
|
||||
|
||||
def OnProfileRemoved(self, event):
|
||||
self.sourceList.OnProfileRemoved(event)
|
||||
self.targetList.OnProfileRemoved(event)
|
||||
|
||||
def formatLabel(self, axisDef):
|
||||
|
||||
Reference in New Issue
Block a user