class MainFrame(wx.Frame, IPortUser): __instance = None @classmethod def getInstance(cls): return cls.__instance if cls.__instance is not None else MainFrame() def __init__(self, title="pyfa"): pyfalog.debug("Initialize MainFrame") self.title = title wx.Frame.__init__(self, None, wx.ID_ANY, self.title) MainFrame.__instance = self # Load stored settings (width/height/maximized..) self.LoadMainFrameAttribs() self.disableOverrideEditor = disableOverrideEditor # 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", "gui")) self.SetIcon(i) # Create the layout and windows mainSizer = wx.BoxSizer(wx.HORIZONTAL) self.browser_fitting_split = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) self.fitting_additions_split = wx.SplitterWindow( self.browser_fitting_split, style=wx.SP_LIVE_UPDATE) mainSizer.Add(self.browser_fitting_split, 1, wx.EXPAND | wx.LEFT, 2) self.fitMultiSwitch = MultiSwitch(self.fitting_additions_split) self.additionsPane = AdditionsPane(self.fitting_additions_split) self.notebookBrowsers = PFNotebook(self.browser_fitting_split, False) marketImg = BitmapLoader.getImage("market_small", "gui") shipBrowserImg = BitmapLoader.getImage("ship_small", "gui") self.marketBrowser = MarketBrowser(self.notebookBrowsers) self.notebookBrowsers.AddPage(self.marketBrowser, "Market", tabImage=marketImg, showClose=False) self.marketBrowser.splitter.SetSashPosition(self.marketHeight) self.shipBrowser = ShipBrowser(self.notebookBrowsers) self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage=shipBrowserImg, showClose=False) self.notebookBrowsers.SetSelection(1) self.browser_fitting_split.SplitVertically( self.notebookBrowsers, self.fitting_additions_split) self.browser_fitting_split.SetMinimumPaneSize(204) self.browser_fitting_split.SetSashPosition(self.browserWidth) self.fitting_additions_split.SplitHorizontally(self.fitMultiSwitch, self.additionsPane, -200) self.fitting_additions_split.SetMinimumPaneSize(200) self.fitting_additions_split.SetSashPosition(self.fittingHeight) self.fitting_additions_split.SetSashGravity(1.0) 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)) 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() self.LoadPreviousOpenFits() # Check for updates self.sUpdate = Update.getInstance() self.sUpdate.CheckUpdate(self.ShowUpdateBox) if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin) self.Bind(GE.EVT_SSO_LOGOUT, self.onSSOLogout) self.titleTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.updateTitle, self.titleTimer) def ShowUpdateBox(self, release): dlg = UpdateDialog(self, release) dlg.ShowModal() def LoadPreviousOpenFits(self): sFit = Fit.getInstance() self.prevOpenFits = SettingsProvider.getInstance().getSettings( "pyfaPrevOpenFits", { "enabled": False, "pyfaOpenFits": [] }) fits = self.prevOpenFits['pyfaOpenFits'] # Remove any fits that cause exception when fetching (non-existent fits) for id in fits[:]: try: fit = sFit.getFit(id, basic=True) if fit is None: fits.remove(id) except: fits.remove(id) if not self.prevOpenFits['enabled'] or len(fits) is 0: # add blank page if there are no fits to be loaded self.fitMultiSwitch.AddPage() return self.waitDialog = wx.BusyInfo("Loading previous fits...") OpenFitsThread(fits, self.closeWaitDialog) def LoadMainFrameAttribs(self): mainFrameDefaultAttribs = { "wnd_width": 1000, "wnd_height": 700, "wnd_maximized": False, "browser_width": 300, "market_height": 0, "fitting_height": -200 } self.mainFrameAttribs = 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"])) self.browserWidth = self.mainFrameAttribs["browser_width"] self.marketHeight = self.mainFrameAttribs["market_height"] self.fittingHeight = self.mainFrameAttribs["fitting_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() self.mainFrameAttribs["browser_width"] = self.notebookBrowsers.GetSize( )[0] self.mainFrameAttribs[ "market_height"] = self.marketBrowser.marketView.GetSize()[1] self.mainFrameAttribs[ "fitting_height"] = self.fitting_additions_split.GetSashPosition() 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): 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() # save open fits self.prevOpenFits['pyfaOpenFits'] = [] # clear old list for page in self.fitMultiSwitch.pages: m = getattr(page, "getActiveFit", None) if m is not None: self.prevOpenFits['pyfaOpenFits'].append(m()) # save all teh settingz SettingsProvider.getInstance().saveAll() event.Skip() def ExitApp(self, event): self.Close() event.Skip() def ShowAboutBox(self, evt): v = sys.version_info info = wx.AboutDialogInfo() info.Name = "pyfa" info.Version = gui.aboutData.versionString try: import matplotlib matplotlib_version = matplotlib.__version__ except: matplotlib_version = None info.Description = wordwrap( gui.aboutData.description + "\n\nDevelopers:\n\t" + "\n\t".join(gui.aboutData.developers) + "\n\nAdditional credits:\n\t" + "\n\t".join(gui.aboutData.credits) + "\n\nLicenses:\n\t" + "\n\t".join(gui.aboutData.licenses) + "\n\nEVE Data: \t" + gamedata_version + "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + "\nwxPython: \t" + wx.__version__ + "\nSQLAlchemy: \t" + sqlalchemy.__version__ + "\nmatplotlib: \t {}".format( matplotlib_version if matplotlib_version else "Not Installed"), 500, wx.ClientDC(self)) forumUrl = "https://forums.eveonline.com/t/27156" info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") wx.AboutBox(info) def showCharacterEditor(self, event): dlg = CharacterEditor(self) dlg.Show() def showAttrEditor(self, event): dlg = AttributeEditor(self) dlg.Show() def showTargetResistsEditor(self, event): ResistsEditorDlg(self) def showDamagePatternEditor(self, event): dlg = DmgPatternEditorDlg(self) dlg.ShowModal() try: dlg.Destroy() except PyDeadObjectError: pyfalog.error( "Tried to destroy an object that doesn't exist in <showDamagePatternEditor>." ) def showImplantSetEditor(self, event): ImplantSetEditorDlg(self) def showExportDialog(self, event): """ Export active fit """ sFit = Fit.getInstance() fit = sFit.getFit(self.getActiveFit()) defaultFile = u"%s - %s.xml" % (fit.ship.item.name, fit.name) if fit else None dlg = wx.FileDialog(self, "Save Fitting As...", wildcard="EVE XML fitting files (*.xml)|*.xml", style=wx.FD_SAVE, defaultFile=defaultFile) if dlg.ShowModal() == wx.ID_OK: format_ = dlg.GetFilterIndex() path = dlg.GetPath() if format_ == 0: output = Port.exportXml(None, fit) if '.' not in os.path.basename(path): path += ".xml" else: print("oops, invalid fit format %d" % format_) try: dlg.Destroy() except PyDeadObjectError: pyfalog.error( "Tried to destroy an object that doesn't exist in <showExportDialog>." ) return with open(path, "w", encoding="utf-8") as openfile: openfile.write(output) openfile.close() try: dlg.Destroy() except PyDeadObjectError: pyfalog.error( "Tried to destroy an object that doesn't exist in <showExportDialog>." ) def showPreferenceDialog(self, event): dlg = PreferenceDialog(self) dlg.ShowModal() @staticmethod def goWiki(event): webbrowser.open('https://github.com/pyfa-org/Pyfa/wiki') @staticmethod def goForums(event): webbrowser.open('https://forums.eveonline.com/t/27156') @staticmethod def loadDatabaseDefaults(event): # Import values that must exist otherwise Pyfa breaks DefaultDatabaseValues.importRequiredDefaults() # Import default values for damage profiles DefaultDatabaseValues.importDamageProfileDefaults() # Import default values for target resist profiles DefaultDatabaseValues.importResistProfileDefaults() def registerMenu(self): menuBar = self.GetMenuBar() # Quit self.Bind(wx.EVT_MENU, self.ExitApp, id=wx.ID_EXIT) # Load Default Database values self.Bind(wx.EVT_MENU, self.loadDatabaseDefaults, id=menuBar.importDatabaseDefaultsId) # 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) # 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 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) # Import character self.Bind(wx.EVT_MENU, self.importCharacter, id=menuBar.importCharacterId) # Export HTML self.Bind(wx.EVT_MENU, self.exportHtml, id=menuBar.exportHtmlId) # Preference dialog self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id=wx.ID_PREFERENCES) # User guide self.Bind(wx.EVT_MENU, self.goWiki, id=menuBar.wikiId) # EVE Forums self.Bind(wx.EVT_MENU, self.goForums, id=menuBar.forumId) # Save current character self.Bind(wx.EVT_MENU, self.saveChar, id=menuBar.saveCharId) # Save current character as another character self.Bind(wx.EVT_MENU, self.saveCharAs, id=menuBar.saveCharAsId) # Save current character self.Bind(wx.EVT_MENU, self.revertChar, id=menuBar.revertCharId) # Browse fittings self.Bind(wx.EVT_MENU, self.eveFittings, id=menuBar.eveFittingsId) # Export to EVE self.Bind(wx.EVT_MENU, self.exportToEve, id=menuBar.exportToEveId) # Handle SSO event (login/logout/manage characters, depending on mode and current state) self.Bind(wx.EVT_MENU, self.ssoHandler, id=menuBar.ssoLoginId) # Open attribute editor self.Bind(wx.EVT_MENU, self.showAttrEditor, id=menuBar.attrEditorId) # Toggle Overrides self.Bind(wx.EVT_MENU, self.toggleOverrides, id=menuBar.toggleOverridesId) # Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) # Fitting Restrictions self.Bind(wx.EVT_MENU, self.toggleIgnoreRestriction, id=menuBar.toggleIgnoreRestrictionID) # Graphs self.Bind(wx.EVT_MENU, self.openGraphFrame, id=menuBar.graphFrameId) toggleSearchBoxId = wx.NewId() toggleShipMarketId = wx.NewId() ctabnext = wx.NewId() ctabprev = 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.toggleSearchBox, id=toggleSearchBoxId) 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) actb = [ (wx.ACCEL_CTRL, ord('T'), self.addPageId), (wx.ACCEL_CMD, ord('T'), self.addPageId), (wx.ACCEL_CTRL, ord('F'), toggleSearchBoxId), (wx.ACCEL_CMD, ord('F'), toggleSearchBoxId), (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), # Ctrl+(Shift+)Tab (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), # Ctrl+Page(Up/Down) (wx.ACCEL_CTRL, wx.WXK_PAGEDOWN, ctabnext), (wx.ACCEL_CTRL, wx.WXK_PAGEUP, ctabprev), (wx.ACCEL_CMD, wx.WXK_PAGEDOWN, ctabnext), (wx.ACCEL_CMD, wx.WXK_PAGEUP, ctabprev) ] # Ctrl/Cmd+# for addition pane selection self.additionsSelect = [] for i in range(0, self.additionsPane.notebook.GetPageCount()): self.additionsSelect.append(wx.NewId()) self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionsSelect[i]) actb.append((wx.ACCEL_CMD, i + 49, self.additionsSelect[i])) actb.append((wx.ACCEL_CTRL, i + 49, self.additionsSelect[i])) # Alt+1-9 for market item selection self.itemSelect = [] for i in range(0, 9): self.itemSelect.append(wx.NewId()) self.Bind(wx.EVT_MENU, self.ItemSelect, id=self.itemSelect[i]) actb.append((wx.ACCEL_ALT, i + 49, self.itemSelect[i])) atable = wx.AcceleratorTable(actb) self.SetAcceleratorTable(atable) def toggleIgnoreRestriction(self, event): sFit = Fit.getInstance() fitID = self.getActiveFit() fit = sFit.getFit(fitID) if not fit.ignoreRestrictions: dlg = wx.MessageDialog( self, "Are you sure you wish to ignore fitting restrictions for the " "current fit? This could lead to wildly inaccurate results and possible errors.", "Confirm", wx.YES_NO | wx.ICON_QUESTION) else: dlg = wx.MessageDialog( self, "Re-enabling fitting restrictions for this fit will also remove any illegal items " "from the fit. Do you want to continue?", "Confirm", wx.YES_NO | wx.ICON_QUESTION) result = dlg.ShowModal() == wx.ID_YES dlg.Destroy() if result: sFit.toggleRestrictionIgnore(fitID) wx.PostEvent(self, GE.FitChanged(fitID=fitID)) def eveFittings(self, event): dlg = CrestFittings(self) dlg.Show() def updateTitle(self, event): sCrest = Crest.getInstance() char = sCrest.implicitCharacter if char: t = time.gmtime(char.eve.expires - time.time()) sTime = time.strftime("%H:%M:%S", t if t >= 0 else 0) newTitle = "%s | %s - %s" % (self.title, char.name, sTime) self.SetTitle(newTitle) def onSSOLogin(self, event): menu = self.GetMenuBar() menu.Enable(menu.eveFittingsId, True) menu.Enable(menu.exportToEveId, True) if event.type == CrestModes.IMPLICIT: menu.SetLabel(menu.ssoLoginId, "Logout Character") self.titleTimer.Start(1000) def onSSOLogout(self, event): self.titleTimer.Stop() self.SetTitle(self.title) menu = self.GetMenuBar() if event.type == CrestModes.IMPLICIT or event.numChars == 0: menu.Enable(menu.eveFittingsId, False) menu.Enable(menu.exportToEveId, False) if event.type == CrestModes.IMPLICIT: menu.SetLabel(menu.ssoLoginId, "Login to EVE") def updateCrestMenus(self, type): # in case we are logged in when switching, change title back self.titleTimer.Stop() self.SetTitle(self.title) menu = self.GetMenuBar() sCrest = Crest.getInstance() if type == CrestModes.IMPLICIT: menu.SetLabel(menu.ssoLoginId, "Login to EVE") menu.Enable(menu.eveFittingsId, False) menu.Enable(menu.exportToEveId, False) else: menu.SetLabel(menu.ssoLoginId, "Manage Characters") enable = len(sCrest.getCrestCharacters()) == 0 menu.Enable(menu.eveFittingsId, not enable) menu.Enable(menu.exportToEveId, not enable) def ssoHandler(self, event): sCrest = Crest.getInstance() if sCrest.settings.get('mode') == CrestModes.IMPLICIT: if sCrest.implicitCharacter is not None: sCrest.logout() else: uri = sCrest.startServer() webbrowser.open(uri) else: dlg = CrestMgmt(self) dlg.Show() def exportToEve(self, event): dlg = ExportToEve(self) dlg.Show() def toggleOverrides(self, event): ModifiedAttributeDict.overrides_enabled = not ModifiedAttributeDict.overrides_enabled wx.PostEvent(self, GE.FitChanged(fitID=self.getActiveFit())) menu = self.GetMenuBar() menu.SetLabel( menu.toggleOverridesId, "Turn Overrides Off" if ModifiedAttributeDict.overrides_enabled else "Turn Overrides On") def saveChar(self, event): sChr = Character.getInstance() charID = self.charSelection.getActiveCharacter() sChr.saveCharacter(charID) wx.PostEvent(self, GE.CharListUpdated()) def saveCharAs(self, event): charID = self.charSelection.getActiveCharacter() CharacterEditor.SaveCharacterAs(self, charID) wx.PostEvent(self, GE.CharListUpdated()) def revertChar(self, event): sChr = Character.getInstance() charID = self.charSelection.getActiveCharacter() sChr.revertCharacter(charID) wx.PostEvent(self, GE.CharListUpdated()) def AdditionsTabSelect(self, event): selTab = self.additionsSelect.index(event.GetId()) if selTab <= self.additionsPane.notebook.GetPageCount(): self.additionsPane.notebook.SetSelection(selTab) def ItemSelect(self, event): selItem = self.itemSelect.index(event.GetId()) activeListing = getattr(self.marketBrowser.itemView, 'active', None) if activeListing and selItem < len(activeListing): wx.PostEvent( self, ItemSelected( itemID=self.marketBrowser.itemView.active[selItem].ID)) 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 toggleSearchBox(self, event): sel = self.notebookBrowsers.GetSelection() if sel == 1: self.shipBrowser.navpanel.ToggleSearchBox() else: self.marketBrowser.search.Focus() def clipboardEft(self): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportEft(fit)) def clipboardEftImps(self): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportEftImps(fit)) def clipboardDna(self): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportDna(fit)) def clipboardCrest(self): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportCrest(fit)) def clipboardXml(self): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportXml(None, fit)) def clipboardMultiBuy(self): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportMultiBuy(fit)) def importFromClipboard(self, event): clipboard = fromClipboard() try: fits = Port().importFitFromBuffer(clipboard, self.getActiveFit()) except: pyfalog.error("Attempt to import failed:\n{0}", clipboard) else: self._openAfterImport(fits) def exportToClipboard(self, event): CopySelectDict = { CopySelectDialog.copyFormatEft: self.clipboardEft, CopySelectDialog.copyFormatEftImps: self.clipboardEftImps, CopySelectDialog.copyFormatXml: self.clipboardXml, CopySelectDialog.copyFormatDna: self.clipboardDna, CopySelectDialog.copyFormatCrest: self.clipboardCrest, CopySelectDialog.copyFormatMultiBuy: self.clipboardMultiBuy } dlg = CopySelectDialog(self) dlg.ShowModal() selected = dlg.GetSelected() CopySelectDict[selected]() try: dlg.Destroy() except PyDeadObjectError: pyfalog.error( "Tried to destroy an object that doesn't exist in <exportToClipboard>." ) def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ sCharacter = 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() if saveFmtInt == 0: # Per ordering of wildcards above saveFmt = "emp" elif saveFmtInt == 1: saveFmt = "xml" else: saveFmt = "txt" filePath = saveDialog.GetPath() if '.' not in os.path.basename(filePath): filePath += ".{0}".format(saveFmt) self.waitDialog = wx.BusyInfo("Exporting skills needed...") sCharacter.backupSkills(filePath, saveFmt, self.getActiveFit(), self.closeWaitDialog) saveDialog.Destroy() def fileImportDialog(self, event): """Handles importing single/multiple EVE XML / EFT cfg fit files""" dlg = wx.FileDialog(self, "Open One Or More Fitting Files", wildcard=("EVE XML fitting files (*.xml)|*.xml|" "EFT text fitting files (*.cfg)|*.cfg|" "All Files (*)|*"), style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) if dlg.ShowModal() == wx.ID_OK: self.progressDialog = wx.ProgressDialog( "Importing fits", " " * 100, # set some arbitrary spacing to create width in window parent=self, style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL) # self.progressDialog.message = None Port.importFitsThreaded(dlg.GetPaths(), self) self.progressDialog.ShowModal() try: dlg.Destroy() except PyDeadObjectError: pyfalog.error( "Tried to destroy an object that doesn't exist in <fileImportDialog>." ) def backupToXml(self, event): """ Back up all fits to EVE XML file """ defaultFile = "pyfa-fits-%s.xml" % strftime("%Y%m%d_%H%M%S", gmtime()) saveDialog = wx.FileDialog( self, "Save Backup As...", wildcard="EVE XML fitting file (*.xml)|*.xml", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, defaultFile=defaultFile, ) if saveDialog.ShowModal() == wx.ID_OK: filePath = saveDialog.GetPath() if '.' not in os.path.basename(filePath): filePath += ".xml" sFit = Fit.getInstance() max_ = sFit.countAllFits() self.progressDialog = wx.ProgressDialog( "Backup fits", "Backing up %d fits to: %s" % (max_, filePath), maximum=max_, parent=self, style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL) Port.backupFits(filePath, self) self.progressDialog.ShowModal() def exportHtml(self, event): from gui.utils.exportHtml import exportHtml sFit = Fit.getInstance() settings = HTMLExportSettings.getInstance() max_ = sFit.countAllFits() path = settings.getPath() if not os.path.isdir(os.path.dirname(path)): dlg = wx.MessageDialog( self, "Invalid Path\n\nThe following path is invalid or does not exist: \n%s\n\nPlease verify path location pyfa's preferences." % path, "Error", wx.OK | wx.ICON_ERROR) if dlg.ShowModal() == wx.ID_OK: return self.progressDialog = wx.ProgressDialog( "Backup fits", "Generating HTML file at: %s" % path, maximum=max_, parent=self, style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback) self.progressDialog.ShowModal() def backupCallback(self, info): if info == -1: self.closeProgressDialog() else: self.progressDialog.Update(info) def on_port_process_start(self): # flag for progress dialog. self.__progress_flag = True def on_port_processing(self, action, data=None): # 2017/03/29 NOTE: implementation like interface wx.CallAfter(self._on_port_processing, action, data) return self.__progress_flag def _on_port_processing(self, action, data): """ While importing fits from file, the logic calls back to this function to update progress bar to show activity. XML files can contain multiple ships with multiple fits, whereas EFT cfg files contain many fits of a single ship. When iterating through the files, we update the message when we start a new file, and then Pulse the progress bar with every fit that is processed. action : a flag that lets us know how to deal with :data None: Pulse the progress bar 1: Replace message with data other: Close dialog and handle based on :action (-1 open fits, -2 display error) """ _message = None if action & IPortUser.ID_ERROR: self.closeProgressDialog() _message = "Import Error" if action & IPortUser.PROCESS_IMPORT else "Export Error" dlg = wx.MessageDialog( self, "The following error was generated\n\n%s\n\nBe aware that already processed fits were not saved" % data, _message, wx.OK | wx.ICON_ERROR) # if dlg.ShowModal() == wx.ID_OK: # return dlg.ShowModal() return # data is str if action & IPortUser.PROCESS_IMPORT: if action & IPortUser.ID_PULSE: _message = () # update message elif action & IPortUser.ID_UPDATE: # and data != self.progressDialog.message: _message = data if _message is not None: self.__progress_flag, _unuse = self.progressDialog.Pulse( _message) else: self.closeProgressDialog() if action & IPortUser.ID_DONE: self._openAfterImport(data) # data is tuple(int, str) elif action & IPortUser.PROCESS_EXPORT: if action & IPortUser.ID_DONE: self.closeProgressDialog() else: self.__progress_flag, _unuse = self.progressDialog.Update( data[0], data[1]) def _openAfterImport(self, fits): if len(fits) > 0: if len(fits) == 1: fit = fits[0] wx.PostEvent(self, FitSelected(fitID=fit.ID, from_import=True)) wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True)) else: fits.sort(key=lambda _fit: (_fit.ship.item.name, _fit.name)) results = [] for fit in fits: results.append((fit.ID, fit.name, fit.modifiedCoalesce, fit.ship.item, fit.notes)) wx.PostEvent(self.shipBrowser, ImportSelected(fits=results, back=True)) def closeProgressDialog(self): # Windows apparently handles ProgressDialogs differently. We can # simply Destroy it here, but for other platforms we must Close it if 'wxMSW' in wx.PlatformInfo: self.progressDialog.Destroy() else: self.progressDialog.EndModal(wx.ID_OK) self.progressDialog.Close() def importCharacter(self, event): """ Imports character XML file from EVE API """ dlg = wx.FileDialog( self, "Open One Or More Character Files", wildcard= "EVE API XML character 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 = wx.BusyInfo("Importing Character...") sCharacter = Character.getInstance() sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback) def importCharacterCallback(self): self.closeWaitDialog() wx.PostEvent(self, GE.CharListUpdated()) def closeWaitDialog(self): del self.waitDialog def openGraphFrame(self, event): if not self.graphFrame: self.graphFrame = GraphFrame(self) if graphFrame.graphFrame_enabled: self.graphFrame.Show() elif graphFrame.graphFrame_enabled: self.graphFrame.SetFocus() def openWXInspectTool(self, event): 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)
class MainFrame(wx.Frame): __instance = None @classmethod def getInstance(cls): return cls.__instance if cls.__instance is not None else MainFrame() def __init__(self, title="pyfa"): pyfalog.debug("Initialize MainFrame") self.title = title super().__init__(None, wx.ID_ANY, self.title) self.supress_left_up = False MainFrame.__instance = self # Load stored settings (width/height/maximized..) self.LoadMainFrameAttribs() self.disableOverrideEditor = disableOverrideEditor # 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.Icon(BitmapLoader.getBitmap("pyfa", "gui")) self.SetIcon(i) # Create the layout and windows mainSizer = wx.BoxSizer(wx.HORIZONTAL) self.browser_fitting_split = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) self.fitting_additions_split = wx.SplitterWindow( self.browser_fitting_split, style=wx.SP_LIVE_UPDATE) mainSizer.Add(self.browser_fitting_split, 1, wx.EXPAND | wx.LEFT, 2) self.fitMultiSwitch = MultiSwitch(self.fitting_additions_split) self.additionsPane = AdditionsPane(self.fitting_additions_split, self) self.notebookBrowsers = ChromeNotebook(self.browser_fitting_split, False) marketImg = BitmapLoader.getImage("market_small", "gui") shipBrowserImg = BitmapLoader.getImage("ship_small", "gui") self.marketBrowser = MarketBrowser(self.notebookBrowsers) self.notebookBrowsers.AddPage(self.marketBrowser, "Market", image=marketImg, closeable=False) self.marketBrowser.splitter.SetSashPosition(self.marketHeight) self.shipBrowser = ShipBrowser(self.notebookBrowsers) self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", image=shipBrowserImg, closeable=False) self.notebookBrowsers.SetSelection(1) self.browser_fitting_split.SplitVertically( self.notebookBrowsers, self.fitting_additions_split) self.browser_fitting_split.SetMinimumPaneSize(204) self.browser_fitting_split.SetSashPosition(self.browserWidth) self.fitting_additions_split.SplitHorizontally(self.fitMultiSwitch, self.additionsPane, -200) self.fitting_additions_split.SetMinimumPaneSize(200) self.fitting_additions_split.SetSashPosition(self.fittingHeight) self.fitting_additions_split.SetSashGravity(1.0) cstatsSizer = wx.BoxSizer(wx.VERTICAL) self.charSelection = CharacterSelection(self) cstatsSizer.Add(self.charSelection, 0, wx.EXPAND) # @todo pheonix: fix all stats stuff 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.closeAllPagesId = wx.NewId() self.hiddenGraphsId = wx.NewId() self.widgetInspectMenuID = wx.NewId() self.SetMenuBar(MainMenuBar(self)) self.registerMenu() # Internal vars to keep track of other windows self.statsWnds = [] self.activeStatsWnd = None self.Bind(wx.EVT_CLOSE, self.OnClose) # Show ourselves self.Show() self.LoadPreviousOpenFits() # Check for updates self.sUpdate = Update.getInstance() self.sUpdate.CheckUpdate(self.ShowUpdateBox) self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin) @property def command(self) -> wx.CommandProcessor: return Fit.getCommandProcessor(self.getActiveFit()) def getCommandForFit(self, fitID) -> wx.CommandProcessor: return Fit.getCommandProcessor(fitID) def ShowUpdateBox(self, release, version): with UpdateDialog(self, release, version) as dlg: dlg.ShowModal() def LoadPreviousOpenFits(self): sFit = Fit.getInstance() self.prevOpenFits = SettingsProvider.getInstance().getSettings( "pyfaPrevOpenFits", { "enabled": False, "pyfaOpenFits": [] }) fits = self.prevOpenFits['pyfaOpenFits'] # Remove any fits that cause exception when fetching (non-existent fits) for id in fits[:]: try: fit = sFit.getFit(id, basic=True) if fit is None: fits.remove(id) except (KeyboardInterrupt, SystemExit): raise except: fits.remove(id) if not self.prevOpenFits['enabled'] or len(fits) == 0: # add blank page if there are no fits to be loaded self.fitMultiSwitch.AddPage() return self.waitDialog = wx.BusyInfo("Loading previous fits...", parent=self) OpenFitsThread(fits, self.closeWaitDialog) def _getDisplayData(self): displayData = [] for i in range(wx.Display.GetCount()): display = wx.Display(i) displayData.append(display.GetClientArea()) return displayData def LoadMainFrameAttribs(self): mainFrameDefaultAttribs = { "wnd_display": 0, "wnd_x": 0, "wnd_y": 0, "wnd_width": 1000, "wnd_height": 700, "wnd_maximized": False, "browser_width": 300, "market_height": 0, "fitting_height": -200 } self.mainFrameAttribs = SettingsProvider.getInstance().getSettings( "pyfaMainWindowAttribs", mainFrameDefaultAttribs) wndDisplay = self.mainFrameAttribs["wnd_display"] displayData = self._getDisplayData() try: selectedDisplayData = displayData[wndDisplay] except IndexError: selectedDisplayData = displayData[0] dspX, dspY, dspW, dspH = selectedDisplayData if self.mainFrameAttribs["wnd_maximized"]: wndW = mainFrameDefaultAttribs["wnd_width"] wndH = mainFrameDefaultAttribs["wnd_height"] wndX = min(mainFrameDefaultAttribs["wnd_x"], dspW * 0.75) wndY = min(mainFrameDefaultAttribs["wnd_y"], dspH * 0.75) self.Maximize() else: wndW = self.mainFrameAttribs["wnd_width"] wndH = self.mainFrameAttribs["wnd_height"] wndX = min(self.mainFrameAttribs["wnd_x"], dspW * 0.75) wndY = min(self.mainFrameAttribs["wnd_y"], dspH * 0.75) self.SetPosition((dspX + wndX, dspY + wndY)) self.SetSize((wndW, wndH)) self.SetMinSize((mainFrameDefaultAttribs["wnd_width"], mainFrameDefaultAttribs["wnd_height"])) self.browserWidth = self.mainFrameAttribs["browser_width"] self.marketHeight = self.mainFrameAttribs["market_height"] self.fittingHeight = self.mainFrameAttribs["fitting_height"] def UpdateMainFrameAttribs(self): if self.IsIconized(): return wndGlobalX, wndGlobalY = self.GetPosition() displayData = self._getDisplayData() wndDisplay = 0 wndX = 0 wndY = 0 for i, (sdX, sdY, sdW, sdH) in enumerate(displayData): wndRelX = wndGlobalX - sdX wndRelY = wndGlobalY - sdY if 0 <= wndRelX < sdW and 0 <= wndRelY < sdH: wndDisplay = i wndX = wndRelX wndY = wndRelY break self.mainFrameAttribs["wnd_display"] = wndDisplay self.mainFrameAttribs["wnd_x"] = wndX self.mainFrameAttribs["wnd_y"] = wndY wndW, wndH = self.GetSize() self.mainFrameAttribs["wnd_width"] = wndW self.mainFrameAttribs["wnd_height"] = wndH self.mainFrameAttribs["wnd_maximized"] = self.IsMaximized() self.mainFrameAttribs["browser_width"] = self.notebookBrowsers.GetSize( )[0] self.mainFrameAttribs[ "market_height"] = self.marketBrowser.marketView.GetSize()[1] self.mainFrameAttribs[ "fitting_height"] = self.fitting_additions_split.GetSashPosition() 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): self.fitMultiSwitch.GetSelectedPage() def CloseCurrentPage(self, evt): ms = self.fitMultiSwitch page = ms.GetSelection() if page is not None: ms.DeletePage(page) def CloseAllPages(self, evt): ms = self.fitMultiSwitch for _ in range(ms.GetPageCount()): ms.DeletePage(0) def OnClose(self, event): self.UpdateMainFrameAttribs() # save open fits self.prevOpenFits['pyfaOpenFits'] = [] # clear old list for page in self.fitMultiSwitch._pages: m = getattr(page, "getActiveFit", None) if m is not None: self.prevOpenFits['pyfaOpenFits'].append(m()) # save all teh settingz SettingsProvider.getInstance().saveAll() event.Skip() def ExitApp(self, event): self.Close() event.Skip() def ShowAboutBox(self, evt): info = wx.adv.AboutDialogInfo() info.Name = "pyfa" time = datetime.datetime.fromtimestamp( int(gamedata_date)).strftime('%Y-%m-%d %H:%M:%S') info.Version = config.getVersion( ) + '\nEVE Data Version: {} ({})'.format( gamedata_version, time) # gui.aboutData.versionString # # try: # import matplotlib # matplotlib_version = matplotlib.__version__ # except: # matplotlib_version = None # # info.Description = wordwrap(gui.aboutData.description + "\n\nDevelopers:\n\t" + # "\n\t".join(gui.aboutData.developers) + # "\n\nAdditional credits:\n\t" + # "\n\t".join(gui.aboutData.credits) + # "\n\nLicenses:\n\t" + # "\n\t".join(gui.aboutData.licenses) + # "\n\nEVE Data: \t" + gamedata_version + # "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + # "\nwxPython: \t" + wx.__version__ + # "\nSQLAlchemy: \t" + sqlalchemy.__version__ + # "\nmatplotlib: \t {}".format(matplotlib_version if matplotlib_version else "Not Installed"), # 500, wx.ClientDC(self)) # if "__WXGTK__" in wx.PlatformInfo: # forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" # else: # forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" # info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") wx.adv.AboutBox(info) def OnShowGraphFrame(self, event): GraphFrame.openOne(self) def OnShowGraphFrameHidden(self, event): GraphFrame.openOne(self, includeHidden=True) def OnShowDevTools(self, event): DevTools.openOne(parent=self) def OnShowCharacterEditor(self, event): CharacterEditor.openOne(parent=self) def OnShowAttrEditor(self, event): AttributeEditor.openOne(parent=self) def OnShowTargetProfileEditor(self, event): TargetProfileEditor.openOne(parent=self) def OnShowDamagePatternEditor(self, event): DmgPatternEditor.openOne(parent=self) def OnShowImplantSetEditor(self, event): ImplantSetEditor.openOne(parent=self) def OnShowExportDialog(self, event): """ Export active fit """ sFit = Fit.getInstance() fit = sFit.getFit(self.getActiveFit()) defaultFile = "%s - %s.xml" % (fit.ship.item.name, fit.name) if fit else None with wx.FileDialog(self, "Save Fitting As...", wildcard="EVE XML fitting files (*.xml)|*.xml", style=wx.FD_SAVE, defaultFile=defaultFile) as dlg: if dlg.ShowModal() == wx.ID_OK: self.supress_left_up = True format_ = dlg.GetFilterIndex() path = dlg.GetPath() if format_ == 0: output = Port.exportXml([fit], None) if '.' not in os.path.basename(path): path += ".xml" with open(path, "w", encoding="utf-8") as openfile: openfile.write(output) openfile.close() else: pyfalog.warning("oops, invalid fit format %d" % format_) return def OnShowPreferenceDialog(self, event): with PreferenceDialog(self) as dlg: dlg.ShowModal() @staticmethod def goWiki(event): webbrowser.open('https://github.com/pyfa-org/Pyfa/wiki') @staticmethod def goForums(event): webbrowser.open('https://forums.eveonline.com/t/27156') 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) self.Bind(wx.EVT_MENU, self.OnShowDevTools, id=menuBar.devToolsId) # About self.Bind(wx.EVT_MENU, self.ShowAboutBox, id=wx.ID_ABOUT) # Char editor self.Bind(wx.EVT_MENU, self.OnShowCharacterEditor, id=menuBar.characterEditorId) # Damage pattern editor self.Bind(wx.EVT_MENU, self.OnShowDamagePatternEditor, id=menuBar.damagePatternEditorId) # Target Profile editor self.Bind(wx.EVT_MENU, self.OnShowTargetProfileEditor, id=menuBar.targetProfileEditorId) # Implant Set editor self.Bind(wx.EVT_MENU, self.OnShowImplantSetEditor, id=menuBar.implantSetEditorId) # Import dialog self.Bind(wx.EVT_MENU, self.fileImportDialog, id=wx.ID_OPEN) # Export dialog self.Bind(wx.EVT_MENU, self.OnShowExportDialog, 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) # Import character self.Bind(wx.EVT_MENU, self.importCharacter, id=menuBar.importCharacterId) # Export HTML self.Bind(wx.EVT_MENU, self.exportHtml, id=menuBar.exportHtmlId) # Preference dialog self.Bind(wx.EVT_MENU, self.OnShowPreferenceDialog, id=wx.ID_PREFERENCES) # User guide self.Bind(wx.EVT_MENU, self.goWiki, id=menuBar.wikiId) self.Bind(wx.EVT_MENU, lambda evt: MainFrame.getInstance().command.Undo(), id=wx.ID_UNDO) self.Bind(wx.EVT_MENU, lambda evt: MainFrame.getInstance().command.Redo(), id=wx.ID_REDO) # EVE Forums self.Bind(wx.EVT_MENU, self.goForums, id=menuBar.forumId) # Save current character self.Bind(wx.EVT_MENU, self.saveChar, id=menuBar.saveCharId) # Save current character as another character self.Bind(wx.EVT_MENU, self.saveCharAs, id=menuBar.saveCharAsId) # Save current character self.Bind(wx.EVT_MENU, self.revertChar, id=menuBar.revertCharId) # Optimize fit price self.Bind(wx.EVT_MENU, self.optimizeFitPrice, id=menuBar.optimizeFitPrice) # Browse fittings self.Bind(wx.EVT_MENU, self.eveFittings, id=menuBar.eveFittingsId) # Export to EVE self.Bind(wx.EVT_MENU, self.exportToEve, id=menuBar.exportToEveId) # Handle SSO event (login/logout/manage characters, depending on mode and current state) self.Bind(wx.EVT_MENU, self.ssoHandler, id=menuBar.ssoLoginId) # Open attribute editor self.Bind(wx.EVT_MENU, self.OnShowAttrEditor, id=menuBar.attrEditorId) # Toggle Overrides self.Bind(wx.EVT_MENU, self.toggleOverrides, id=menuBar.toggleOverridesId) # Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) # Fitting Restrictions self.Bind(wx.EVT_MENU, self.toggleIgnoreRestriction, id=menuBar.toggleIgnoreRestrictionID) # Graphs self.Bind(wx.EVT_MENU, self.OnShowGraphFrame, id=menuBar.graphFrameId) self.Bind(wx.EVT_MENU, self.OnShowGraphFrameHidden, id=self.hiddenGraphsId) toggleSearchBoxId = wx.NewId() toggleShipMarketId = wx.NewId() ctabnext = wx.NewId() ctabprev = wx.NewId() # Close Page self.Bind(wx.EVT_MENU, self.CloseCurrentPage, id=self.closePageId) self.Bind(wx.EVT_MENU, self.CloseAllPages, id=self.closeAllPagesId) self.Bind(wx.EVT_MENU, self.HAddPage, id=self.addPageId) self.Bind(wx.EVT_MENU, self.toggleSearchBox, id=toggleSearchBoxId) 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) actb = [ (wx.ACCEL_CTRL, ord('T'), self.addPageId), (wx.ACCEL_CMD, ord('T'), self.addPageId), (wx.ACCEL_CTRL, ord('F'), toggleSearchBoxId), (wx.ACCEL_CMD, ord('F'), toggleSearchBoxId), (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 | wx.ACCEL_ALT, ord("G"), self.hiddenGraphsId), (wx.ACCEL_CMD | wx.ACCEL_ALT, ord("G"), self.hiddenGraphsId), (wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("W"), self.closeAllPagesId), (wx.ACCEL_CTRL | wx.ACCEL_ALT, wx.WXK_F4, self.closeAllPagesId), (wx.ACCEL_CMD | wx.ACCEL_ALT, ord("W"), self.closeAllPagesId), (wx.ACCEL_CTRL, ord(" "), toggleShipMarketId), (wx.ACCEL_CMD, ord(" "), toggleShipMarketId), # Ctrl+(Shift+)Tab (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), # Ctrl+Page(Up/Down) (wx.ACCEL_CTRL, wx.WXK_PAGEDOWN, ctabnext), (wx.ACCEL_CTRL, wx.WXK_PAGEUP, ctabprev), (wx.ACCEL_CMD, wx.WXK_PAGEDOWN, ctabnext), (wx.ACCEL_CMD, wx.WXK_PAGEUP, ctabprev), (wx.ACCEL_CMD | wx.ACCEL_SHIFT, ord("Z"), wx.ID_REDO) ] # Ctrl/Cmd+# for addition pane selection self.additionsSelect = [] for i in range(0, self.additionsPane.notebook.GetPageCount()): self.additionsSelect.append(wx.NewId()) self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionsSelect[i]) actb.append((wx.ACCEL_CMD, i + 49, self.additionsSelect[i])) actb.append((wx.ACCEL_CTRL, i + 49, self.additionsSelect[i])) # Alt+1-9 for market item selection self.itemSelect = [] for i in range(0, 9): self.itemSelect.append(wx.NewId()) self.Bind(wx.EVT_MENU, self.ItemSelect, id=self.itemSelect[i]) actb.append((wx.ACCEL_ALT, i + 49, self.itemSelect[i])) atable = wx.AcceleratorTable(actb) self.SetAcceleratorTable(atable) def toggleIgnoreRestriction(self, event): sFit = Fit.getInstance() fitID = self.getActiveFit() fit = sFit.getFit(fitID) if not fit.ignoreRestrictions: with wx.MessageDialog( self, "Are you sure you wish to ignore fitting restrictions for the " "current fit? This could lead to wildly inaccurate results and possible errors.", "Confirm", wx.YES_NO | wx.ICON_QUESTION) as dlg: result = dlg.ShowModal() == wx.ID_YES else: with wx.MessageDialog( self, "Re-enabling fitting restrictions for this fit will also remove any illegal items " "from the fit. Do you want to continue?", "Confirm", wx.YES_NO | wx.ICON_QUESTION) as dlg: result = dlg.ShowModal() == wx.ID_YES if result: self.command.Submit( cmd.GuiToggleFittingRestrictionsCommand(fitID=fitID)) def eveFittings(self, event): EveFittings.openOne(parent=self) def onSSOLogin(self, event): menu = self.GetMenuBar() menu.Enable(menu.eveFittingsId, True) menu.Enable(menu.exportToEveId, True) def updateEsiMenus(self, type): menu = self.GetMenuBar() sEsi = Esi.getInstance() menu.SetLabel(menu.ssoLoginId, "Manage Characters") enable = len(sEsi.getSsoCharacters()) == 0 menu.Enable(menu.eveFittingsId, not enable) menu.Enable(menu.exportToEveId, not enable) def ssoHandler(self, event): SsoCharacterMgmt.openOne(parent=self) def exportToEve(self, event): ExportToEve.openOne(parent=self) def toggleOverrides(self, event): ModifiedAttributeDict.overrides_enabled = not ModifiedAttributeDict.overrides_enabled changedFitIDs = Fit.getInstance().processOverrideToggle() wx.PostEvent(self, GE.FitChanged(fitIDs=changedFitIDs)) menu = self.GetMenuBar() menu.SetLabel( menu.toggleOverridesId, "&Turn Overrides Off" if ModifiedAttributeDict.overrides_enabled else "&Turn Overrides On") def saveChar(self, event): sChr = Character.getInstance() charID = self.charSelection.getActiveCharacter() sChr.saveCharacter(charID) wx.PostEvent(self, GE.CharListUpdated()) def saveCharAs(self, event): charID = self.charSelection.getActiveCharacter() CharacterEditor.SaveCharacterAs(self, charID) wx.PostEvent(self, GE.CharListUpdated()) def revertChar(self, event): sChr = Character.getInstance() charID = self.charSelection.getActiveCharacter() sChr.revertCharacter(charID) wx.PostEvent(self, GE.CharListUpdated()) def optimizeFitPrice(self, event): fitID = self.getActiveFit() sFit = Fit.getInstance() fit = sFit.getFit(fitID) if fit: def updateFitCb(replacementsCheaper): del self.waitDialog del self.disablerAll rebaseMap = { k.ID: v.ID for k, v in replacementsCheaper.items() } self.command.Submit( cmd.GuiRebaseItemsCommand(fitID=fitID, rebaseMap=rebaseMap)) fitItems = { i for i in Fit.fitItemIter(fit, forceFitImplants=True) if i is not fit.ship.item } self.disablerAll = wx.WindowDisabler() self.waitDialog = wx.BusyInfo("Please Wait...", parent=self) Price.getInstance().findCheaperReplacements(fitItems, updateFitCb, fetchTimeout=10) def AdditionsTabSelect(self, event): selTab = self.additionsSelect.index(event.GetId()) if selTab <= self.additionsPane.notebook.GetPageCount(): self.additionsPane.notebook.SetSelection(selTab) def ItemSelect(self, event): selItem = self.itemSelect.index(event.GetId()) activeListing = getattr(self.marketBrowser.itemView, 'active', None) if activeListing and selItem < len(activeListing): wx.PostEvent( self, ItemSelected( itemID=self.marketBrowser.itemView.active[selItem].ID, allowBatch=False)) 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 toggleSearchBox(self, event): sel = self.notebookBrowsers.GetSelection() if sel == 1: self.shipBrowser.navpanel.ToggleSearchBox() else: self.marketBrowser.search.Focus() def importFromClipboard(self, event): clipboard = fromClipboard() activeFit = self.getActiveFit() try: importType, importData = Port().importFitFromBuffer( clipboard, activeFit) if importType == "FittingItem": baseItem, mutaplasmidItem, mutations = importData[0] if mutaplasmidItem: self.command.Submit( cmd.GuiImportLocalMutatedModuleCommand( activeFit, baseItem, mutaplasmidItem, mutations)) else: self.command.Submit( cmd.GuiAddLocalModuleCommand(activeFit, baseItem.ID)) return if importType == "AdditionsDrones": if self.command.Submit( cmd.GuiImportLocalDronesCommand( activeFit, [(i.ID, a) for i, a in importData[0]])): self.additionsPane.select("Drones") return if importType == "AdditionsFighters": if self.command.Submit( cmd.GuiImportLocalFightersCommand( activeFit, [(i.ID, a) for i, a in importData[0]])): self.additionsPane.select("Fighters") return if importType == "AdditionsImplants": if self.command.Submit( cmd.GuiImportImplantsCommand( activeFit, [(i.ID, a) for i, a in importData[0]])): self.additionsPane.select("Implants") return if importType == "AdditionsBoosters": if self.command.Submit( cmd.GuiImportBoostersCommand( activeFit, [(i.ID, a) for i, a in importData[0]])): self.additionsPane.select("Boosters") return if importType == "AdditionsCargo": if self.command.Submit( cmd.GuiImportCargosCommand( activeFit, [(i.ID, a) for i, a in importData[0]])): self.additionsPane.select("Cargo") return except (KeyboardInterrupt, SystemExit): raise except: pyfalog.error("Attempt to import failed:\n{0}", clipboard) else: self._openAfterImport(importData) def exportToClipboard(self, event): with CopySelectDialog(self) as dlg: dlg.ShowModal() def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ sCharacter = Character.getInstance() with 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, ) as dlg: if dlg.ShowModal() == wx.ID_OK: saveFmtInt = dlg.GetFilterIndex() if saveFmtInt == 0: # Per ordering of wildcards above saveFmt = "emp" elif saveFmtInt == 1: saveFmt = "xml" else: saveFmt = "txt" filePath = dlg.GetPath() if '.' not in os.path.basename(filePath): filePath += ".{0}".format(saveFmt) self.waitDialog = wx.BusyInfo("Exporting skills needed...", parent=self) sCharacter.backupSkills(filePath, saveFmt, self.getActiveFit(), self.closeWaitDialog) def fileImportDialog(self, event): """Handles importing single/multiple EVE XML / EFT cfg fit files""" with wx.FileDialog(self, "Open One Or More Fitting Files", wildcard=("EVE XML fitting files (*.xml)|*.xml|" "EFT text fitting files (*.cfg)|*.cfg|" "All Files (*)|*"), style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) as dlg: if dlg.ShowModal() == wx.ID_OK: self.progressDialog = wx.ProgressDialog( "Importing fits", " " * 100, # set some arbitrary spacing to create width in window parent=self, style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL) Port.importFitsThreaded(dlg.GetPaths(), self) self.progressDialog.ShowModal() def backupToXml(self, event): """ Back up all fits to EVE XML file """ defaultFile = "pyfa-fits-%s.xml" % strftime("%Y%m%d_%H%M%S", gmtime()) with wx.FileDialog( self, "Save Backup As...", wildcard="EVE XML fitting file (*.xml)|*.xml", style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, defaultFile=defaultFile, ) as dlg: if dlg.ShowModal() == wx.ID_OK: filePath = dlg.GetPath() if '.' not in os.path.basename(filePath): filePath += ".xml" sFit = Fit.getInstance() max_ = sFit.countAllFits() self.progressDialog = wx.ProgressDialog( "Backup fits", "Backing up %d fits to: %s" % (max_, filePath), maximum=max_, parent=self, style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL) Port.backupFits(filePath, self) self.progressDialog.ShowModal() def exportHtml(self, event): from gui.utils.exportHtml import exportHtml sFit = Fit.getInstance() settings = HTMLExportSettings.getInstance() max_ = sFit.countAllFits() path = settings.getPath() if not os.path.isdir(os.path.dirname(path)): with wx.MessageDialog( self, "Invalid Path\n\nThe following path is invalid or does not exist: \n%s\n\nPlease verify path location pyfa's preferences." % path, "Error", wx.OK | wx.ICON_ERROR) as dlg: if dlg.ShowModal() == wx.ID_OK: return self.progressDialog = wx.ProgressDialog( "Backup fits", "Generating HTML file at: %s" % path, maximum=max_, parent=self, style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback) self.progressDialog.ShowModal() def backupCallback(self, info): if info == -1: self.closeProgressDialog() else: self.progressDialog.Update(info) def on_port_process_start(self): # flag for progress dialog. self.__progress_flag = True def on_port_processing(self, action, data=None): # 2017/03/29 NOTE: implementation like interface wx.CallAfter(self._on_port_processing, action, data) return self.__progress_flag def _on_port_processing(self, action, data): """ While importing fits from file, the logic calls back to this function to update progress bar to show activity. XML files can contain multiple ships with multiple fits, whereas EFT cfg files contain many fits of a single ship. When iterating through the files, we update the message when we start a new file, and then Pulse the progress bar with every fit that is processed. action : a flag that lets us know how to deal with :data None: Pulse the progress bar 1: Replace message with data other: Close dialog and handle based on :action (-1 open fits, -2 display error) """ _message = None if action & IPortUser.ID_ERROR: self.closeProgressDialog() _message = "Import Error" if action & IPortUser.PROCESS_IMPORT else "Export Error" with wx.MessageDialog( self, "The following error was generated\n\n%s\n\nBe aware that already processed fits were not saved" % data, _message, wx.OK | wx.ICON_ERROR) as dlg: dlg.ShowModal() return # data is str if action & IPortUser.PROCESS_IMPORT: if action & IPortUser.ID_PULSE: _message = () # update message elif action & IPortUser.ID_UPDATE: # and data != self.progressDialog.message: _message = data if _message is not None: self.__progress_flag, _unuse = self.progressDialog.Pulse( _message) else: self.closeProgressDialog() if action & IPortUser.ID_DONE: self._openAfterImport(data) # data is tuple(int, str) elif action & IPortUser.PROCESS_EXPORT: if action & IPortUser.ID_DONE: self.closeProgressDialog() else: self.__progress_flag, _unuse = self.progressDialog.Update( data[0], data[1]) def _openAfterImport(self, fits): if len(fits) > 0: if len(fits) == 1: fit = fits[0] wx.PostEvent(self, FitSelected(fitID=fit.ID, from_import=True)) wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True)) else: fits.sort(key=lambda _fit: (_fit.ship.item.name, _fit.name)) results = [] for fit in fits: results.append((fit.ID, fit.name, fit.modifiedCoalesce, fit.ship.item, fit.notes)) wx.PostEvent(self.shipBrowser, ImportSelected(fits=results, back=True)) def closeProgressDialog(self): # Windows apparently handles ProgressDialogs differently. We can # simply Destroy it here, but for other platforms we must Close it if 'wxMSW' in wx.PlatformInfo: self.progressDialog.Destroy() else: self.progressDialog.EndModal(wx.ID_OK) self.progressDialog.Close() def importCharacter(self, event): """ Imports character XML file from EVE API """ with wx.FileDialog( self, "Open One Or More Character Files", wildcard= "EVE API XML character files (*.xml)|*.xml|All Files (*)|*", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) as dlg: if dlg.ShowModal() == wx.ID_OK: self.supress_left_up = True self.waitDialog = wx.BusyInfo("Importing Character...", parent=self) sCharacter = Character.getInstance() sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback) def importCharacterCallback(self): self.closeWaitDialog() wx.PostEvent(self, GE.CharListUpdated()) def closeWaitDialog(self): del self.waitDialog def openWXInspectTool(self, event): 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)
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://www.evefit.org/Pyfa", "pyfa home page") 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) # 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 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)
class MainFrame(wx.Frame): __instance = None @classmethod def getInstance(cls): return cls.__instance if cls.__instance is not None else MainFrame() def __init__(self): title="pyfa %s%s - Python Fitting Assistant"%(config.version, "" if config.tag.lower() != 'git' else " (git)") wx.Frame.__init__(self, None, wx.ID_ANY, title) 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.marketBrowser.splitter.SetSashPosition(self.marketHeight) 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(self.browserWidth) 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() self.LoadPreviousOpenFits() #Check for updates self.sUpdate = service.Update.getInstance() self.sUpdate.CheckUpdate(self.ShowUpdateBox) def ShowUpdateBox(self, release): dlg = UpdateDialog(self, release) dlg.ShowModal() dlg.Destroy() def LoadPreviousOpenFits(self): sFit = service.Fit.getInstance() self.prevOpenFits = service.SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) fits = self.prevOpenFits['pyfaOpenFits'] # Remove any fits that cause exception when fetching (non-existent fits) for id in fits[:]: try: sFit.getFit(id) except: fits.remove(id) if not self.prevOpenFits['enabled'] or len(fits) is 0: # add blank page if there are no fits to be loaded self.fitMultiSwitch.AddPage() return self.waitDialog = animUtils.WaitDialog(self, title="Opening previous fits") OpenFitsThread(fits, self.closeWaitDialog) self.waitDialog.ShowModal() def LoadMainFrameAttribs(self): mainFrameDefaultAttribs = {"wnd_width": 1000, "wnd_height": 680, "wnd_maximized": False, "browser_width": 300, "market_height": 0} 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"])) self.browserWidth = self.mainFrameAttribs["browser_width"] self.marketHeight = self.mainFrameAttribs["market_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() self.mainFrameAttribs["browser_width"] = self.notebookBrowsers.GetSize()[0] self.mainFrameAttribs["market_height"] = self.marketBrowser.marketView.GetSize()[1] 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() # save open fits self.prevOpenFits['pyfaOpenFits'] = [] # clear old list for page in self.fitMultiSwitch.pages: m = getattr(page, "getActiveFit", None) if m is not None: self.prevOpenFits['pyfaOpenFits'].append(m()) # save all teh settingz service.SettingsProvider.getInstance().saveAll() event.Skip() def ExitApp(self, event): self.Close() event.Skip() def ShowAboutBox(self, evt): import eos.config info = wx.AboutDialogInfo() info.Name = "pyfa" info.Version = gui.aboutData.versionString info.Description = wordwrap(gui.aboutData.description + "\n\nDevelopers:\n\t" + "\n\t".join(gui.aboutData.developers) + "\n\nAdditional credits:\n\t" + "\n\t".join(gui.aboutData.credits) + "\n\nLicenses:\n\t" + "\n\t".join(gui.aboutData.licenses) + "\n\nEVE Data: \t" + eos.config.gamedata_version + "\nPython: \t" + sys.version + "\nwxPython: \t" + wx.__version__ + "\nSQLAlchemy: \t" + sqlalchemy.__version__, 700, wx.ClientDC(self)) if "__WXGTK__" in wx.PlatformInfo: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=247609" else: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=247609" info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") wx.AboutBox(info) def showCharacterEditor(self, event): dlg=CharacterEditor(self) dlg.Show() def showTargetResistsEditor(self, event): dlg=ResistsEditorDlg(self) dlg.ShowModal() dlg.Destroy() def showDamagePatternEditor(self, event): dlg=DmgPatternEditorDlg(self) dlg.ShowModal() dlg.Destroy() def showExportDialog(self, event): """ Export active fit """ sFit = service.Fit.getInstance() fit = sFit.getFit(self.getActiveFit()) defaultFile = "%s - %s.xml"%(fit.ship.item.name, fit.name) if fit else None dlg = wx.FileDialog(self, "Save Fitting As...", wildcard = "EVE XML fitting files (*.xml)|*.xml", style = wx.FD_SAVE, defaultFile=defaultFile) if dlg.ShowModal() == wx.ID_OK: format = dlg.GetFilterIndex() path = dlg.GetPath() if format == 0: output = sFit.exportXml(None, 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 goWiki(self, event): wx.LaunchDefaultBrowser('https://github.com/DarkFenX/Pyfa/wiki') def goForums(self, event): wx.LaunchDefaultBrowser('https://forums.eveonline.com/default.aspx?g=posts&t=247609') 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) # Target Resists editor self.Bind(wx.EVT_MENU, self.showTargetResistsEditor, id=menuBar.targetResistsEditorId) # Import dialog self.Bind(wx.EVT_MENU, self.fileImportDialog, 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) # Import character self.Bind(wx.EVT_MENU, self.importCharacter, id=menuBar.importCharacterId) # Export HTML self.Bind(wx.EVT_MENU, self.exportHtml, id=menuBar.exportHtmlId) # Preference dialog self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id=wx.ID_PREFERENCES) # User guide self.Bind(wx.EVT_MENU, self.goWiki, id = menuBar.wikiId) # EVE Forums self.Bind(wx.EVT_MENU, self.goForums, id = menuBar.forumId) #Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) #Graphs self.Bind(wx.EVT_MENU, self.openGraphFrame, id=menuBar.graphFrameId) toggleSearchBoxId = wx.NewId() toggleShipMarketId = wx.NewId() ctabnext = wx.NewId() ctabprev = 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.toggleSearchBox, id = toggleSearchBoxId) 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) actb = [(wx.ACCEL_CTRL, ord('T'), self.addPageId), (wx.ACCEL_CMD, ord('T'), self.addPageId), (wx.ACCEL_CTRL, ord('F'), toggleSearchBoxId), (wx.ACCEL_CMD, ord('F'), toggleSearchBoxId), (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), # Ctrl+(Shift+)Tab (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), # Ctrl+Page(Up/Down) (wx.ACCEL_CTRL, wx.WXK_PAGEDOWN, ctabnext), (wx.ACCEL_CTRL, wx.WXK_PAGEUP, ctabprev), (wx.ACCEL_CMD, wx.WXK_PAGEDOWN, ctabnext), (wx.ACCEL_CMD, wx.WXK_PAGEUP, ctabprev) ] # Ctrl/Cmd+# for addition pane selection self.additionsSelect = [] for i in range(0, self.additionsPane.notebook.GetPageCount()): self.additionsSelect.append(wx.NewId()) self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionsSelect[i]) actb.append((wx.ACCEL_CMD, i+49, self.additionsSelect[i])) actb.append((wx.ACCEL_CTRL, i+49, self.additionsSelect[i])) # Alt+1-9 for market item selection self.itemSelect = [] for i in range(0, 9): self.itemSelect.append(wx.NewId()) self.Bind(wx.EVT_MENU, self.ItemSelect, id = self.itemSelect[i]) actb.append((wx.ACCEL_ALT, i + 49, self.itemSelect[i])) atable = wx.AcceleratorTable(actb) self.SetAcceleratorTable(atable) def AdditionsTabSelect(self, event): selTab = self.additionsSelect.index(event.GetId()) if selTab <= self.additionsPane.notebook.GetPageCount(): self.additionsPane.notebook.SetSelection(selTab) def ItemSelect(self, event): selItem = self.itemSelect.index(event.GetId()) if selItem < len(self.marketBrowser.itemView.active): wx.PostEvent(self, ItemSelected(itemID=self.marketBrowser.itemView.active[selItem].ID)) 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 toggleSearchBox(self, event): sel = self.notebookBrowsers.GetSelection() if sel == 1: self.shipBrowser.navpanel.ToggleSearchBox() else: self.marketBrowser.search.Focus() 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(None, self.getActiveFit())) def importFromClipboard(self, event): sFit = service.Fit.getInstance() try: fits = sFit.importFitFromBuffer(fromClipboard(), self.getActiveFit()) except: pass else: self._openAfterImport(fits) 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 fileImportDialog(self, event): """Handles importing single/multiple EVE XML / EFT cfg fit files""" sFit = service.Fit.getInstance() dlg = wx.FileDialog(self, "Open One Or More Fitting Files", wildcard = "EVE XML fitting files (*.xml)|*.xml|" \ "EFT text fitting files (*.cfg)|*.cfg|" \ "All Files (*)|*", style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) if (dlg.ShowModal() == wx.ID_OK): self.progressDialog = wx.ProgressDialog( "Importing fits", " "*100, # set some arbitrary spacing to create wifth in window parent=self, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) self.progressDialog.message = None sFit.importFitsThreaded(dlg.GetPaths(), self.fileImportCallback) self.progressDialog.ShowModal() dlg.Destroy() def fileImportCallback(self, info, fits=None): """ While importing fits from file, the logic calls back to this function to update progress bar to show activity. XML files can contain multiple ships with multiple fits, whereas EFT cfg files contain many fits of a single ship. When iterating through the files, we update the message when we start a new file, and then Pulse the progress bar with every fit that is processed. """ if info == -1: # Done processing self.progressDialog.Hide() self._openAfterImport(fits) elif info != self.progressDialog.message and info is not None: # New message, overwrite cached message and update self.progressDialog.message = info self.progressDialog.Pulse(info) else: # Simply Pulse() if we don't have anything else to do self.progressDialog.Pulse() def _openAfterImport(self, fits): if len(fits) > 0: if len(fits) == 1: fit = fits[0] wx.PostEvent(self, FitSelected(fitID=fit.ID)) wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True)) else: wx.PostEvent(self.shipBrowser, ImportSelected(fits=fits, back=True)) def backupToXml(self, event): """ Back up all fits to EVE XML file """ defaultFile = "pyfa-fits-%s.xml"%strftime("%Y%m%d_%H%M%S", gmtime()) saveDialog = wx.FileDialog(self, "Save Backup As...", wildcard = "EVE XML fitting file (*.xml)|*.xml", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, defaultFile=defaultFile) if saveDialog.ShowModal() == wx.ID_OK: filePath = saveDialog.GetPath() if '.' not in os.path.basename(filePath): filePath += ".xml" sFit = service.Fit.getInstance() max = sFit.countAllFits() self.progressDialog = wx.ProgressDialog("Backup fits", "Backing up %d fits to: %s"%(max, filePath), maximum=max, parent=self, style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) sFit.backupFits(filePath, self.backupCallback) self.progressDialog.ShowModal() def backupCallback(self, info): if info == -1: self.progressDialog.Hide() else: self.progressDialog.Update(info) def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ 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() if saveFmtInt == 0: # Per ordering of wildcards above saveFmt = "emp" elif saveFmtInt == 1: saveFmt = "xml" else: saveFmt = "txt" filePath = saveDialog.GetPath() if '.' not in os.path.basename(filePath): filePath += ".{0}".format(saveFmt) self.waitDialog = animUtils.WaitDialog(self) sCharacter.backupSkills(filePath, saveFmt, self.getActiveFit(), self.closeWaitDialog) self.waitDialog.ShowModal() saveDialog.Destroy() def importCharacter(self, event): """ Imports character XML file from EVE API """ dlg = wx.FileDialog(self, "Open One Or More Character Files", wildcard="EVE API XML character 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 Character") sCharacter = service.Character.getInstance() sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback) dlg.Destroy() self.waitDialog.ShowModal() def importCharacterCallback(self): self.waitDialog.Destroy() wx.PostEvent(self, GE.CharListUpdated()) def exportHtml(self, event): from gui.utils.exportHtml import exportHtml sFit = service.Fit.getInstance() settings = service.settings.HTMLExportSettings.getInstance() max = sFit.countAllFits() path = settings.getPath() self.progressDialog = wx.ProgressDialog("Backup fits", "Generating HTML file at: %s"%path, maximum=max, parent=self, style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback) self.progressDialog.ShowModal() 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)
class MainFrame(wx.Frame): __instance = None @classmethod def getInstance(cls): return cls.__instance if cls.__instance is not None else MainFrame() def __init__(self): title = "pyfa %s%s - Python Fitting Assistant" % ( config.version, "" if config.tag.lower() != 'git' else " (git)") wx.Frame.__init__(self, None, wx.ID_ANY, title) 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() self.LoadPreviousOpenFits() #Check for updates self.sUpdate = service.Update.getInstance() self.sUpdate.CheckUpdate(self.ShowUpdateBox) def ShowUpdateBox(self, release): dlg = UpdateDialog(self, release) dlg.ShowModal() dlg.Destroy() def LoadPreviousOpenFits(self): self.prevOpenFits = service.SettingsProvider.getInstance().getSettings( "pyfaPrevOpenFits", { "enabled": False, "pyfaOpenFits": [] }) fits = self.prevOpenFits['pyfaOpenFits'] if not self.prevOpenFits['enabled'] or len(fits) is 0: # add blank page if there are no fits to be loaded self.fitMultiSwitch.AddPage() return self.waitDialog = animUtils.WaitDialog(self, title="Opening previous fits") OpenFitsThread(fits, self.closeWaitDialog) self.waitDialog.ShowModal() def LoadMainFrameAttribs(self): mainFrameDefaultAttribs = { "wnd_width": 1000, "wnd_height": 680, "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() # save open fits self.prevOpenFits['pyfaOpenFits'] = [] # clear old list for page in self.fitMultiSwitch.pages: m = getattr(page, "getActiveFit", None) if m is not None: self.prevOpenFits['pyfaOpenFits'].append(m()) # save all teh settingz 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\nDevelopers:\n\t" + "\n\t".join(gui.aboutData.developers) + "\n\nAdditional credits:\n\t" + "\n\t".join(gui.aboutData.credits) + "\n\nLicenses:\n\t" + "\n\t".join(gui.aboutData.licenses) + "\n\nPython: \t" + sys.version + "\nwxPython: \t" + wx.__version__ + "\nSQLAlchemy: \t" + sqlalchemy.__version__, 700, wx.ClientDC(self)) if "__WXGTK__" in wx.PlatformInfo: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=247609" else: forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=247609" info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") wx.AboutBox(info) def showCharacterEditor(self, event): dlg = CharacterEditor(self) dlg.Show() def showTargetResistsEditor(self, event): dlg = ResistsEditorDlg(self) dlg.ShowModal() dlg.Destroy() 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: 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 goWiki(self, event): wx.LaunchDefaultBrowser('https://github.com/DarkFenX/Pyfa/wiki') def goForums(self, event): wx.LaunchDefaultBrowser( 'https://forums.eveonline.com/default.aspx?g=posts&t=247609') 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) # Target Resists editor self.Bind(wx.EVT_MENU, self.showTargetResistsEditor, id=menuBar.targetResistsEditorId) # 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) # Import character self.Bind(wx.EVT_MENU, self.importCharacter, id=menuBar.importCharacterId) # Export HTML self.Bind(wx.EVT_MENU, self.exportHtml, id=menuBar.exportHtmlId) # Preference dialog self.Bind(wx.EVT_MENU, self.showPreferenceDialog, id=wx.ID_PREFERENCES) # User guide self.Bind(wx.EVT_MENU, self.goWiki, id=menuBar.wikiId) # EVE Forums self.Bind(wx.EVT_MENU, self.goForums, id=menuBar.forumId) #Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) #Graphs self.Bind(wx.EVT_MENU, self.openGraphFrame, id=menuBar.graphFrameId) toggleSearchBoxId = wx.NewId() 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() self.additionstab5 = 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.toggleSearchBox, id=toggleSearchBoxId) 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) self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionstab5) actb = [ (wx.ACCEL_CTRL, ord('T'), self.addPageId), (wx.ACCEL_CMD, ord('T'), self.addPageId), (wx.ACCEL_CTRL, ord('F'), toggleSearchBoxId), (wx.ACCEL_CMD, ord('F'), toggleSearchBoxId), (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), # Ctrl+(Shift+)Tab (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), # Ctrl+age(Up/Down) (wx.ACCEL_CTRL, wx.WXK_PAGEDOWN, ctabnext), (wx.ACCEL_CTRL, wx.WXK_PAGEUP, ctabprev), (wx.ACCEL_CMD, wx.WXK_PAGEDOWN, ctabnext), (wx.ACCEL_CMD, wx.WXK_PAGEUP, 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_CTRL, ord('5'), self.additionstab5), (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), (wx.ACCEL_CMD, ord('5'), self.additionstab5) ] 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 event.GetId() == self.additionstab5: selTab = 4 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 toggleSearchBox(self, event): sel = self.notebookBrowsers.GetSelection() if sel == 1: self.shipBrowser.navpanel.ToggleSearchBox() else: self.marketBrowser.search.Focus() 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(), self.getActiveFit()) 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() if '.' not in os.path.basename(filePath): filePath += ".{0}".format(saveFmt) self.waitDialog = animUtils.WaitDialog(self) sCharacter.backupSkills(filePath, saveFmt, self.getActiveFit(), self.closeWaitDialog) self.waitDialog.ShowModal() saveDialog.Destroy() def importCharacter(self, event): sCharacter = service.Character.getInstance() dlg=wx.FileDialog( self, "Open One Or More Character Files", wildcard = "EVE CCP API XML character 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 Character") sCharacter.importCharacter(dlg.GetPaths(), self.importCharacterCallback) dlg.Destroy() self.waitDialog.ShowModal() def exportHtml(self, event): from gui.utils.exportHtml import exportHtml self.waitDialog = animUtils.WaitDialog(self) exportHtml.getInstance().refreshFittingHtml(True, self.closeWaitDialog) self.waitDialog.ShowModal() def importCharacterCallback(self): self.waitDialog.Destroy() wx.PostEvent(self, GE.CharListUpdated()) 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)