530 lines
19 KiB
Python
530 lines
19 KiB
Python
#===============================================================================
|
|
# 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 <http://www.gnu.org/licenses/>.
|
|
#===============================================================================
|
|
|
|
import sys
|
|
import os.path
|
|
|
|
import sqlalchemy
|
|
import wx
|
|
|
|
from wx._core import PyDeadObjectError
|
|
from wx.lib.wordwrap import wordwrap
|
|
|
|
import service
|
|
import config
|
|
|
|
import gui.aboutData
|
|
import gui.chromeTabs
|
|
import gui.utils.animUtils as animUtils
|
|
|
|
from gui import bitmapLoader
|
|
from gui.mainMenuBar import MainMenuBar
|
|
from gui.additionsPane import AdditionsPane
|
|
from gui.marketBrowser import MarketBrowser
|
|
from gui.multiSwitch import MultiSwitch
|
|
from gui.statsPane import StatsPane
|
|
from gui.shipBrowser import ShipBrowser, FitSelected
|
|
from gui.characterEditor import CharacterEditor
|
|
from gui.characterSelection import CharacterSelection
|
|
from gui.patternEditor import DmgPatternEditorDlg
|
|
from gui.preferenceDialog import PreferenceDialog
|
|
from gui.graphFrame import GraphFrame
|
|
from gui.copySelectDialog import CopySelectDialog
|
|
from gui.utils.clipboard import toClipboard, fromClipboard
|
|
from gui.fleetBrowser import FleetBrowser
|
|
from gui.builtinViews import *
|
|
|
|
#dummy panel(no paint no erasebk)
|
|
class PFPanel(wx.Panel):
|
|
def __init__(self,parent):
|
|
wx.Panel.__init__(self,parent)
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnBkErase)
|
|
|
|
def OnPaint(self, event):
|
|
event.Skip()
|
|
def OnBkErase(self, event):
|
|
pass
|
|
|
|
class MainFrame(wx.Frame):
|
|
__instance = None
|
|
@classmethod
|
|
def getInstance(cls):
|
|
return cls.__instance if cls.__instance is not None else MainFrame()
|
|
|
|
def __init__(self):
|
|
wx.Frame.__init__(self, None, wx.ID_ANY, title="pyfa - Python Fitting Assistant")
|
|
|
|
MainFrame.__instance = self
|
|
|
|
#Load stored settings (width/height/maximized..)
|
|
self.LoadMainFrameAttribs()
|
|
|
|
#Fix for msw (have the frame background color match panel color
|
|
if 'wxMSW' in wx.PlatformInfo:
|
|
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
|
|
|
|
#Load and set the icon for pyfa main window
|
|
i = wx.IconFromBitmap(bitmapLoader.getBitmap("pyfa", "icons"))
|
|
self.SetIcon(i)
|
|
|
|
#Create the layout and windows
|
|
mainSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
|
self.splitter = wx.SplitterWindow(self, style = wx.SP_LIVE_UPDATE)
|
|
|
|
mainSizer.Add(self.splitter,1,wx.EXPAND | wx.LEFT, 2)
|
|
|
|
self.FitviewAdditionsPanel = PFPanel(self.splitter)
|
|
faSizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
self.fitMultiSwitch = MultiSwitch(self.FitviewAdditionsPanel)
|
|
|
|
faSizer.Add(self.fitMultiSwitch,1,wx.EXPAND)
|
|
|
|
self.additionsPane = AdditionsPane(self.FitviewAdditionsPanel)
|
|
faSizer.Add(self.additionsPane, 0, wx.EXPAND)
|
|
|
|
self.FitviewAdditionsPanel.SetSizer(faSizer)
|
|
|
|
|
|
self.notebookBrowsers = gui.chromeTabs.PFNotebook(self.splitter, False)
|
|
|
|
marketImg = bitmapLoader.getImage("market_small", "icons")
|
|
shipBrowserImg = bitmapLoader.getImage("ship_small", "icons")
|
|
|
|
self.marketBrowser = MarketBrowser(self.notebookBrowsers)
|
|
self.notebookBrowsers.AddPage(self.marketBrowser, "Market", tabImage = marketImg, showClose = False)
|
|
|
|
self.shipBrowser = ShipBrowser(self.notebookBrowsers)
|
|
self.notebookBrowsers.AddPage(self.shipBrowser, "Ships", tabImage = shipBrowserImg, showClose = False)
|
|
|
|
#=======================================================================
|
|
# DISABLED FOR RC2 RELEASE
|
|
#self.fleetBrowser = FleetBrowser(self.notebookBrowsers)
|
|
#self.notebookBrowsers.AddPage(self.fleetBrowser, "Fleets", showClose = False)
|
|
#=======================================================================
|
|
|
|
self.notebookBrowsers.SetSelection(1)
|
|
|
|
self.splitter.SplitVertically(self.notebookBrowsers, self.FitviewAdditionsPanel)
|
|
self.splitter.SetMinimumPaneSize(204)
|
|
self.splitter.SetSashPosition(300)
|
|
|
|
cstatsSizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
self.charSelection = CharacterSelection(self)
|
|
cstatsSizer.Add(self.charSelection, 0, wx.EXPAND)
|
|
|
|
self.statsPane = StatsPane(self)
|
|
cstatsSizer.Add(self.statsPane, 0, wx.EXPAND)
|
|
|
|
mainSizer.Add(cstatsSizer, 0 , wx.EXPAND)
|
|
|
|
self.SetSizer(mainSizer)
|
|
|
|
#Add menu
|
|
self.addPageId = wx.NewId()
|
|
self.closePageId = wx.NewId()
|
|
|
|
self.widgetInspectMenuID = wx.NewId()
|
|
self.SetMenuBar(MainMenuBar())
|
|
self.registerMenu()
|
|
|
|
|
|
|
|
#Internal vars to keep track of other windows (graphing/stats)
|
|
self.graphFrame = None
|
|
self.statsWnds = []
|
|
self.activeStatsWnd = None
|
|
|
|
|
|
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
|
|
|
#Show ourselves
|
|
self.Show()
|
|
|
|
def LoadMainFrameAttribs(self):
|
|
|
|
mainFrameDefaultAttribs = {"wnd_width":1000, "wnd_height": 700, "wnd_maximized": False}
|
|
self.mainFrameAttribs = service.SettingsProvider.getInstance().getSettings("pyfaMainWindowAttribs", mainFrameDefaultAttribs)
|
|
|
|
if self.mainFrameAttribs["wnd_maximized"]:
|
|
width = mainFrameDefaultAttribs["wnd_width"]
|
|
height = mainFrameDefaultAttribs["wnd_height"]
|
|
self.Maximize()
|
|
else:
|
|
width = self.mainFrameAttribs["wnd_width"]
|
|
height = self.mainFrameAttribs["wnd_height"]
|
|
|
|
self.SetSize((width, height))
|
|
self.SetMinSize((mainFrameDefaultAttribs["wnd_width"], mainFrameDefaultAttribs["wnd_height"]))
|
|
|
|
def UpdateMainFrameAttribs(self):
|
|
if self.IsIconized():
|
|
return
|
|
width,height = self.GetSize()
|
|
|
|
self.mainFrameAttribs["wnd_width"] = width
|
|
self.mainFrameAttribs["wnd_height"] = height
|
|
self.mainFrameAttribs["wnd_maximized"] = self.IsMaximized()
|
|
|
|
def SetActiveStatsWindow(self, wnd):
|
|
self.activeStatsWnd = wnd
|
|
|
|
def GetActiveStatsWindow(self):
|
|
|
|
if self.activeStatsWnd in self.statsWnds:
|
|
return self.activeStatsWnd
|
|
|
|
if len(self.statsWnds) > 0:
|
|
return self.statsWnds[len(self.statsWnds) - 1]
|
|
else:
|
|
return None
|
|
|
|
def RegisterStatsWindow(self, wnd):
|
|
self.statsWnds.append(wnd)
|
|
|
|
def UnregisterStatsWindow(self, wnd):
|
|
self.statsWnds.remove(wnd)
|
|
|
|
def getActiveFit(self):
|
|
p = self.fitMultiSwitch.GetSelectedPage()
|
|
m = getattr(p, "getActiveFit", None)
|
|
return m() if m is not None else None
|
|
|
|
def getActiveView(self):
|
|
sel = self.fitMultiSwitch.GetSelectedPage()
|
|
|
|
def CloseCurrentPage(self, evt):
|
|
ms = self.fitMultiSwitch
|
|
|
|
page = ms.GetSelection()
|
|
if page is not None:
|
|
ms.DeletePage(page)
|
|
|
|
def OnClose(self, event):
|
|
self.UpdateMainFrameAttribs()
|
|
service.SettingsProvider.getInstance().saveAll()
|
|
event.Skip()
|
|
|
|
def ExitApp(self, event):
|
|
self.Close()
|
|
event.Skip()
|
|
|
|
def ShowAboutBox(self, evt):
|
|
info = wx.AboutDialogInfo()
|
|
info.Name = "pyfa"
|
|
info.Version = gui.aboutData.versionString
|
|
info.Description = wordwrap(gui.aboutData.description + "\n\n\nDevelopers: " +
|
|
"".join(gui.aboutData.developers) +
|
|
"\n\nAdditional credits:\n " +
|
|
"\n ".join(gui.aboutData.credits)
|
|
+ "\n\nLicense: " +
|
|
gui.aboutData.license +
|
|
" - see included " +
|
|
gui.aboutData.licenseLocation +
|
|
"\n\nPython: \t" + sys.version +
|
|
"\nwxPython: \t" + wx.__version__ +
|
|
"\nSQLAlchemy: \t" + sqlalchemy.__version__,
|
|
700, wx.ClientDC(self))
|
|
info.WebSite = ("http://forums.eveonline.com/default.aspx?g=posts&t=247609", "pyfa thread at EVE Online forum")
|
|
wx.AboutBox(info)
|
|
|
|
|
|
def showCharacterEditor(self, event):
|
|
dlg=CharacterEditor(self)
|
|
dlg.Show()
|
|
|
|
def showDamagePatternEditor(self, event):
|
|
dlg=DmgPatternEditorDlg(self)
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
|
|
def showImportDialog(self, event):
|
|
fits = []
|
|
sFit = service.Fit.getInstance()
|
|
dlg=wx.FileDialog(
|
|
self,
|
|
"Open One Or More Fitting Files",
|
|
wildcard = "EFT text fitting files (*.cfg)|*.cfg|" \
|
|
"EvE XML fitting files (*.xml)|*.xml|" \
|
|
"All Files (*)|*",
|
|
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE)
|
|
if (dlg.ShowModal() == wx.ID_OK):
|
|
self.waitDialog = animUtils.WaitDialog(self, title = "Importing")
|
|
sFit.importFitsThreaded(dlg.GetPaths(), self.importCallback)
|
|
dlg.Destroy()
|
|
self.waitDialog.ShowModal()
|
|
|
|
|
|
def importCallback(self, fits):
|
|
self.waitDialog.Destroy()
|
|
sFit = service.Fit.getInstance()
|
|
IDs = sFit.saveImportedFits(fits)
|
|
self._openAfterImport(len(fits), IDs)
|
|
|
|
def _openAfterImport(self, importCount, fitIDs):
|
|
if importCount == 1:
|
|
if self.getActiveFit() != fitIDs[0]:
|
|
wx.PostEvent(self, FitSelected(fitID=fitIDs[0]))
|
|
|
|
self.shipBrowser.RefreshContent()
|
|
|
|
def showExportDialog(self, event):
|
|
dlg=wx.FileDialog(
|
|
self,
|
|
"Save Fitting As...",
|
|
wildcard = "EvE XML fitting files (*.xml)|*.xml",
|
|
style = wx.FD_SAVE)
|
|
if (dlg.ShowModal() == wx.ID_OK):
|
|
sFit = service.Fit.getInstance()
|
|
format = dlg.GetFilterIndex()
|
|
output = ""
|
|
path = dlg.GetPath()
|
|
if (format == 0):
|
|
output = sFit.exportXml(self.getActiveFit())
|
|
if '.' not in os.path.basename(path):
|
|
path += ".xml"
|
|
else:
|
|
print "oops, invalid fit format %d" % format
|
|
dlg.Destroy()
|
|
return
|
|
file = open(path, "w")
|
|
file.write(output)
|
|
file.close()
|
|
dlg.Destroy()
|
|
|
|
def showPreferenceDialog(self, event):
|
|
dlg = PreferenceDialog(self)
|
|
dlg.ShowModal()
|
|
dlg.Destroy()
|
|
|
|
def registerMenu(self):
|
|
menuBar = self.GetMenuBar()
|
|
# Quit
|
|
self.Bind(wx.EVT_MENU, self.ExitApp, id=wx.ID_EXIT)
|
|
# Widgets Inspector
|
|
if config.debug:
|
|
self.Bind(wx.EVT_MENU, self.openWXInspectTool, id = self.widgetInspectMenuID)
|
|
# About
|
|
self.Bind(wx.EVT_MENU, self.ShowAboutBox, id=wx.ID_ABOUT)
|
|
# Char editor
|
|
self.Bind(wx.EVT_MENU, self.showCharacterEditor, id=menuBar.characterEditorId)
|
|
# Damage pattern editor
|
|
self.Bind(wx.EVT_MENU, self.showDamagePatternEditor, id=menuBar.damagePatternEditorId)
|
|
# Import dialog
|
|
self.Bind(wx.EVT_MENU, self.showImportDialog, id=wx.ID_OPEN)
|
|
# Export dialog
|
|
self.Bind(wx.EVT_MENU, self.showExportDialog, id=wx.ID_SAVEAS)
|
|
# Import from Clipboard
|
|
self.Bind(wx.EVT_MENU, self.importFromClipboard, id=wx.ID_PASTE)
|
|
# Backup fits
|
|
self.Bind(wx.EVT_MENU, self.backupToXml, id=menuBar.backupFitsId)
|
|
# Export skills needed
|
|
self.Bind(wx.EVT_MENU, self.exportSkillsNeeded, id=menuBar.exportSkillsNeededId)
|
|
# Preference dialog
|
|
self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id = menuBar.preferencesId)
|
|
|
|
#Clipboard exports
|
|
self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY)
|
|
|
|
#Graphs
|
|
self.Bind(wx.EVT_MENU, self.openGraphFrame, id=menuBar.graphFrameId)
|
|
|
|
toggleShipMarketId = wx.NewId()
|
|
ctabnext = wx.NewId()
|
|
ctabprev = wx.NewId()
|
|
|
|
self.additionstab1 = wx.NewId()
|
|
self.additionstab2 = wx.NewId()
|
|
self.additionstab3 = wx.NewId()
|
|
self.additionstab4 = wx.NewId()
|
|
|
|
# Close Page
|
|
self.Bind(wx.EVT_MENU, self.CloseCurrentPage, id=self.closePageId)
|
|
self.Bind(wx.EVT_MENU, self.HAddPage, id = self.addPageId)
|
|
self.Bind(wx.EVT_MENU, self.toggleShipMarket, id = toggleShipMarketId)
|
|
self.Bind(wx.EVT_MENU, self.CTabNext, id = ctabnext)
|
|
self.Bind(wx.EVT_MENU, self.CTabPrev, id = ctabprev)
|
|
|
|
self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id = self.additionstab1)
|
|
self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id = self.additionstab2)
|
|
self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id = self.additionstab3)
|
|
self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id = self.additionstab4)
|
|
|
|
actb = [(wx.ACCEL_CTRL, ord('T'), self.addPageId),
|
|
(wx.ACCEL_CMD, ord('T'), self.addPageId),
|
|
|
|
(wx.ACCEL_CTRL, ord("W"), self.closePageId),
|
|
(wx.ACCEL_CTRL, wx.WXK_F4, self.closePageId),
|
|
(wx.ACCEL_CMD, ord("W"), self.closePageId),
|
|
|
|
(wx.ACCEL_CTRL, ord(" "), toggleShipMarketId),
|
|
(wx.ACCEL_CMD, ord(" "), toggleShipMarketId),
|
|
|
|
(wx.ACCEL_CTRL, wx.WXK_TAB, ctabnext),
|
|
(wx.ACCEL_CTRL | wx.ACCEL_SHIFT, wx.WXK_TAB, ctabprev),
|
|
(wx.ACCEL_CMD, wx.WXK_TAB, ctabnext),
|
|
(wx.ACCEL_CMD | wx.ACCEL_SHIFT, wx.WXK_TAB, ctabprev),
|
|
|
|
(wx.ACCEL_CTRL, ord('1'), self.additionstab1),
|
|
(wx.ACCEL_CTRL, ord('2'), self.additionstab2),
|
|
(wx.ACCEL_CTRL, ord('3'), self.additionstab3),
|
|
(wx.ACCEL_CTRL, ord('4'), self.additionstab4),
|
|
(wx.ACCEL_CMD, ord('1'), self.additionstab1),
|
|
(wx.ACCEL_CMD, ord('2'), self.additionstab2),
|
|
(wx.ACCEL_CMD, ord('3'), self.additionstab3),
|
|
(wx.ACCEL_CMD, ord('4'), self.additionstab4)
|
|
]
|
|
atable = wx.AcceleratorTable(actb)
|
|
self.SetAcceleratorTable(atable)
|
|
|
|
def AdditionsTabSelect(self, event):
|
|
selTab = None
|
|
if event.GetId() == self.additionstab1:
|
|
selTab = 0
|
|
if event.GetId() == self.additionstab2:
|
|
selTab = 1
|
|
if event.GetId() == self.additionstab3:
|
|
selTab = 2
|
|
if event.GetId() == self.additionstab4:
|
|
selTab = 3
|
|
|
|
if selTab is not None:
|
|
self.additionsPane.notebook.SetSelection(selTab)
|
|
|
|
def CTabNext(self, event):
|
|
self.fitMultiSwitch.NextPage()
|
|
|
|
def CTabPrev(self, event):
|
|
self.fitMultiSwitch.PrevPage()
|
|
|
|
def HAddPage(self,event):
|
|
self.fitMultiSwitch.AddPage()
|
|
|
|
def toggleShipMarket(self, event):
|
|
sel = self.notebookBrowsers.GetSelection()
|
|
self.notebookBrowsers.SetSelection(0 if sel == 1 else 1)
|
|
|
|
def clipboardEft(self):
|
|
sFit = service.Fit.getInstance()
|
|
toClipboard(sFit.exportFit(self.getActiveFit()))
|
|
|
|
def clipboardEftImps(self):
|
|
sFit = service.Fit.getInstance()
|
|
toClipboard(sFit.exportEftImps(self.getActiveFit()))
|
|
|
|
def clipboardDna(self):
|
|
sFit = service.Fit.getInstance()
|
|
toClipboard(sFit.exportDna(self.getActiveFit()))
|
|
|
|
def clipboardXml(self):
|
|
sFit = service.Fit.getInstance()
|
|
toClipboard(sFit.exportXml(self.getActiveFit()))
|
|
|
|
def importFromClipboard(self, event):
|
|
sFit = service.Fit.getInstance()
|
|
try:
|
|
fits = sFit.importFitFromBuffer(fromClipboard())
|
|
IDs = sFit.saveImportedFits(fits)
|
|
self._openAfterImport(len(fits), IDs)
|
|
except:
|
|
pass
|
|
|
|
def exportToClipboard(self, event):
|
|
CopySelectDict = {CopySelectDialog.copyFormatEft: self.clipboardEft,
|
|
CopySelectDialog.copyFormatEftImps: self.clipboardEftImps,
|
|
CopySelectDialog.copyFormatXml: self.clipboardXml,
|
|
CopySelectDialog.copyFormatDna: self.clipboardDna}
|
|
dlg = CopySelectDialog(self)
|
|
dlg.ShowModal()
|
|
selected = dlg.GetSelected()
|
|
try:
|
|
CopySelectDict[selected]()
|
|
except:
|
|
pass
|
|
dlg.Destroy()
|
|
|
|
def backupToXml(self, event):
|
|
sFit = service.Fit.getInstance()
|
|
saveDialog = wx.FileDialog(
|
|
self,
|
|
"Save Backup As...",
|
|
wildcard = "EvE XML fitting file (*.xml)|*.xml",
|
|
style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
|
|
if (saveDialog.ShowModal() == wx.ID_OK):
|
|
filePath = saveDialog.GetPath()
|
|
if '.' not in os.path.basename(filePath):
|
|
filePath += ".xml"
|
|
self.waitDialog = animUtils.WaitDialog(self)
|
|
sFit.backupFits(filePath, self.closeWaitDialog)
|
|
self.waitDialog.ShowModal()
|
|
|
|
saveDialog.Destroy()
|
|
|
|
def exportSkillsNeeded(self, event):
|
|
sCharacter = service.Character.getInstance()
|
|
saveDialog = wx.FileDialog(
|
|
self,
|
|
"Export Skills Needed As...",
|
|
wildcard = "EVEMon skills training file (*.emp)|*.emp|" \
|
|
"EVEMon skills training XML file (*.xml)|*.xml|" \
|
|
"Text skills training file (*.txt)|*.txt",
|
|
style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
|
|
if (saveDialog.ShowModal() == wx.ID_OK):
|
|
saveFmtInt = saveDialog.GetFilterIndex()
|
|
saveFmt = ""
|
|
if saveFmtInt == 0: # Per ordering of wildcards above
|
|
saveFmt = "emp"
|
|
elif saveFmtInt == 1:
|
|
saveFmt = "xml"
|
|
else:
|
|
saveFmt = "txt"
|
|
filePath = saveDialog.GetPath()
|
|
self.waitDialog = animUtils.WaitDialog(self)
|
|
sCharacter.backupSkills(filePath, saveFmt, self.getActiveFit(), self.closeWaitDialog)
|
|
self.waitDialog.ShowModal()
|
|
|
|
saveDialog.Destroy()
|
|
|
|
def closeWaitDialog(self):
|
|
self.waitDialog.Destroy()
|
|
|
|
def openGraphFrame(self, event):
|
|
if not self.graphFrame:
|
|
self.graphFrame = GraphFrame(self)
|
|
if gui.graphFrame.enabled:
|
|
self.graphFrame.Show()
|
|
else:
|
|
self.graphFrame.SetFocus()
|
|
|
|
def openWXInspectTool(self,event):
|
|
from wx.lib.inspection import InspectionTool
|
|
if not InspectionTool().initialized:
|
|
InspectionTool().Init()
|
|
|
|
# Find a widget to be selected in the tree. Use either the
|
|
# one under the cursor, if any, or this frame.
|
|
wnd = wx.FindWindowAtPointer()
|
|
if not wnd:
|
|
wnd = self
|
|
InspectionTool().Show(wnd, True)
|
|
|