示例#1
0
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):
        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
        }
        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):
        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()

        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(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)
示例#2
0
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
        wx.Frame.__init__(self, 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.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.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)

        self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin)

    @property
    def command(self) -> wx.CommandProcessor:
        return Fit.getCommandProcessor(self.getActiveFit())

    def ShowUpdateBox(self, release, version):
        dlg = UpdateDialog(self, release, version)
        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):
        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 showDevTools(self, event):
        DevTools(self)

    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 RuntimeError:
            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 = "%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:
            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"
            else:
                pyfalog.warning("oops, invalid fit format %d" % format_)
                try:
                    dlg.Destroy()
                except RuntimeError:
                    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 RuntimeError:
            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')

    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.showDevTools, id=menuBar.devToolsId)
        # 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)

        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.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 = EveFittings(self)
        dlg.Show()

    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):
        dlg = SsoCharacterMgmt(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 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))

    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 it's mutated item - make sure there's at least base item specified
            if importType == "MutatedItem":
                # we've imported an Abyssal module, need to fire off the command to add it to the fit
                self.command.Submit(cmd.GuiImportLocalMutatedModuleCommand(activeFit, *importData[0]))
                return  # no need to do anything else
        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()
        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 RuntimeError:
                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.supress_left_up = True
            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)
示例#3
0
class MainFrame(wx.Frame):
    __instance = None

    @classmethod
    def getInstance(cls):
        return cls.__instance if cls.__instance is not None else MainFrame()

    def __init__(self):
        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, self.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", "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 = gui.chromeTabs.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,
                                      "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.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.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)

        if not 'wxMac' 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 = 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, basic=True)
            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 = 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"]
        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):
        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
        v = sys.version_info
        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\t" +
            '{}.{}.{}'.format(v.major, v.minor, v.micro) + "\nwxPython: \t" +
            wx.__version__ + "\nSQLAlchemy: \t" + sqlalchemy.__version__, 500,
            wx.ClientDC(self))
        if "__WXGTK__" in wx.PlatformInfo:
            forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&amp;t=466425"
        else:
            forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425"
        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):
        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", encoding="utf-8")
            file.write(output)
            file.close()
        dlg.Destroy()

    def showPreferenceDialog(self, event):
        dlg = PreferenceDialog(self)
        dlg.ShowModal()

    def goWiki(self, event):
        webbrowser.open('https://github.com/pyfa-org/Pyfa/wiki')

    def goForums(self, event):
        webbrowser.open(
            'https://forums.eveonline.com/default.aspx?g=posts&t=466425')

    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)
        # 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)

        #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 eveFittings(self, event):
        dlg = CrestFittings(self)
        dlg.Show()

    def updateTitle(self, event):
        sCrest = service.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 = service.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 = service.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 = not ModifiedAttributeDict.OVERRIDES
        wx.PostEvent(self, GE.FitChanged(fitID=self.getActiveFit()))
        menu = self.GetMenuBar()
        menu.SetLabel(
            menu.toggleOverridesId, "Turn Overrides Off"
            if ModifiedAttributeDict.OVERRIDES else "Turn Overrides On")

    def saveChar(self, event):
        sChr = service.Character.getInstance()
        charID = self.charSelection.getActiveCharacter()
        sChr.saveCharacter(charID)
        wx.PostEvent(self, GE.CharListUpdated())

    def saveCharAs(self, event):
        charID = self.charSelection.getActiveCharacter()
        dlg = SaveCharacterAs(self, charID)
        dlg.ShowModal()

    def revertChar(self, event):
        sChr = service.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())

        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 clipboardCrest(self):
        sFit = service.Fit.getInstance()
        toClipboard(sFit.exportCrest(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,
            CopySelectDialog.copyFormatCrest: self.clipboardCrest
        }
        dlg = CopySelectDialog(self)
        dlg.ShowModal()
        selected = dlg.GetSelected()

        CopySelectDict[selected]()

        dlg.Destroy()

    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 = 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"""
        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 width 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 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 exportHtml(self, event):
        from gui.utils.exportHtml import exportHtml
        sFit = service.Fit.getInstance()
        settings = service.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 fileImportCallback(self, action, data=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.

        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)
        """

        if action is None:
            self.progressDialog.Pulse()
        elif action == 1 and data != self.progressDialog.message:
            self.progressDialog.message = data
            self.progressDialog.Pulse(data)
        else:
            self.closeProgressDialog()
            if action == -1:
                self._openAfterImport(data)
            elif action == -2:
                dlg = wx.MessageDialog(
                    self,
                    "The following error was generated\n\n%s\n\nBe aware that already processed fits were not saved"
                    % data, "Import Error", wx.OK | wx.ICON_ERROR)
                if dlg.ShowModal() == wx.ID_OK:
                    return

    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 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 = service.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 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)
示例#4
0
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))
        if "__WXGTK__" in wx.PlatformInfo:
            forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&amp;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 showDamagePatternEditor(self, event):
        dlg = DmgPatternEditorDlg(self)
        dlg.ShowModal()
        dlg.Destroy()

    def showImportDialog(self, event):
        fits = []
        sFit = service.Fit.getInstance()
        dlg=wx.FileDialog(
            self,
            "Open One Or More Fitting Files",
            wildcard = "EFT text fitting files (*.cfg)|*.cfg|" \
                       "EVE XML fitting files (*.xml)|*.xml|" \
                       "All Files (*)|*",
            style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE)
        if (dlg.ShowModal() == wx.ID_OK):
            self.waitDialog = animUtils.WaitDialog(self, title="Importing")
            sFit.importFitsThreaded(dlg.GetPaths(), self.importCallback)
            dlg.Destroy()
            self.waitDialog.ShowModal()

    def importCallback(self, fits):
        self.waitDialog.Destroy()
        sFit = service.Fit.getInstance()
        IDs = sFit.saveImportedFits(fits)
        self._openAfterImport(len(fits), IDs)

    def _openAfterImport(self, importCount, fitIDs):
        if importCount == 1:
            if self.getActiveFit() != fitIDs[0]:
                wx.PostEvent(self, FitSelected(fitID=fitIDs[0]))

        self.shipBrowser.RefreshContent()

    def showExportDialog(self, event):
        dlg = wx.FileDialog(self,
                            "Save Fitting As...",
                            wildcard="EVE XML fitting files (*.xml)|*.xml",
                            style=wx.FD_SAVE)
        if (dlg.ShowModal() == wx.ID_OK):
            sFit = service.Fit.getInstance()
            format = dlg.GetFilterIndex()
            output = ""
            path = dlg.GetPath()
            if (format == 0):
                output = sFit.exportXml(self.getActiveFit())
                if '.' not in os.path.basename(path):
                    path += ".xml"
            else:
                print "oops, invalid fit format %d" % format
                dlg.Destroy()
                return
            file = open(path, "w")
            file.write(output)
            file.close()
        dlg.Destroy()

    def showPreferenceDialog(self, event):
        dlg = PreferenceDialog(self)
        dlg.ShowModal()
        dlg.Destroy()

    def registerMenu(self):
        menuBar = self.GetMenuBar()
        # Quit
        self.Bind(wx.EVT_MENU, self.ExitApp, id=wx.ID_EXIT)
        # Widgets Inspector
        if config.debug:
            self.Bind(wx.EVT_MENU,
                      self.openWXInspectTool,
                      id=self.widgetInspectMenuID)
        # About
        self.Bind(wx.EVT_MENU, self.ShowAboutBox, id=wx.ID_ABOUT)
        # Char editor
        self.Bind(wx.EVT_MENU,
                  self.showCharacterEditor,
                  id=menuBar.characterEditorId)
        # Damage pattern editor
        self.Bind(wx.EVT_MENU,
                  self.showDamagePatternEditor,
                  id=menuBar.damagePatternEditorId)
        # Import dialog
        self.Bind(wx.EVT_MENU, self.showImportDialog, id=wx.ID_OPEN)
        # Export dialog
        self.Bind(wx.EVT_MENU, self.showExportDialog, id=wx.ID_SAVEAS)
        # Import from Clipboard
        self.Bind(wx.EVT_MENU, self.importFromClipboard, id=wx.ID_PASTE)
        # Backup fits
        self.Bind(wx.EVT_MENU, self.backupToXml, id=menuBar.backupFitsId)
        # Export skills needed
        self.Bind(wx.EVT_MENU,
                  self.exportSkillsNeeded,
                  id=menuBar.exportSkillsNeededId)
        # Import character
        self.Bind(wx.EVT_MENU,
                  self.importCharacter,
                  id=menuBar.importCharacterId)
        # Preference dialog
        self.Bind(wx.EVT_MENU,
                  self.showPreferenceDialog,
                  id=menuBar.preferencesId)

        #Clipboard exports
        self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY)

        #Graphs
        self.Bind(wx.EVT_MENU, self.openGraphFrame, id=menuBar.graphFrameId)

        toggleShipMarketId = wx.NewId()
        ctabnext = wx.NewId()
        ctabprev = wx.NewId()

        self.additionstab1 = wx.NewId()
        self.additionstab2 = wx.NewId()
        self.additionstab3 = wx.NewId()
        self.additionstab4 = wx.NewId()

        # Close Page
        self.Bind(wx.EVT_MENU, self.CloseCurrentPage, id=self.closePageId)
        self.Bind(wx.EVT_MENU, self.HAddPage, id=self.addPageId)
        self.Bind(wx.EVT_MENU, self.toggleShipMarket, id=toggleShipMarketId)
        self.Bind(wx.EVT_MENU, self.CTabNext, id=ctabnext)
        self.Bind(wx.EVT_MENU, self.CTabPrev, id=ctabprev)

        self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionstab1)
        self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionstab2)
        self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionstab3)
        self.Bind(wx.EVT_MENU, self.AdditionsTabSelect, id=self.additionstab4)

        actb = [(wx.ACCEL_CTRL, ord('T'), self.addPageId),
                (wx.ACCEL_CMD, ord('T'), self.addPageId),
                (wx.ACCEL_CTRL, ord("W"), self.closePageId),
                (wx.ACCEL_CTRL, wx.WXK_F4, self.closePageId),
                (wx.ACCEL_CMD, ord("W"), self.closePageId),
                (wx.ACCEL_CTRL, ord(" "), toggleShipMarketId),
                (wx.ACCEL_CMD, ord(" "), toggleShipMarketId),
                (wx.ACCEL_CTRL, wx.WXK_TAB, ctabnext),
                (wx.ACCEL_CTRL | wx.ACCEL_SHIFT, wx.WXK_TAB, ctabprev),
                (wx.ACCEL_CMD, wx.WXK_TAB, ctabnext),
                (wx.ACCEL_CMD | wx.ACCEL_SHIFT, wx.WXK_TAB, ctabprev),
                (wx.ACCEL_CTRL, ord('1'), self.additionstab1),
                (wx.ACCEL_CTRL, ord('2'), self.additionstab2),
                (wx.ACCEL_CTRL, ord('3'), self.additionstab3),
                (wx.ACCEL_CTRL, ord('4'), self.additionstab4),
                (wx.ACCEL_CMD, ord('1'), self.additionstab1),
                (wx.ACCEL_CMD, ord('2'), self.additionstab2),
                (wx.ACCEL_CMD, ord('3'), self.additionstab3),
                (wx.ACCEL_CMD, ord('4'), self.additionstab4)]
        atable = wx.AcceleratorTable(actb)
        self.SetAcceleratorTable(atable)

    def AdditionsTabSelect(self, event):
        selTab = None
        if event.GetId() == self.additionstab1:
            selTab = 0
        if event.GetId() == self.additionstab2:
            selTab = 1
        if event.GetId() == self.additionstab3:
            selTab = 2
        if event.GetId() == self.additionstab4:
            selTab = 3

        if selTab is not None:
            self.additionsPane.notebook.SetSelection(selTab)

    def CTabNext(self, event):
        self.fitMultiSwitch.NextPage()

    def CTabPrev(self, event):
        self.fitMultiSwitch.PrevPage()

    def HAddPage(self, event):
        self.fitMultiSwitch.AddPage()

    def toggleShipMarket(self, event):
        sel = self.notebookBrowsers.GetSelection()
        self.notebookBrowsers.SetSelection(0 if sel == 1 else 1)

    def clipboardEft(self):
        sFit = service.Fit.getInstance()
        toClipboard(sFit.exportFit(self.getActiveFit()))

    def clipboardEftImps(self):
        sFit = service.Fit.getInstance()
        toClipboard(sFit.exportEftImps(self.getActiveFit()))

    def clipboardDna(self):
        sFit = service.Fit.getInstance()
        toClipboard(sFit.exportDna(self.getActiveFit()))

    def clipboardXml(self):
        sFit = service.Fit.getInstance()
        toClipboard(sFit.exportXml(self.getActiveFit()))

    def importFromClipboard(self, event):
        sFit = service.Fit.getInstance()
        try:
            fits = sFit.importFitFromBuffer(fromClipboard())
            IDs = sFit.saveImportedFits(fits)
            self._openAfterImport(len(fits), IDs)
        except:
            pass

    def exportToClipboard(self, event):
        CopySelectDict = {
            CopySelectDialog.copyFormatEft: self.clipboardEft,
            CopySelectDialog.copyFormatEftImps: self.clipboardEftImps,
            CopySelectDialog.copyFormatXml: self.clipboardXml,
            CopySelectDialog.copyFormatDna: self.clipboardDna
        }
        dlg = CopySelectDialog(self)
        dlg.ShowModal()
        selected = dlg.GetSelected()
        try:
            CopySelectDict[selected]()
        except:
            pass
        dlg.Destroy()

    def backupToXml(self, event):
        sFit = service.Fit.getInstance()
        saveDialog = wx.FileDialog(
            self,
            "Save Backup As...",
            wildcard="EVE XML fitting file (*.xml)|*.xml",
            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
        if (saveDialog.ShowModal() == wx.ID_OK):
            filePath = saveDialog.GetPath()
            if '.' not in os.path.basename(filePath):
                filePath += ".xml"
            self.waitDialog = animUtils.WaitDialog(self)
            sFit.backupFits(filePath, self.closeWaitDialog)
            self.waitDialog.ShowModal()

        saveDialog.Destroy()

    def exportSkillsNeeded(self, event):
        sCharacter = service.Character.getInstance()
        saveDialog = wx.FileDialog(
            self,
            "Export Skills Needed As...",
            wildcard = "EVEMon skills training file (*.emp)|*.emp|" \
                       "EVEMon skills training XML file (*.xml)|*.xml|" \
                       "Text skills training file (*.txt)|*.txt",
            style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
        if (saveDialog.ShowModal() == wx.ID_OK):
            saveFmtInt = saveDialog.GetFilterIndex()
            saveFmt = ""
            if saveFmtInt == 0:  # Per ordering of wildcards above
                saveFmt = "emp"
            elif saveFmtInt == 1:
                saveFmt = "xml"
            else:
                saveFmt = "txt"
            filePath = saveDialog.GetPath()
            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 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)
示例#5
0
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

        if 'wxMSW' in wx.PlatformInfo:
            self.SetBackgroundColour(
                wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE))

        i = wx.IconFromBitmap(bitmapLoader.getBitmap("pyfa", "icons"))
        self.SetIcon(i)

        self.SetMinSize((1000, 700))
        self.SetSize((1000, 700))

        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)
        self.fitMultiSwitch.AddTab()
        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 = wx.Notebook(self.splitter, wx.ID_ANY)
        self.notebookBrowsers.Bind(wx.EVT_LEFT_DOWN, self.mouseHit)

        self.marketBrowser = MarketBrowser(self.notebookBrowsers)
        self.notebookBrowsers.AddPage(self.marketBrowser, "Market")

        self.shipBrowser = ShipBrowser(self.notebookBrowsers)
        self.notebookBrowsers.AddPage(self.shipBrowser, "Ships")
        self.notebookBrowsers.SetSelection(1)

        self.splitter.SplitVertically(self.notebookBrowsers,
                                      self.FitviewAdditionsPanel)
        self.splitter.SetMinimumPaneSize(200)
        self.splitter.SetSashPosition(300)

        cstatsSizer = wx.BoxSizer(wx.VERTICAL)

        self.charSelection = CharacterSelection(self)
        cstatsSizer.Add(self.charSelection, 0,
                        wx.EXPAND | wx.TOP | wx.RIGHT | wx.LEFT, 3)

        self.statsPane = StatsPane(self)
        cstatsSizer.Add(self.statsPane, 0, wx.EXPAND)

        mainSizer.Add(cstatsSizer, 0, wx.EXPAND)

        self.SetSizer(mainSizer)

        self.addTabId = wx.NewId()
        self.closeTabId = wx.NewId()

        self.graphFrame = None

        #Add menu
        self.SetMenuBar(MainMenuBar())
        #self.SetToolBar(MainToolBar(self))

        self.registerMenu()

        #Show ourselves
        self.Show()

    def getActiveFit(self):
        sel = self.fitMultiSwitch.GetSelection()
        view = self.fitMultiSwitch.GetPage(sel).view
        return view.activeFitID

    def getFittingView(self):
        sel = self.fitMultiSwitch.GetSelection()
        return self.fitMultiSwitch.GetPage(sel).view

    def mouseHit(self, event):
        tab, _ = self.notebookBrowsers.HitTest(event.Position)
        if tab != -1:
            self.notebookBrowsers.SetSelection(tab)

    def CloseCurrentFit(self, evt):
        self.fitMultiSwitch.removeCurrentTab()

    def ExitApp(self, evt):
        try:
            service.SettingsProvider.getInstance().saveAll()
            self.Close()
        except PyDeadObjectError:
            pass

    def ShowAboutBox(self, evt):
        info = wx.AboutDialogInfo()
        info.Name = "pyfa"
        info.Version = aboutData.versionString
        info.Description = wordwrap(
            aboutData.description + "\n\n\nDevelopers: " +
            ", ".join(aboutData.developers) + "\n\nAdditional credits:\n" +
            "\n".join(aboutData.credits) + "\n\nLicense: " +
            aboutData.license + " - see included " + aboutData.licenseLocation,
            550, 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):
            try:
                for importPath in dlg.GetPaths():
                    fits += sFit.importFit(importPath)
                IDs = sFit.saveImportedFits(fits)
                self._openAfterImport(len(fits), IDs)
            except:
                wx.MessageBox("Error importing from file.", "Error",
                              wx.OK | wx.ICON_ERROR, self)
        dlg.Destroy()

    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=
            "EFT text fitting files (*.cfg)|*.cfg|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.exportFit(self.getActiveFit())
                if '.' not in os.path.basename(path):
                    path += ".cfg"
            elif (format == 1):
                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
        self.Bind(wx.EVT_MENU, self.openWXInspectTool, id=911)
        # 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=wx.ID_PREFERENCES)

        #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()
        # Close Tab
        self.Bind(wx.EVT_MENU, self.CloseCurrentFit, id=self.closeTabId)
        self.Bind(wx.EVT_MENU, self.HAddTab, id=self.addTabId)
        self.Bind(wx.EVT_MENU, self.toggleShipMarket, id=toggleShipMarketId)

        actb = [(wx.ACCEL_CTRL, ord('T'), self.addTabId),
                (wx.ACCEL_CMD, ord('T'), self.addTabId),
                (wx.ACCEL_CTRL, ord("W"), self.closeTabId),
                (wx.ACCEL_CMD, ord("W"), self.closeTabId),
                (wx.ACCEL_CTRL, ord(" "), toggleShipMarketId),
                (wx.ACCEL_CMD, ord(" "), toggleShipMarketId)]
        atable = wx.AcceleratorTable(actb)
        self.SetAcceleratorTable(atable)

    def HAddTab(self, event):
        self.fitMultiSwitch.AddTab()

    def toggleShipMarket(self, event):
        sel = self.notebookBrowsers.GetSelection()
        self.notebookBrowsers.SetSelection(0 if sel == 1 else 1)

    def clipboardEft(self):
        sFit = service.Fit.getInstance()
        self.toClipboard(sFit.exportFit(self.getActiveFit()))

    def clipboardDna(self):
        sFit = service.Fit.getInstance()
        self.toClipboard(sFit.exportDna(self.getActiveFit()))

    def clipboardXml(self):
        sFit = service.Fit.getInstance()
        self.toClipboard(sFit.exportXml(self.getActiveFit()))

    def importFromClipboard(self, event):
        sFit = service.Fit.getInstance()
        try:
            fits = sFit.importFitFromBuffer(self.fromClipboard())
            IDs = sFit.saveImportedFits(fits)
            self._openAfterImport(len(fits), IDs)
        except:
            pass

    def exportToClipboard(self, event):
        CopySelectDict = {
            CopySelectDialog.copyFormatEft: self.clipboardEft,
            CopySelectDialog.copyFormatXml: self.clipboardXml,
            CopySelectDialog.copyFormatDna: self.clipboardDna
        }
        dlg = CopySelectDialog(self)
        dlg.ShowModal()
        selected = dlg.GetSelected()
        try:
            CopySelectDict[selected]()
        except:
            pass
        dlg.Destroy()

    def toClipboard(self, text):
        clip = wx.TheClipboard
        clip.Open()
        data = wx.TextDataObject(text)
        clip.SetData(data)
        clip.Close()

    def fromClipboard(self):
        clip = wx.TheClipboard
        clip.Open()
        data = wx.TextDataObject("")
        if clip.GetData(data):
            clip.Close()
            return data.GetText()
        else:
            clip.Close()
            return None

    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 = WaitDialog(self)
            sFit.backupFits(filePath, self.closeWaitDialog)
            self.waitDialog.ShowModal()

        saveDialog.Destroy()

    def closeWaitDialog(self):
        if self.waitDialog.timer.IsRunning():
            self.waitDialog.timer.Stop()
        self.waitDialog.Destroy()

    def openGraphFrame(self, event):
        if not self.graphFrame:
            self.graphFrame = GraphFrame(self)
            self.graphFrame.Show()
        else:
            self.graphFrame.SetFocus()

    def toggleShipBrowser(self, event):
        self.GetToolBar().toggleShipBrowser(event)

    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)