Merge branch 'master' into charImplants

# Conflicts:
#	gui/characterEditor.py
This commit is contained in:
blitzmann
2016-03-12 23:20:26 -05:00
1260 changed files with 3684 additions and 676 deletions

View File

@@ -25,8 +25,16 @@ licenses = (
"Silk Icons Set by famfamfam.com - Creative Commons Attribution 2.5 License",
"Fat Cow Icons by fatcow.com - Creative Commons Attribution 3.0 License"
)
developers = ("blitzmann \t(Sable Blitzmann)", "cncfanatics \t(Sakari Orisi) (founder)" , "DarkPhoenix \t(Kadesh Priestess) (project lead)", "Darriele \t(Darriele)")
credits = ("Entity (Entity) \t\tCapacitor calculations / EVEAPI python lib / Reverence", "Aurora \t\t\tMaths", "Corollax (Aamrr) \tVarious EOS / pyfa improvements")
developers = (
"blitzmann \t(Sable Blitzmann) (maintainer)",
"cncfanatics \t(Sakari Orisi)" ,
"DarkPhoenix \t(Kadesh Priestess)",
"Darriele \t\t(Darriele)")
credits = (
"Entity (Entity) \tCapacitor calculations / EVEAPI python lib / Reverence",
"Aurora \t\tMaths",
"Corollax (Aamrr) \tVarious EOS / pyfa improvements",
"Dreae (Dreae)\tPyCrest")
description = (
"Pyfa (the Python Fitting Assistant) is an open-source standalone application able to "
"create and simulate fittings for EVE-Online SciFi MMORPG with a very high degree of "

View File

@@ -7,6 +7,8 @@ import gui.mainFrame
import service
from service.crest import CrestModes
from wx.lib.intctrl import IntCtrl
class PFCrestPref ( PreferenceView):
title = "CREST"
@@ -46,6 +48,19 @@ class PFCrestPref ( PreferenceView):
mainSizer.Add(rbSizer, 1, wx.ALL|wx.EXPAND, 0)
timeoutSizer = wx.BoxSizer(wx.HORIZONTAL)
self.stTimout = wx.StaticText( panel, wx.ID_ANY, u"Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0 )
self.stTimout.Wrap( -1 )
timeoutSizer.Add( self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5 )
self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout'))
timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5 )
self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange)
mainSizer.Add(timeoutSizer, 0, wx.ALL|wx.EXPAND, 0)
detailsTitle = wx.StaticText( panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0 )
detailsTitle.Wrap( -1 )
detailsTitle.SetFont( wx.Font( 12, 70, 90, 90, False, wx.EmptyString ) )
@@ -53,7 +68,6 @@ class PFCrestPref ( PreferenceView):
mainSizer.Add( detailsTitle, 0, wx.ALL, 5 )
mainSizer.Add( wx.StaticLine( panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ), 0, wx.EXPAND, 5 )
fgAddrSizer = wx.FlexGridSizer( 2, 2, 0, 0 )
fgAddrSizer.AddGrowableCol( 1 )
fgAddrSizer.SetFlexibleDirection( wx.BOTH )
@@ -87,6 +101,9 @@ class PFCrestPref ( PreferenceView):
panel.SetSizer( mainSizer )
panel.Layout()
def OnTimeoutChange(self, event):
self.settings.set('timeout', event.GetEventObject().GetValue())
def OnModeChange(self, event):
self.settings.set('mode', event.GetInt())
self.ToggleProxySettings(self.settings.get('mode'))
@@ -97,8 +114,8 @@ class PFCrestPref ( PreferenceView):
service.Crest.restartService()
def OnBtnApply(self, event):
self.settings.set('clientID', self.inputClientID.GetValue())
self.settings.set('clientSecret', self.inputClientSecret.GetValue())
self.settings.set('clientID', self.inputClientID.GetValue().strip())
self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip())
sCrest = service.Crest.getInstance()
sCrest.delAllCharacters()

View File

@@ -59,6 +59,12 @@ class PFGeneralPref ( PreferenceView):
self.cbMarketShortcuts = wx.CheckBox( panel, wx.ID_ANY, u"Show market shortcuts", wx.DefaultPosition, wx.DefaultSize, 0 )
mainSizer.Add( self.cbMarketShortcuts, 0, wx.ALL|wx.EXPAND, 5 )
self.cbGaugeAnimation = wx.CheckBox( panel, wx.ID_ANY, u"Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0 )
mainSizer.Add( self.cbGaugeAnimation, 0, wx.ALL|wx.EXPAND, 5 )
self.cbExportCharges = wx.CheckBox( panel, wx.ID_ANY, u"Export loaded charges", wx.DefaultPosition, wx.DefaultSize, 0 )
mainSizer.Add( self.cbExportCharges, 0, wx.ALL|wx.EXPAND, 5 )
defCharSizer = wx.BoxSizer( wx.HORIZONTAL )
self.sFit = service.Fit.getInstance()
@@ -73,6 +79,8 @@ class PFGeneralPref ( PreferenceView):
self.cbReopenFits.SetValue(self.openFitsSettings["enabled"])
self.cbShowTooltip.SetValue(self.sFit.serviceFittingOptions["showTooltip"] or False)
self.cbMarketShortcuts.SetValue(self.sFit.serviceFittingOptions["showMarketShortcuts"] or False)
self.cbGaugeAnimation.SetValue(self.sFit.serviceFittingOptions["enableGaugeAnimation"])
self.cbExportCharges.SetValue(self.sFit.serviceFittingOptions["exportCharges"])
self.cbGlobalChar.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalCharStateChange)
self.cbGlobalDmgPattern.Bind(wx.EVT_CHECKBOX, self.OnCBGlobalDmgPatternStateChange)
@@ -84,6 +92,8 @@ class PFGeneralPref ( PreferenceView):
self.cbReopenFits.Bind(wx.EVT_CHECKBOX, self.onCBReopenFits)
self.cbShowTooltip.Bind(wx.EVT_CHECKBOX, self.onCBShowTooltip)
self.cbMarketShortcuts.Bind(wx.EVT_CHECKBOX, self.onCBShowShortcuts)
self.cbGaugeAnimation.Bind(wx.EVT_CHECKBOX, self.onCBGaugeAnimation)
self.cbExportCharges.Bind(wx.EVT_CHECKBOX, self.onCBExportCharges)
self.cbRackLabels.Enable(self.sFit.serviceFittingOptions["rackSlots"] or False)
@@ -143,6 +153,12 @@ class PFGeneralPref ( PreferenceView):
def onCBShowShortcuts(self, event):
self.sFit.serviceFittingOptions["showMarketShortcuts"] = self.cbMarketShortcuts.GetValue()
def onCBGaugeAnimation(self, event):
self.sFit.serviceFittingOptions["enableGaugeAnimation"] = self.cbGaugeAnimation.GetValue()
def onCBExportCharges(self, event):
self.sFit.serviceFittingOptions["exportCharges"] = self.cbExportCharges.GetValue()
def getImage(self):
return BitmapLoader.getBitmap("prefs_settings", "gui")

View File

@@ -96,7 +96,7 @@ class PFUpdatePref (PreferenceView):
self.resetButton.Hide()
def OnDownload(self, event):
wx.LaunchDefaultBrowser('https://github.com/DarkFenX/Pyfa/releases/tag/'+self.UpdateSettings.get('version'))
wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/'+self.UpdateSettings.get('version'))
def getImage(self):
return BitmapLoader.getBitmap("prefs_update", "gui")

View File

@@ -23,7 +23,6 @@ from gui import builtinStatsViews
from gui.bitmapLoader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
import service
import locale
class PriceViewFull(StatsView):
name = "priceViewFull"
@@ -107,15 +106,15 @@ class PriceViewFull(StatsView):
if self._cachedShip != shipPrice:
self.labelPriceShip.SetLabel("%s ISK" % formatAmount(shipPrice, 3, 3, 9, currency=True))
self.labelPriceShip.SetToolTip(wx.ToolTip(locale.format('%.2f', shipPrice, 1)))
self.labelPriceShip.SetToolTip(wx.ToolTip('{:,.2f}'.format(shipPrice)))
self._cachedShip = shipPrice
if self._cachedFittings != modPrice:
self.labelPriceFittings.SetLabel("%s ISK" % formatAmount(modPrice, 3, 3, 9, currency=True))
self.labelPriceFittings.SetToolTip(wx.ToolTip(locale.format('%.2f', modPrice, 1)))
self.labelPriceFittings.SetToolTip(wx.ToolTip('{:,.2f}'.format(modPrice)))
self._cachedFittings = modPrice
if self._cachedTotal != (shipPrice+modPrice):
self.labelPriceTotal.SetLabel("%s ISK" % formatAmount(shipPrice + modPrice, 3, 3, 9, currency=True))
self.labelPriceTotal.SetToolTip(wx.ToolTip(locale.format('%.2f', (shipPrice + modPrice), 1)))
self.labelPriceTotal.SetToolTip(wx.ToolTip('{:,.2f}'.format(shipPrice + modPrice)))
self._cachedTotal = shipPrice + modPrice
self.panel.Layout()

View File

@@ -22,7 +22,6 @@ from gui.statsView import StatsView
from gui import builtinStatsViews
from gui.utils.numberFormatter import formatAmount
import locale
try:
from collections import OrderedDict
except ImportError:
@@ -153,7 +152,7 @@ class TargetingMiscViewFull(StatsView):
("labelFullAlignTime", {"main": lambda: fit.alignTime}, 3, 0, 0, "s"),
("labelFullSigRadius", {"main": lambda: fit.ship.getModifiedItemAttr("signatureRadius")}, 3, 0, 9, ""),
("labelFullWarpSpeed", {"main": lambda: fit.warpSpeed}, 3, 0, 0, "AU/s"),
("labelFullCargo", cargoValues, 3, 0, 9, u"m\u00B3"))
("labelFullCargo", cargoValues, 4, 0, 9, u"m\u00B3"))
counter = 0
RADII = [("Pod",25), ("Interceptor",33), ("Frigate",38),
@@ -201,15 +200,15 @@ class TargetingMiscViewFull(StatsView):
label.SetToolTip(wx.ToolTip("Type: %s" % (fit.scanType)))
elif labelName == "labelFullAlignTime":
alignTime = "Align:\t%.3fs"%mainValue
mass = "Mass:\t%skg"%locale.format('%d', fit.ship.getModifiedItemAttr("mass"), 1)
mass = 'Mass:\t{:,.0f}kg'.format(fit.ship.getModifiedItemAttr("mass"))
agility = "Agility:\t%.3fx"%fit.ship.getModifiedItemAttr("agility")
label.SetToolTip(wx.ToolTip("%s\n%s\n%s" % (alignTime, mass, agility)))
elif labelName == "labelFullCargo":
tipLines = []
tipLines.append(u"Cargohold: %.1fm\u00B3 / %sm\u00B3"% (fit.cargoBayUsed, newValues["main"]))
tipLines.append(u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"]))
for attrName, tipAlias in cargoNamesOrder.items():
if newValues[attrName] > 0:
tipLines.append(u"%s: %sm\u00B3"% (tipAlias, newValues[attrName]))
tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName]))
label.SetToolTip(wx.ToolTip(u"\n".join(tipLines)))
else:
label.SetToolTip(wx.ToolTip("%.1f" % mainValue))
@@ -235,10 +234,10 @@ class TargetingMiscViewFull(StatsView):
# if you add stuff to cargo, the capacity doesn't change and thus it is still cached
# This assures us that we force refresh of cargo tooltip
tipLines = []
tipLines.append(u"Cargohold: %.1fm\u00B3 / %sm\u00B3"% (fit.cargoBayUsed, cachedCargo["main"]))
tipLines.append(u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"]))
for attrName, tipAlias in cargoNamesOrder.items():
if cachedCargo[attrName] > 0:
tipLines.append(u"%s: %sm\u00B3"% (tipAlias, cachedCargo[attrName]))
tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName]))
label.SetToolTip(wx.ToolTip(u"\n".join(tipLines)))
else:
label.SetToolTip(wx.ToolTip(""))

View File

@@ -42,7 +42,7 @@ class AmmoIcon(ViewColumn):
if stuff.charge is None:
return -1
else:
iconFile = stuff.charge.icon.iconFile if stuff.item.icon else ""
iconFile = stuff.charge.icon.iconFile if stuff.charge.icon else ""
if iconFile:
return self.fittingView.imageList.GetImageIndex(iconFile, "icons")
else:

View File

@@ -85,7 +85,7 @@ class Miscellanea(ViewColumn):
if n > 0:
info.append("{0}{1}".format(n, slot[0].upper()))
return "+ "+", ".join(info), "Slot Modifiers"
elif itemGroup == "Energy Destabilizer":
elif itemGroup == "Energy Neutralizer":
neutAmount = stuff.getModifiedItemAttr("energyDestabilizationAmount")
cycleTime = stuff.cycleTime
if not neutAmount or not cycleTime:
@@ -94,7 +94,7 @@ class Miscellanea(ViewColumn):
text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3))
tooltip = "Energy neutralization per second"
return text, tooltip
elif itemGroup == "Energy Vampire":
elif itemGroup == "Energy Nosferatu":
neutAmount = stuff.getModifiedItemAttr("powerTransferAmount")
cycleTime = stuff.cycleTime
if not neutAmount or not cycleTime:
@@ -139,7 +139,7 @@ class Miscellanea(ViewColumn):
text = "{0}%".format(formatAmount(sigRadBonus, 3, 0, 3, forceSign=True))
tooltip = "Signature radius increase"
return text, tooltip
elif itemGroup == "Remote Sensor Damper":
elif itemGroup == "Sensor Dampener":
lockRangeBonus = stuff.getModifiedItemAttr("maxTargetRangeBonus")
scanResBonus = stuff.getModifiedItemAttr("scanResolutionBonus")
if lockRangeBonus is None or scanResBonus is None:
@@ -158,29 +158,54 @@ class Miscellanea(ViewColumn):
ttEntries.append("scan resolution")
tooltip = "{0} dampening".format(formatList(ttEntries)).capitalize()
return text, tooltip
elif itemGroup == "Tracking Disruptor":
elif itemGroup == "Weapon Disruptor":
# Weapon disruption now covers both tracking and guidance (missile) disruptors
# First get the attributes for tracking disruptors
optimalRangeBonus = stuff.getModifiedItemAttr("maxRangeBonus")
falloffRangeBonus = stuff.getModifiedItemAttr("falloffBonus")
trackingSpeedBonus = stuff.getModifiedItemAttr("trackingSpeedBonus")
if optimalRangeBonus is None or falloffRangeBonus is None or trackingSpeedBonus is None:
return "", None
display = 0
for bonus in (optimalRangeBonus, falloffRangeBonus, trackingSpeedBonus):
if abs(bonus) > abs(display):
display = bonus
if not display:
trackingDisruptorAttributes = {
"optimal range": optimalRangeBonus,
"falloff range": falloffRangeBonus,
"tracking speed": trackingSpeedBonus}
isTrackingDisruptor = any(map(lambda x: x is not None and x != 0, trackingDisruptorAttributes.values()))
# Then get the attributes for guidance disruptors
explosionVelocityBonus = stuff.getModifiedItemAttr("aoeVelocityBonus")
explosionRadiusBonus = stuff.getModifiedItemAttr("aoeCloudSizeBonus")
flightTimeBonus = stuff.getModifiedItemAttr("explosionDelayBonus")
missileVelocityBonus = stuff.getModifiedItemAttr("missileVelocityBonus")
guidanceDisruptorAttributes = {
"explosion velocity": explosionVelocityBonus,
"explosion radius": explosionRadiusBonus,
"flight time": flightTimeBonus,
"missile velocity": missileVelocityBonus}
isGuidanceDisruptor = any(map(lambda x: x is not None and x != 0, guidanceDisruptorAttributes.values()))
if isTrackingDisruptor:
attributes = trackingDisruptorAttributes
elif isGuidanceDisruptor:
attributes = guidanceDisruptorAttributes
else:
return "", None
display = max(attributes.values(), key=lambda x: abs(x))
text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True))
ttEntries = []
if display == optimalRangeBonus:
ttEntries.append("optimal range")
if display == falloffRangeBonus:
ttEntries.append("falloff range")
if display == trackingSpeedBonus:
ttEntries.append("tracking speed")
for attributeName, attributeValue in attributes.items():
if attributeValue == display:
ttEntries.append(attributeName)
tooltip = "{0} disruption".format(formatList(ttEntries)).capitalize()
return text, tooltip
elif itemGroup in ("ECM", "ECM Burst", "Remote ECM Burst"):
elif itemGroup in ("ECM", "Burst Jammer", "Remote ECM Burst"):
grav = stuff.getModifiedItemAttr("scanGravimetricStrengthBonus")
ladar = stuff.getModifiedItemAttr("scanLadarStrengthBonus")
radar = stuff.getModifiedItemAttr("scanRadarStrengthBonus")
@@ -401,7 +426,7 @@ class Miscellanea(ViewColumn):
ttEntries.append("shield")
tooltip = "{0} repaired per second".format(formatList(ttEntries)).capitalize()
return text, tooltip
elif itemGroup == "Cap Drain Drone":
elif itemGroup == "Energy Neutralizer Drone":
neutAmount = stuff.getModifiedItemAttr("energyDestabilizationAmount")
cycleTime = stuff.getModifiedItemAttr("duration")
if not neutAmount or not cycleTime:
@@ -462,7 +487,7 @@ class Miscellanea(ViewColumn):
if chargeGroup in ("Rocket", "Advanced Rocket", "Light Missile", "Advanced Light Missile", "FoF Light Missile",
"Heavy Assault Missile", "Advanced Heavy Assault Missile", "Heavy Missile", "Advanced Heavy Missile", "FoF Heavy Missile",
"Torpedo", "Advanced Torpedo", "Cruise Missile", "Advanced Cruise Missile", "FoF Cruise Missile",
"Citadel Torpedo", "Citadel Cruise"):
"Capital Torpedo", "Capital Cruise"):
cloudSize = stuff.getModifiedChargeAttr("aoeCloudSize")
aoeVelocity = stuff.getModifiedChargeAttr("aoeVelocity")
if not cloudSize or not aoeVelocity:

View File

@@ -526,7 +526,8 @@ class FittingView(d.Display):
# update state tooltip
tooltip = self.activeColumns[col].getToolTip(self.mods[self.GetItemData(row)])
self.SetToolTipString(tooltip)
if tooltip:
self.SetToolTipString(tooltip)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit()))
else:

View File

@@ -32,15 +32,14 @@ import gui.globalEvents as GE
class CharacterEditor(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__ (self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition,
size=wx.Size(641, 600), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.TAB_TRAVERSAL)
size=wx.Size(640, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
i = wx.IconFromBitmap(BitmapLoader.getBitmap("character_small", "gui"))
self.SetIcon(i)
self.mainFrame = parent
#self.disableWin = wx.WindowDisabler(self)
self.disableWin= wx.WindowDisabler(self)
self.SetSizeHintsSz(wx.Size(640, 600), wx.DefaultSize)
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
mainSizer = wx.BoxSizer(wx.VERTICAL)
@@ -170,7 +169,7 @@ class CharacterEditor(wx.Frame):
self.btnRestrict()
def editingFinished(self, event):
del self.disableWin
#del self.disableWin
wx.PostEvent(self.mainFrame, GE.CharListUpdated())
self.Destroy()
@@ -200,7 +199,7 @@ class CharacterEditor(wx.Frame):
wx.PostEvent(self, GE.CharListUpdated())
def closeEvent(self, event):
del self.disableWin
#del self.disableWin
wx.PostEvent(self.mainFrame, GE.CharListUpdated())
self.Destroy()
@@ -821,4 +820,4 @@ class SaveCharacterAs(wx.Dialog):
event.Skip()
self.Close()

804
gui/characterEditor.py.BASE Normal file
View File

@@ -0,0 +1,804 @@
#===============================================================================
# Copyright (C) 2010 Diego Duclos
#
# This file is part of pyfa.
#
# pyfa is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyfa is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyfa. If not, see <http://www.gnu.org/licenses/>.
#===============================================================================
import wx
import gui.mainFrame
import wx.lib.newevent
import wx.gizmos
from gui.bitmapLoader import BitmapLoader
import service
import gui.display as d
from gui.contextMenu import ContextMenu
from wx.lib.buttons import GenBitmapButton
import gui.globalEvents as GE
class CharacterEditor(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__ (self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition,
size=wx.Size(641, 600), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.TAB_TRAVERSAL)
i = wx.IconFromBitmap(BitmapLoader.getBitmap("character_small", "gui"))
self.SetIcon(i)
self.mainFrame = parent
self.disableWin= wx.WindowDisabler(self)
self.SetSizeHintsSz(wx.Size(640, 600), wx.DefaultSize)
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
mainSizer = wx.BoxSizer(wx.VERTICAL)
self.navSizer = wx.BoxSizer(wx.HORIZONTAL)
sChar = service.Character.getInstance()
self.btnSave = wx.Button(self, wx.ID_SAVE)
self.btnSave.Hide()
self.btnSave.Bind(wx.EVT_BUTTON, self.processRename)
self.characterRename = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
self.characterRename.Hide()
self.characterRename.Bind(wx.EVT_TEXT_ENTER, self.processRename)
self.charChoice = wx.Choice(self, wx.ID_ANY, style=0)
self.navSizer.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 5)
charList = sChar.getCharacterList()
for id, name, active in charList:
i = self.charChoice.Append(name, id)
if active:
self.charChoice.SetSelection(i)
self.navSizer.Add(self.btnSave, 0, wx.ALL , 5)
buttons = (("new", wx.ART_NEW),
("rename", BitmapLoader.getBitmap("rename", "gui")),
("copy", wx.ART_COPY),
("delete", wx.ART_DELETE))
size = None
for name, art in buttons:
bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) if name != "rename" else art
btn = wx.BitmapButton(self, wx.ID_ANY, bitmap)
if size is None:
size = btn.GetSize()
btn.SetMinSize(size)
btn.SetMaxSize(size)
btn.SetToolTipString("%s character" % name.capitalize())
btn.Bind(wx.EVT_BUTTON, getattr(self, name))
setattr(self, "btn%s" % name.capitalize(), btn)
self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2)
mainSizer.Add(self.navSizer, 0, wx.ALL | wx.EXPAND, 5)
self.viewsNBContainer = wx.Notebook(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0)
self.sview = SkillTreeView(self.viewsNBContainer)
#self.iview = ImplantsTreeView(self.viewsNBContainer)
#=======================================================================
# RC2
#self.iview.Show(False)
#=======================================================================
self.aview = APIView(self.viewsNBContainer)
self.viewsNBContainer.AddPage(self.sview, "Skills")
#=======================================================================
# Disabled for RC2
# self.viewsNBContainer.AddPage(self.iview, "Implants")
#=======================================================================
self.viewsNBContainer.AddPage(self.aview, "API")
mainSizer.Add(self.viewsNBContainer, 1, wx.EXPAND | wx.ALL, 5)
bSizerButtons = wx.BoxSizer(wx.HORIZONTAL)
self.btnSaveChar = wx.Button(self, wx.ID_ANY, "Save")
self.btnSaveAs = wx.Button(self, wx.ID_ANY, "Save As...")
self.btnRevert = wx.Button(self, wx.ID_ANY, "Revert")
self.btnOK = wx.Button(self, wx.ID_OK)
bSizerButtons.Add(self.btnSaveChar, 0, wx.ALL, 5)
bSizerButtons.Add(self.btnSaveAs, 0, wx.ALL, 5)
bSizerButtons.Add(self.btnRevert, 0, wx.ALL, 5)
bSizerButtons.AddStretchSpacer()
bSizerButtons.Add(self.btnOK, 0, wx.ALL, 5)
self.btnSaveChar.Bind(wx.EVT_BUTTON, self.saveChar)
self.btnSaveAs.Bind(wx.EVT_BUTTON, self.saveCharAs)
self.btnRevert.Bind(wx.EVT_BUTTON, self.revertChar)
self.btnOK.Bind(wx.EVT_BUTTON, self.editingFinished)
mainSizer.Add(bSizerButtons, 0, wx.EXPAND, 5)
self.btnRestrict()
self.SetSizer(mainSizer)
self.Layout()
self.Centre(wx.BOTH)
charID = self.getActiveCharacter()
if sChar.getCharName(charID) in ("All 0", "All 5"):
self.restrict()
self.registerEvents()
def btnRestrict(self):
sChar = service.Character.getInstance()
charID = self.getActiveCharacter()
char = sChar.getCharacter(charID)
# enable/disable character saving stuff
self.btnSaveChar.Enable(not char.ro and char.isDirty)
self.btnSaveAs.Enable(char.isDirty)
self.btnRevert.Enable(char.isDirty)
def refreshCharacterList(self, event=None):
sChar = service.Character.getInstance()
charList = sChar.getCharacterList()
active = self.getActiveCharacter()
self.charChoice.Clear()
for id, name, _ in charList:
i = self.charChoice.Append(name, id)
if active == id:
self.charChoice.SetSelection(i)
self.btnRestrict()
def editingFinished(self, event):
del self.disableWin
wx.PostEvent(self.mainFrame, GE.CharListUpdated())
self.Destroy()
def registerEvents(self):
self.Bind(wx.EVT_CLOSE, self.closeEvent)
self.Bind(GE.CHAR_LIST_UPDATED, self.refreshCharacterList)
self.charChoice.Bind(wx.EVT_CHOICE, self.charChanged)
def saveChar(self, event):
sChr = service.Character.getInstance()
charID = self.getActiveCharacter()
sChr.saveCharacter(charID)
self.sview.populateSkillTree()
wx.PostEvent(self, GE.CharListUpdated())
def saveCharAs(self, event):
charID = self.getActiveCharacter()
dlg = SaveCharacterAs(self, charID)
dlg.ShowModal()
self.sview.populateSkillTree()
def revertChar(self, event):
sChr = service.Character.getInstance()
charID = self.getActiveCharacter()
sChr.revertCharacter(charID)
self.sview.populateSkillTree()
wx.PostEvent(self, GE.CharListUpdated())
def closeEvent(self, event):
del self.disableWin
wx.PostEvent(self.mainFrame, GE.CharListUpdated())
self.Destroy()
def restrict(self):
self.btnRename.Enable(False)
self.btnDelete.Enable(False)
self.aview.stDisabledTip.Show()
self.aview.inputID.Enable(False)
self.aview.inputKey.Enable(False)
self.aview.charChoice.Enable(False)
self.aview.btnFetchCharList.Enable(False)
self.aview.btnFetchSkills.Enable(False)
self.aview.stStatus.SetLabel("")
self.aview.Layout()
def unrestrict(self):
self.btnRename.Enable(True)
self.btnDelete.Enable(True)
self.aview.stDisabledTip.Hide()
self.aview.inputID.Enable(True)
self.aview.inputKey.Enable(True)
self.aview.btnFetchCharList.Enable(True)
self.aview.btnFetchSkills.Enable(True)
self.aview.stStatus.SetLabel("")
self.aview.Layout()
def charChanged(self, event):
self.sview.populateSkillTree()
sChar = service.Character.getInstance()
charID = self.getActiveCharacter()
if sChar.getCharName(charID) in ("All 0", "All 5"):
self.restrict()
else:
self.unrestrict()
wx.PostEvent(self, GE.CharChanged())
if event is not None:
event.Skip()
def getActiveCharacter(self):
selection = self.charChoice.GetCurrentSelection()
return self.charChoice.GetClientData(selection) if selection is not None else None
def new(self, event):
sChar = service.Character.getInstance()
charID = sChar.new()
id = self.charChoice.Append(sChar.getCharName(charID), charID)
self.charChoice.SetSelection(id)
self.unrestrict()
self.btnSave.SetLabel("Create")
self.rename(None)
self.charChanged(None)
def rename(self, event):
if event is not None:
self.btnSave.SetLabel("Rename")
self.charChoice.Hide()
self.characterRename.Show()
self.navSizer.Replace(self.charChoice, self.characterRename)
self.characterRename.SetFocus()
for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete):
btn.Hide()
self.btnSave.Show()
self.navSizer.Layout()
sChar = service.Character.getInstance()
currName = sChar.getCharName(self.getActiveCharacter())
self.characterRename.SetValue(currName)
self.characterRename.SetSelection(0, len(currName))
def processRename(self, event):
sChar = service.Character.getInstance()
newName = self.characterRename.GetLineText(0)
if newName == "All 0" or newName == "All 5":
newName = newName + " bases are belong to us"
charID = self.getActiveCharacter()
sChar.rename(charID, newName)
self.charChoice.Show()
self.characterRename.Hide()
self.navSizer.Replace(self.characterRename, self.charChoice)
for btn in (self.btnNew, self.btnCopy, self.btnRename, self.btnDelete):
btn.Show()
self.btnSave.Hide()
self.navSizer.Layout()
self.refreshCharacterList()
def copy(self, event):
sChar = service.Character.getInstance()
charID = sChar.copy(self.getActiveCharacter())
id = self.charChoice.Append(sChar.getCharName(charID), charID)
self.charChoice.SetSelection(id)
self.unrestrict()
self.btnSave.SetLabel("Copy")
self.rename(None)
wx.PostEvent(self, GE.CharChanged())
def delete(self, event):
dlg = wx.MessageDialog(self,
"Do you really want to delete this character?",
"Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION)
if dlg.ShowModal() == wx.ID_YES:
sChar = service.Character.getInstance()
sChar.delete(self.getActiveCharacter())
sel = self.charChoice.GetSelection()
self.charChoice.Delete(sel)
self.charChoice.SetSelection(sel - 1)
newSelection = self.getActiveCharacter()
if sChar.getCharName(newSelection) in ("All 0", "All 5"):
self.restrict()
wx.PostEvent(self, GE.CharChanged())
def Destroy(self):
sFit = service.Fit.getInstance()
fitID = self.mainFrame.getActiveFit()
if fitID is not None:
sFit.clearFit(fitID)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
wx.Frame.Destroy(self)
class SkillTreeView (wx.Panel):
def __init__(self, parent):
wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL)
self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
pmainSizer = wx.BoxSizer(wx.VERTICAL)
tree = self.skillTreeListCtrl = wx.gizmos.TreeListCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT)
pmainSizer.Add(tree, 1, wx.EXPAND | wx.ALL, 5)
self.imageList = wx.ImageList(16, 16)
tree.SetImageList(self.imageList)
self.skillBookImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui"))
tree.AddColumn("Skill")
tree.AddColumn("Level")
tree.SetMainColumn(0)
self.root = tree.AddRoot("Skills")
tree.SetItemText(self.root, "Levels", 1)
tree.SetColumnWidth(0, 500)
self.populateSkillTree()
tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
tree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.scheduleMenu)
srcContext = "skillItem"
itemContext = "Skill"
context = (srcContext, itemContext)
self.statsMenu = ContextMenu.getMenu(None, context)
self.levelChangeMenu = ContextMenu.getMenu(None, context) or wx.Menu()
self.levelChangeMenu.AppendSeparator()
self.levelIds = {}
idUnlearned = wx.NewId()
self.levelIds[idUnlearned] = "Not learned"
self.levelChangeMenu.Append(idUnlearned, "Unlearn")
for level in xrange(6):
id = wx.NewId()
self.levelIds[id] = level
self.levelChangeMenu.Append(id, "Level %d" % level)
self.levelChangeMenu.AppendSeparator()
self.revertID = wx.NewId()
self.levelChangeMenu.Append(self.revertID, "Revert")
self.saveID = wx.NewId()
self.levelChangeMenu.Append(self.saveID, "Save")
self.levelChangeMenu.Bind(wx.EVT_MENU, self.changeLevel)
self.SetSizer(pmainSizer)
self.Layout()
def populateSkillTree(self):
sChar = service.Character.getInstance()
charID = self.Parent.Parent.getActiveCharacter()
dirtySkills = sChar.getDirtySkills(charID)
dirtyGroups = set([skill.item.group.ID for skill in dirtySkills])
groups = sChar.getSkillGroups()
imageId = self.skillBookImageId
root = self.root
tree = self.skillTreeListCtrl
tree.DeleteChildren(root)
for id, name in groups:
childId = tree.AppendItem(root, name, imageId)
tree.SetPyData(childId, id)
tree.AppendItem(childId, "dummy")
if id in dirtyGroups:
tree.SetItemTextColour(childId, wx.BLUE)
tree.SortChildren(root)
def expandLookup(self, event):
root = event.Item
tree = self.skillTreeListCtrl
child, cookie = tree.GetFirstChild(root)
if tree.GetItemText(child) == "dummy":
tree.Delete(child)
#Get the real intrestin' stuff
sChar = service.Character.getInstance()
char = self.Parent.Parent.getActiveCharacter()
for id, name in sChar.getSkills(tree.GetPyData(root)):
iconId = self.skillBookImageId
childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id))
level, dirty = sChar.getSkillLevel(char, id)
tree.SetItemText(childId, "Level %d" % level if isinstance(level, int) else level, 1)
if dirty:
tree.SetItemTextColour(childId, wx.BLUE)
tree.SortChildren(root)
def scheduleMenu(self, event):
event.Skip()
wx.CallAfter(self.spawnMenu, event.Item)
def spawnMenu(self, item):
self.skillTreeListCtrl.SelectItem(item)
if self.skillTreeListCtrl.GetChildrenCount(item) > 0:
return
sChar = service.Character.getInstance()
charID = self.Parent.Parent.getActiveCharacter()
sMkt = service.Market.getInstance()
if sChar.getCharName(charID) not in ("All 0", "All 5"):
self.levelChangeMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item))
self.PopupMenu(self.levelChangeMenu)
else:
self.statsMenu.selection = sMkt.getItem(self.skillTreeListCtrl.GetPyData(item))
self.PopupMenu(self.statsMenu)
def changeLevel(self, event):
level = self.levelIds.get(event.Id)
sChar = service.Character.getInstance()
charID = self.Parent.Parent.getActiveCharacter()
selection = self.skillTreeListCtrl.GetSelection()
skillID = self.skillTreeListCtrl.GetPyData(selection)
if level is not None:
self.skillTreeListCtrl.SetItemText(selection, "Level %d" % level if isinstance(level, int) else level, 1)
sChar.changeLevel(charID, skillID, level, persist=True)
elif event.Id == self.revertID:
sChar.revertLevel(charID, skillID)
elif event.Id == self.saveID:
sChar.saveSkill(charID, skillID)
self.skillTreeListCtrl.SetItemTextColour(selection, None)
dirtySkills = sChar.getDirtySkills(charID)
dirtyGroups = set([skill.item.group.ID for skill in dirtySkills])
parentID = self.skillTreeListCtrl.GetItemParent(selection)
groupID = self.skillTreeListCtrl.GetPyData(parentID)
if groupID not in dirtyGroups:
self.skillTreeListCtrl.SetItemTextColour(parentID, None)
wx.PostEvent(self.Parent.Parent, GE.CharListUpdated())
event.Skip()
class ImplantsTreeView (wx.Panel):
def addMarketViewImage(self, iconFile):
if iconFile is None:
return -1
bitmap = BitmapLoader.getBitmap(iconFile, "icons")
if bitmap is None:
return -1
else:
return self.availableImplantsImageList.Add(bitmap)
def __init__(self, parent):
wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL)
pmainSizer = wx.BoxSizer(wx.HORIZONTAL)
availableSizer = wx.BoxSizer(wx.VERTICAL)
pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5)
self.availableImplantsSearch = wx.SearchCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
self.availableImplantsSearch.ShowCancelButton(True)
availableSizer.Add(self.availableImplantsSearch, 0, wx.BOTTOM | wx.EXPAND, 2)
self.availableImplantsTree = wx.TreeCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT)
root = self.availableRoot = self.availableImplantsTree.AddRoot("Available")
self.availableImplantsImageList = wx.ImageList(16, 16)
self.availableImplantsTree.SetImageList(self.availableImplantsImageList)
availableSizer.Add(self.availableImplantsTree, 1, wx.EXPAND)
buttonSizer = wx.BoxSizer(wx.VERTICAL)
pmainSizer.Add(buttonSizer, 0, wx.TOP, 5)
self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style = wx.BORDER_NONE)
buttonSizer.Add(self.btnAdd, 0)
self.btnRemove = GenBitmapButton(self, wx.ID_REMOVE, BitmapLoader.getBitmap("fit_delete_small", "gui"), style = wx.BORDER_NONE)
buttonSizer.Add(self.btnRemove, 0)
self.pluggedImplantsTree = AvailableImplantsView(self, style=wx.LC_SINGLE_SEL)
pmainSizer.Add(self.pluggedImplantsTree, 1, wx.ALL | wx.EXPAND, 5)
self.SetSizer(pmainSizer)
# Populate the market tree
sMkt = service.Market.getInstance()
for mktGrp in sMkt.getImplantTree():
iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp))
childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID))
if sMkt.marketGroupHasTypesCheck(mktGrp) is False:
self.availableImplantsTree.AppendItem(childId, "dummy")
self.availableImplantsTree.SortChildren(self.availableRoot)
#Bind the event to replace dummies by real data
self.availableImplantsTree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup)
#Bind add & remove buttons
self.btnAdd.Bind(wx.EVT_BUTTON, self.addImplant)
self.btnRemove.Bind(wx.EVT_BUTTON, self.removeImplant)
#Bind the change of a character*
self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged)
self.Enable(False)
self.Layout()
def update(self, implants):
self.implants = implants[:]
self.implants.sort(key=lambda i: int(i.getModifiedItemAttr("implantness")))
self.pluggedImplantsTree.update(self.implants)
def charChanged(self, event):
sChar = service.Character.getInstance()
charID = self.Parent.Parent.getActiveCharacter()
name = sChar.getCharName(charID)
if name == "All 0" or name == "All 5":
self.Enable(False)
else:
self.Enable(True)
self.update(sChar.getImplants(charID))
event.Skip()
def expandLookup(self, event):
tree = self.availableImplantsTree
root = event.Item
child, cookie = tree.GetFirstChild(root)
text = tree.GetItemText(child)
if text == "dummy" or text == "itemdummy":
sMkt = service.Market.getInstance()
#A DUMMY! Keeeel!!! EBUL DUMMY MUST DIAF!
tree.Delete(child)
if text == "dummy":
#Add 'real stoof!' instead
for id, name, iconFile, more in sMkt.getChildren(tree.GetPyData(root)):
iconId = self.addMarketViewImage(iconFile)
childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id))
if more:
tree.AppendItem(childId, "dummy")
else:
tree.AppendItem(childId, "itemdummy")
if text == "itemdummy":
sMkt = service.Market.getInstance()
data, usedMetas = sMkt.getVariations(tree.GetPyData(root))
for item in data:
id = item.ID
name = item.name
iconFile = item.icon.iconFile
iconId = self.addMarketViewImage(iconFile)
tree.AppendItem(root, name, iconId, data=wx.TreeItemData(id))
tree.SortChildren(root)
def addImplant(self, event):
root = self.availableImplantsTree.GetSelection()
if not root.IsOk():
return
nchilds = self.availableImplantsTree.GetChildrenCount(root)
sChar = service.Character.getInstance()
charID = self.Parent.Parent.getActiveCharacter()
if nchilds == 0:
itemID = self.availableImplantsTree.GetPyData(root)
sChar.addImplant(charID, itemID)
self.update(sChar.getImplants(charID))
def removeImplant(self, event):
pos = self.pluggedImplantsTree.GetFirstSelected()
if pos != -1:
sChar = service.Character.getInstance()
charID = self.Parent.Parent.getActiveCharacter()
sChar.removeImplant(charID, self.implants[pos].slot)
self.update(sChar.getImplants(charID))
class AvailableImplantsView(d.Display):
DEFAULT_COLS = ["Base Name",
"attr:implantness"]
def __init__(self, parent, style):
d.Display.__init__(self, parent, style=style)
class APIView (wx.Panel):
def __init__(self, parent):
wx.Panel.__init__ (self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL)
self.Parent.Parent.Bind(GE.CHAR_CHANGED, self.charChanged)
self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
self.apiUrlCreatePredefined = u"https://community.eveonline.com/support/api-key/CreatePredefined?accessMask=8"
self.apiUrlKeyList = u"https://community.eveonline.com/support/api-key/"
pmainSizer = wx.BoxSizer(wx.VERTICAL)
hintSizer = wx.BoxSizer( wx.HORIZONTAL )
hintSizer.AddStretchSpacer()
self.stDisabledTip = wx.StaticText( self, wx.ID_ANY, u"You cannot add API Details for All 0 and All 5 characters.\n"
u"Please select another character or make a new one.", style=wx.ALIGN_CENTER )
self.stDisabledTip.Wrap( -1 )
hintSizer.Add( self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10 )
self.stDisabledTip.Hide()
hintSizer.AddStretchSpacer()
pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5)
fgSizerInput = wx.FlexGridSizer(3, 2, 0, 0)
fgSizerInput.AddGrowableCol(1)
fgSizerInput.SetFlexibleDirection(wx.BOTH)
fgSizerInput.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
self.m_staticIDText = wx.StaticText(self, wx.ID_ANY, u"keyID:", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticIDText.Wrap(-1)
fgSizerInput.Add(self.m_staticIDText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputID = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
fgSizerInput.Add(self.inputID, 1, wx.ALL | wx.EXPAND, 5)
self.m_staticKeyText = wx.StaticText(self, wx.ID_ANY, u"vCode:", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticKeyText.Wrap(-1)
fgSizerInput.Add(self.m_staticKeyText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.inputKey = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0)
fgSizerInput.Add(self.inputKey, 0, wx.ALL | wx.EXPAND, 5)
self.m_staticCharText = wx.StaticText(self, wx.ID_ANY, u"Character:", wx.DefaultPosition, wx.DefaultSize, 0)
self.m_staticCharText.Wrap(-1)
fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5)
self.charChoice = wx.Choice(self, wx.ID_ANY, style=0)
self.charChoice.Append("No Selection", 0)
fgSizerInput.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 5)
self.charChoice.Enable(False)
pmainSizer.Add(fgSizerInput, 0, wx.EXPAND, 5)
btnSizer = wx.BoxSizer( wx.HORIZONTAL )
btnSizer.AddStretchSpacer()
self.btnFetchCharList = wx.Button(self, wx.ID_ANY, u"Get Characters")
btnSizer.Add(self.btnFetchCharList, 0, wx.ALL, 2)
self.btnFetchCharList.Bind(wx.EVT_BUTTON, self.fetchCharList)
self.btnFetchSkills = wx.Button(self, wx.ID_ANY, u"Fetch Skills")
btnSizer.Add(self.btnFetchSkills, 0, wx.ALL, 2)
self.btnFetchSkills.Bind(wx.EVT_BUTTON, self.fetchSkills)
self.btnFetchSkills.Enable(False)
btnSizer.AddStretchSpacer()
pmainSizer.Add(btnSizer, 0, wx.EXPAND, 5)
self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString)
pmainSizer.Add(self.stStatus, 0, wx.ALL, 5)
pmainSizer.AddStretchSpacer()
self.stAPITip = wx.StaticText( self, wx.ID_ANY, u"You can create a pre-defined key here (only CharacterSheet is required):", wx.DefaultPosition, wx.DefaultSize, 0 )
self.stAPITip.Wrap( -1 )
pmainSizer.Add( self.stAPITip, 0, wx.ALL, 2 )
self.hlEveAPI = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlCreatePredefined, self.apiUrlCreatePredefined, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE )
pmainSizer.Add( self.hlEveAPI, 0, wx.ALL, 2 )
self.stAPITip2 = wx.StaticText( self, wx.ID_ANY, u"Or, you can choose an existing key from:", wx.DefaultPosition, wx.DefaultSize, 0 )
self.stAPITip2.Wrap( -1 )
pmainSizer.Add( self.stAPITip2, 0, wx.ALL, 2 )
self.hlEveAPI2 = wx.HyperlinkCtrl( self, wx.ID_ANY, self.apiUrlKeyList, self.apiUrlKeyList, wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE )
pmainSizer.Add( self.hlEveAPI2, 0, wx.ALL, 2 )
self.SetSizer(pmainSizer)
self.Layout()
self.charChanged(None)
def charChanged(self, event):
sChar = service.Character.getInstance()
ID, key, char, chars = sChar.getApiDetails(self.Parent.Parent.getActiveCharacter())
self.inputID.SetValue(str(ID))
self.inputKey.SetValue(key)
self.charChoice.Clear()
if chars:
for charName in chars:
i = self.charChoice.Append(charName)
self.charChoice.SetStringSelection(char)
self.charChoice.Enable(True)
self.btnFetchSkills.Enable(True)
else:
self.charChoice.Append("No characters...", 0)
self.charChoice.SetSelection(0)
self.charChoice.Enable(False)
self.btnFetchSkills.Enable(False)
if event is not None:
event.Skip()
def fetchCharList(self, event):
self.stStatus.SetLabel("")
if self.inputID.GetLineText(0) == "" or self.inputKey.GetLineText(0) == "":
self.stStatus.SetLabel("Invalid keyID or vCode!")
return
sChar = service.Character.getInstance()
try:
list = sChar.apiCharList(self.Parent.Parent.getActiveCharacter(), self.inputID.GetLineText(0), self.inputKey.GetLineText(0))
except service.network.AuthenticationError, e:
self.stStatus.SetLabel("Authentication failure. Please check keyID and vCode combination.")
except service.network.TimeoutError, e:
self.stStatus.SetLabel("Request timed out. Please check network connectivity and/or proxy settings.")
except Exception, e:
self.stStatus.SetLabel("Error:\n%s"%e.message)
else:
self.charChoice.Clear()
for charName in list:
i = self.charChoice.Append(charName)
self.btnFetchSkills.Enable(True)
self.charChoice.Enable(True)
self.Layout()
self.charChoice.SetSelection(0)
def fetchSkills(self, event):
charName = self.charChoice.GetString(self.charChoice.GetSelection())
if charName:
try:
sChar = service.Character.getInstance()
sChar.apiFetch(self.Parent.Parent.getActiveCharacter(), charName)
self.stStatus.SetLabel("Successfully fetched %s\'s skills from EVE API." % charName)
except Exception, e:
self.stStatus.SetLabel("Unable to retrieve %s\'s skills. Error message:\n%s" % (charName, e))
class SaveCharacterAs(wx.Dialog):
def __init__(self, parent, charID):
wx.Dialog.__init__(self, parent, title="Save Character As...", size=wx.Size(300, 60))
self.charID = charID
self.parent = parent
sChar = service.Character.getInstance()
name = sChar.getCharName(charID)
bSizer1 = wx.BoxSizer(wx.HORIZONTAL)
self.input = wx.TextCtrl(self, wx.ID_ANY, name, style=wx.TE_PROCESS_ENTER)
bSizer1.Add(self.input, 1, wx.ALL, 5)
self.input.Bind(wx.EVT_TEXT_ENTER, self.change)
self.button = wx.Button(self, wx.ID_OK, u"Save")
bSizer1.Add(self.button, 0, wx.ALL, 5)
self.SetSizer(bSizer1)
self.Layout()
self.Centre(wx.BOTH)
self.button.Bind(wx.EVT_BUTTON, self.change)
def change(self, event):
sChar = service.Character.getInstance()
sChar.saveCharacterAs(self.charID, self.input.GetLineText(0))
wx.PostEvent(self.parent, GE.CharListUpdated())
event.Skip()
self.Close()

View File

@@ -43,15 +43,10 @@ class GangView ( ScrolledPanel ):
self.helpText = wx.StaticText( self, wx.ID_ANY, help, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE )
helpSizer.Add( self.helpText, 1, wx.ALL, 5 )
self.FitDNDPopupMenu = wx.Menu()
self.options = ["Fleet", "Wing", "Squad"]
self.fleet = {}
for id, option in enumerate(self.options):
item = self.FitDNDPopupMenu.Append(-1, option)
# We bind it to the mainFrame because it may be called from either this class or from FitItem via shipBrowser
self.mainFrame.Bind(wx.EVT_MENU, self.OnPopupItemSelected, item)
# set content for each commander
self.fleet[id] = {}
@@ -61,6 +56,8 @@ class GangView ( ScrolledPanel ):
self.fleet[id]['chChar'] = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, [] )
self.fleet[id]['fitSizer'] = wx.BoxSizer( wx.VERTICAL )
self.FitDNDPopupMenu = self.buildBoostermenu()
contentFGSizer = wx.FlexGridSizer( 5, 3, 0, 0 )
contentFGSizer.AddGrowableCol( 1 )
contentFGSizer.SetFlexibleDirection( wx.BOTH )
@@ -130,6 +127,15 @@ class GangView ( ScrolledPanel ):
self.RefreshBoosterFits()
self.RefreshCharacterList()
def buildBoostermenu(self):
menu = wx.Menu()
for id, option in enumerate(self.options):
item = menu.Append(-1, option)
# We bind it to the mainFrame because it may be called from either this class or from FitItem via shipBrowser
self.mainFrame.Bind(wx.EVT_MENU, self.OnPopupItemSelected, item)
return menu
def OnEnterWindow(self, event):
obj = event.GetEventObject()
obj.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
@@ -371,7 +377,7 @@ class GangView ( ScrolledPanel ):
def OnPopupItemSelected(self, event):
''' Fired when booster popup item is selected '''
# Get menu selection ID via self.options
menuItem = self.FitDNDPopupMenu.FindItemById(event.GetId())
menuItem = event.EventObject.FindItemById(event.GetId())
type = self.options.index(menuItem.GetText())
if self.draggedFitID:

View File

@@ -361,6 +361,9 @@ class ItemParams (wx.Panel):
else:
attrName = name
if info and config.debug:
attrName += " ({})".format(info.ID)
if info:
if info.icon is not None:
iconFile = info.icon.iconFile
@@ -376,7 +379,7 @@ class ItemParams (wx.Panel):
attrIcon = self.imageList.Add(BitmapLoader.getBitmap("07_15", "icons"))
index = self.paramList.InsertImageStringItem(sys.maxint, attrName,attrIcon)
index = self.paramList.InsertImageStringItem(sys.maxint, attrName, attrIcon)
idNameMap[idCount] = attrName
self.paramList.SetItemData(index, idCount)
idCount += 1
@@ -499,6 +502,8 @@ class ItemEffects (wx.Panel):
self.effectList.InsertColumn(0,"Name")
self.effectList.InsertColumn(1,"Implemented")
if config.debug:
self.effectList.InsertColumn(2,"ID")
#self.effectList.SetColumnWidth(0,385)
@@ -519,6 +524,8 @@ class ItemEffects (wx.Panel):
implemented = "Erroneous"
self.effectList.SetStringItem(index, 1, implemented)
if config.debug:
self.effectList.SetStringItem(index, 2, str(effects[name].ID))
self.effectList.RefreshRows()
self.Layout()

View File

@@ -24,6 +24,8 @@ import sqlalchemy
import wx
import time
from codecs import open
from wx._core import PyDeadObjectError
from wx.lib.wordwrap import wordwrap
@@ -114,8 +116,8 @@ class MainFrame(wx.Frame):
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)")
def __init__(self, title):
self.title=title
wx.Frame.__init__(self, None, wx.ID_ANY, self.title)
MainFrame.__instance = self
@@ -324,6 +326,7 @@ class MainFrame(wx.Frame):
def ShowAboutBox(self, evt):
import eos.config
v = sys.version_info
info = wx.AboutDialogInfo()
info.Name = "pyfa"
info.Version = gui.aboutData.versionString
@@ -334,14 +337,14 @@ class MainFrame(wx.Frame):
"\n\nLicenses:\n\t" +
"\n\t".join(gui.aboutData.licenses) +
"\n\nEVE Data: \t" + eos.config.gamedata_version +
"\nPython: \t" + sys.version +
"\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) +
"\nwxPython: \t" + wx.__version__ +
"\nSQLAlchemy: \t" + sqlalchemy.__version__,
700, wx.ClientDC(self))
500, wx.ClientDC(self))
if "__WXGTK__" in wx.PlatformInfo:
forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&amp;t=247609"
forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&amp;t=466425"
else:
forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=247609"
forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425"
info.WebSite = (forumUrl, "pyfa thread at EVE Online forum")
wx.AboutBox(info)
@@ -385,7 +388,7 @@ class MainFrame(wx.Frame):
print "oops, invalid fit format %d" % format
dlg.Destroy()
return
file = open(path, "w")
file = open(path, "w", encoding="utf-8")
file.write(output)
file.close()
dlg.Destroy()
@@ -395,10 +398,10 @@ class MainFrame(wx.Frame):
dlg.ShowModal()
def goWiki(self, event):
webbrowser.open('https://github.com/DarkFenX/Pyfa/wiki')
webbrowser.open('https://github.com/pyfa-org/Pyfa/wiki')
def goForums(self, event):
webbrowser.open('https://forums.eveonline.com/default.aspx?g=posts&t=247609')
webbrowser.open('https://forums.eveonline.com/default.aspx?g=posts&t=466425')
def registerMenu(self):
menuBar = self.GetMenuBar()
@@ -784,7 +787,7 @@ class MainFrame(wx.Frame):
else:
self.progressDialog.Update(info)
def fileImportCallback(self, info, fits=None):
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
@@ -792,18 +795,28 @@ class MainFrame(wx.Frame):
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 info == -1:
self.closeProgressDialog()
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
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:

View File

@@ -49,6 +49,10 @@ class MainMenuBar(wx.MenuBar):
self.attrEditorId = wx.NewId()
self.toggleOverridesId = wx.NewId()
if 'wxMac' in wx.PlatformInfo and wx.VERSION >= (3,0):
wx.ID_COPY = wx.NewId()
wx.ID_PASTE = wx.NewId()
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
wx.MenuBar.__init__(self)
@@ -77,10 +81,8 @@ class MainMenuBar(wx.MenuBar):
#editMenu.Append(wx.ID_UNDO)
#editMenu.Append(wx.ID_REDO)
copyText = "&To Clipboard" + ("\tCTRL+C" if 'wxMSW' in wx.PlatformInfo else "")
pasteText = "&From Clipboard" + ("\tCTRL+V" if 'wxMSW' in wx.PlatformInfo else "")
editMenu.Append(wx.ID_COPY, copyText, "Export a fit to the clipboard")
editMenu.Append(wx.ID_PASTE, pasteText, "Import a fit from the clipboard")
editMenu.Append(wx.ID_COPY, "To Clipboard\tCTRL+C", "Export a fit to the clipboard")
editMenu.Append(wx.ID_PASTE, "From Clipboard\tCTRL+V", "Import a fit from the clipboard")
editMenu.AppendSeparator()
editMenu.Append(self.saveCharId, "Save Character")
editMenu.Append(self.saveCharAsId, "Save Character As...")
@@ -106,7 +108,8 @@ class MainMenuBar(wx.MenuBar):
graphFrameItem.SetBitmap(BitmapLoader.getBitmap("graphs_small", "gui"))
windowMenu.AppendItem(graphFrameItem)
preferencesItem = wx.MenuItem(windowMenu, wx.ID_PREFERENCES, "Preferences\tCTRL+P")
preferencesShortCut = "CTRL+," if 'wxMac' in wx.PlatformInfo else "CTRL+P"
preferencesItem = wx.MenuItem(windowMenu, wx.ID_PREFERENCES, "Preferences\t"+preferencesShortCut)
preferencesItem.SetBitmap(BitmapLoader.getBitmap("preferences_small", "gui"))
windowMenu.AppendItem(preferencesItem)

View File

@@ -5,7 +5,7 @@
##
## Author: Darriele - HomeWorld
##
## Project home: http://github.com/DarkFenX/Pyfa - pyfa project
## Project home: https://github.com/pyfa-org/Pyfa - pyfa project
## Some portions of code are based on
## AGW:pycollapsiblepane generic implementation of wx.CollapsiblePane
## AGW:pycollapsiblepane credits ( from the original source file used ):

View File

@@ -20,6 +20,8 @@ import gui.utils.drawUtils as drawUtils
import gui.utils.animEffects as animEffects
import gui.utils.fonts as fonts
from service import fit
class PyGauge(wx.PyWindow):
"""
This class provides a visual alternative for `wx.Gauge`. It currently
@@ -174,10 +176,15 @@ class PyGauge(wx.PyWindow):
return self._range
def Animate(self):
if not self._timer:
self._timer = wx.Timer(self, self._timerId)
self._animStep = 0
self._timer.Start(self._period)
sFit = fit.Fit.getInstance()
if sFit.serviceFittingOptions["enableGaugeAnimation"]:
if not self._timer:
self._timer = wx.Timer(self, self._timerId)
self._animStep = 0
self._timer.Start(self._period)
else:
self._animValue = self._percentage
self.Refresh()
def SetRange(self, range, reinit = False):
"""

View File

@@ -1536,13 +1536,12 @@ class FitItem(SFItem.SFBrowserItem):
menu = wx.Menu()
toggleItem = menu.Append(wx.ID_ANY, "Booster Fit", kind=wx.ITEM_CHECK)
menu.Check(toggleItem.GetId(), self.fitBooster)
self.Bind(wx.EVT_MENU, self.OnToggleBooster, toggleItem)
if self.mainFrame.getActiveFit():
# If there is an active fit, get menu for setting individual boosters
menu.AppendSeparator()
boosterMenu = self.mainFrame.additionsPane.gangPage.FitDNDPopupMenu
boosterMenu = self.mainFrame.additionsPane.gangPage.buildBoostermenu()
menu.AppendSubMenu(boosterMenu, 'Set Booster')
self.PopupMenu(menu, pos)

View File

@@ -116,5 +116,5 @@ class UpdateDialog(wx.Dialog):
self.UpdateSettings.set('version', None)
def OnDownload(self, e):
wx.LaunchDefaultBrowser('https://github.com/DarkFenX/Pyfa/releases/tag/'+self.releaseInfo['tag_name'])
wx.LaunchDefaultBrowser('https://github.com/pyfa-org/Pyfa/releases/tag/'+self.releaseInfo['tag_name'])
self.OnClose(e)