Merge branch 'master' into py3EFFS

This commit is contained in:
Maru Maru
2018-06-16 04:52:10 -04:00
340 changed files with 1741 additions and 1870 deletions

View File

@@ -60,7 +60,7 @@ class ModuleAmmoPicker(ContextMenu):
damage = 0
range_ = (self.module.item.getAttribute("maxRange")) * \
(charge.getAttribute("weaponRangeMultiplier") or 1)
falloff = (self.module.item.getAttribute("falloff")) * \
falloff = (self.module.item.getAttribute("falloff") or 0) * \
(charge.getAttribute("fallofMultiplier") or 1)
for type_ in self.DAMAGE_TYPES:
d = charge.getAttribute("%sDamage" % type_)

View File

@@ -6,9 +6,23 @@ import wx
from service.market import Market
from service.fit import Fit
from service.settings import ContextMenuSettings
from itertools import chain
import re
class WhProjector(ContextMenu):
# CCP doesn't currently provide a mapping between the general Environment, and the specific environment effect
# (which can be random when going into Abyssal space). This is how we currently define it:
# environment type: specific type name previx
abyssal_mapping = {
'caustic_toxin_weather': 47862, # Exotic Particle Storm
'darkness_weather': 47863, # Dark Matter Field
'infernal_weather': 47864, # Plasma Firestorm
'electric_storm_weather': 47865, # Electrical Storm
'xenon_gas_weather': 47866, # Gamma-Ray Afterglow
}
def __init__(self):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
self.settings = ContextMenuSettings.getInstance()
@@ -20,32 +34,49 @@ class WhProjector(ContextMenu):
return srcContext == "projected"
def getText(self, itmContext, selection):
return "Add System Effects"
return "Add Environmental Effect"
def getSubMenu(self, context, selection, rootMenu, i, pitem):
msw = True if "wxMSW" in wx.PlatformInfo else False
sMkt = Market.getInstance()
effdata = sMkt.getSystemWideEffects()
# Wormholes
self.idmap = {}
sub = wx.Menu()
for swType in sorted(effdata):
subItem = wx.MenuItem(sub, wx.ID_ANY, swType)
grandSub = wx.Menu()
subItem.SetSubMenu(grandSub)
sub.Append(subItem)
wormhole_item = wx.MenuItem(sub, wx.ID_ANY, "Wormhole")
wormhole_menu = wx.Menu()
wormhole_item.SetSubMenu(wormhole_menu)
sub.Append(wormhole_item)
effdata = self.getEffectBeacons()
self.buildMenu(effdata, wormhole_menu, rootMenu, msw)
# Incursions
effdata = self.getEffectBeacons(incursions=True)
self.buildMenu(effdata, sub, rootMenu, msw)
# Abyssal Weather
abyssal_item = wx.MenuItem(sub, wx.ID_ANY, "Abyssal Weather")
abyssal_menu = wx.Menu()
abyssal_item.SetSubMenu(abyssal_menu)
sub.Append(abyssal_item)
effdata = self.getAbyssalWeather()
self.buildMenu(effdata, abyssal_menu, rootMenu, msw)
# Localized Weather
local_item = wx.MenuItem(sub, wx.ID_ANY, "Localized")
local_menu = wx.Menu()
local_item.SetSubMenu(local_menu)
sub.Append(local_item)
effdata = self.getLocalizedEnvironments()
self.buildMenu(effdata, local_menu, rootMenu, msw)
for swData in sorted(effdata[swType], key=lambda tpl: tpl[2]):
wxid = ContextMenu.nextID()
swObj, swName, swClass = swData
self.idmap[wxid] = (swObj, swName)
grandSubItem = wx.MenuItem(grandSub, wxid, swClass)
if msw:
rootMenu.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem)
else:
grandSub.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem)
grandSub.Append(grandSubItem)
return sub
def handleSelection(self, event):
@@ -61,5 +92,127 @@ class WhProjector(ContextMenu):
sFit.project(fitID, swObj)
wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID))
def buildMenu(self, data, local_menu, rootMenu, msw):
for swType in sorted(data):
subItem = wx.MenuItem(local_menu, wx.ID_ANY, swType)
grandSub = wx.Menu()
subItem.SetSubMenu(grandSub)
local_menu.Append(subItem)
for swData in sorted(data[swType], key=lambda tpl: tpl[2]):
wxid = ContextMenu.nextID()
swObj, swName, swClass = swData
self.idmap[wxid] = (swObj, swName)
grandSubItem = wx.MenuItem(grandSub, wxid, swClass)
if msw:
rootMenu.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem)
else:
grandSub.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem)
grandSub.Append(grandSubItem)
def getEffectBeacons(self, incursions=False):
"""
Get dictionary with system-wide effects
"""
sMkt = Market.getInstance()
# todo: rework this
# Container for system-wide effects
effects = {}
# Expressions for matching when detecting effects we're looking for
if incursions:
validgroups = ("Incursion ship attributes effects",)
else:
validgroups = ("Black Hole Effect Beacon",
"Cataclysmic Variable Effect Beacon",
"Magnetar Effect Beacon",
"Pulsar Effect Beacon",
"Red Giant Beacon",
"Wolf Rayet Effect Beacon")
# Stuff we don't want to see in names
garbages = ("Effect", "Beacon", "ship attributes effects")
# Get group with all the system-wide beacons
grp = sMkt.getGroup("Effect Beacon")
# Cycle through them
for beacon in sMkt.getItemsByGroup(grp):
# Check if it belongs to any valid group
for group in validgroups:
# Check beginning of the name only
if re.match(group, beacon.name):
# Get full beacon name
beaconname = beacon.name
for garbage in garbages:
beaconname = re.sub(garbage, "", beaconname)
beaconname = re.sub(" {2,}", " ", beaconname).strip()
# Get short name
shortname = re.sub(group, "", beacon.name)
for garbage in garbages:
shortname = re.sub(garbage, "", shortname)
shortname = re.sub(" {2,}", " ", shortname).strip()
# Get group name
groupname = group
for garbage in garbages:
groupname = re.sub(garbage, "", groupname)
groupname = re.sub(" {2,}", " ", groupname).strip()
# Add stuff to dictionary
if groupname not in effects:
effects[groupname] = set()
effects[groupname].add((beacon, beaconname, shortname))
# Break loop on 1st result
break
return effects
def getAbyssalWeather(self):
sMkt = Market.getInstance()
environments = {x.ID: x for x in sMkt.getGroup("Abyssal Environment").items}
items = chain(sMkt.getGroup("MassiveEnvironments").items, sMkt.getGroup("Non-Interactable Object").items)
effects = {}
for beacon in items:
if not beacon.isType('projected'):
continue
type = self.__class__.abyssal_mapping.get(beacon.name[0:-2], None)
type = environments.get(type, None)
if type is None:
continue
if type.name not in effects:
effects[type.name] = set()
display_name = "{} {}".format(type.name, beacon.name[-1:])
effects[type.name].add((beacon, display_name, display_name))
return effects
def getLocalizedEnvironments(self):
sMkt = Market.getInstance()
grp = sMkt.getGroup("Abyssal Hazards")
effects = dict()
for beacon in grp.items:
if not beacon.isType('projected'):
continue
# Localized effects, currently, have a name like "(size) (type) Cloud"
# Until this inevitably changes, do a simple split
name_parts = beacon.name.split(" ")
key = name_parts[1].strip()
if key not in effects:
effects[key] = set()
effects[key].add((beacon, beacon.name, beacon.name))
return effects
WhProjector.register()

View File

@@ -102,7 +102,7 @@ class ItemParams(wx.Panel):
if saveFileDialog.ShowModal() == wx.ID_CANCEL:
return # the user hit cancel...
with open(saveFileDialog.GetPath(), "wb") as exportFile:
with open(saveFileDialog.GetPath(), "w") as exportFile:
writer = csv.writer(exportFile, delimiter=',')
writer.writerow(

View File

@@ -45,7 +45,7 @@ class ItemEffects(wx.Panel):
self.effectList.SetColumnWidth(4, 40)
item = self.item
effects = item.effects
self.effects = effects = item.effects
names = list(effects.keys())
names.sort()
@@ -100,14 +100,15 @@ class ItemEffects(wx.Panel):
self.RefreshValues(event)
@staticmethod
def OnRightClick(event):
def OnRightClick(self, event):
"""
Debug use: open effect file with default application.
If effect file does not exist, create it
"""
file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % event.GetText().lower())
effect = self.effects[event.GetText()]
file_ = os.path.join(config.pyfaPath, "eos", "effects", "%s.py" % effect.handlerName)
if not os.path.isfile(file_):
open(file_, 'a').close()

View File

@@ -28,7 +28,9 @@ from eos.saveddata.fighter import Fighter
from eos.saveddata.module import Module, Slot, Rack
from eos.saveddata.fit import Fit
from service.fit import Fit as FitSvc
from service.market import Market
from gui.viewColumn import ViewColumn
from gui.builtinContextMenus.whProjector import WhProjector
import gui.mainFrame
pyfalog = Logger(__name__)
@@ -77,6 +79,15 @@ class BaseName(ViewColumn):
else:
return ""
elif isinstance(stuff, Module):
if self.projectedView:
# check for projected abyssal name
name_check = stuff.item.name[0:-2]
type = WhProjector.abyssal_mapping.get(name_check, None)
if type:
sMkt = Market.getInstance()
type = sMkt.getItem(type)
return "{} {}".format(type.name, stuff.item.name[-1:])
if stuff.isEmpty:
return "%s Slot" % Slot.getName(stuff.slot).capitalize()
else:

View File

@@ -60,7 +60,7 @@ class MaxRange(ViewColumn):
maxRange = stuff.maxRange if hasattr(stuff, "maxRange") else stuff.getModifiedItemAttr("maxRange", None)
falloff = stuff.falloff
if falloff:
if falloff and falloff >= 5:
falloff = "+%sm" % formatAmount(falloff, 3, 0, 3)
else:
falloff = ""

View File

@@ -108,6 +108,28 @@ class Miscellanea(ViewColumn):
text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3))
tooltip = "Tracking speed"
return text, tooltip
elif itemGroup == "Precursor Weapon":
info = []
trackingSpeed = stuff.getModifiedItemAttr("trackingSpeed")
if trackingSpeed:
text = "{0}".format(formatAmount(trackingSpeed, 3, 0, 3))
tooltip = "tracking speed"
info.append((text, tooltip))
maxBonusDamage = stuff.getModifiedItemAttr("damageMultiplierBonusMax")
bonusDamagePerCycle = stuff.getModifiedItemAttr("damageMultiplierBonusPerCycle")
cycleTime = stuff.getModifiedItemAttr("speed")
if maxBonusDamage and bonusDamagePerCycle and cycleTime:
cyclesToFullDamage = int(maxBonusDamage / bonusDamagePerCycle)
timeToFullDamage = (cycleTime / 1000) * cyclesToFullDamage
if cyclesToFullDamage:
text = "{0}s".format(formatAmount(timeToFullDamage, 3, 0, 3))
tooltip = "spool-up time"
info.append((text, tooltip))
if not info:
return "", None
text = ' | '.join(i[0] for i in info)
tooltip = ' and '.join(i[1] for i in info).capitalize()
return text, tooltip
elif itemCategory == "Subsystem":
slots = ("hi", "med", "low")
info = []

View File

@@ -39,6 +39,7 @@ from service.fit import Fit
from service.market import Market
from gui.utils.staticHelpers import DragDropHelper
import gui.utils.fonts as fonts
import gui.globalEvents as GE
@@ -148,6 +149,7 @@ class FittingView(d.Display):
self.mainFrame.Bind(EVT_FIT_RENAMED, self.fitRenamed)
self.mainFrame.Bind(EVT_FIT_REMOVED, self.fitRemoved)
self.mainFrame.Bind(ITEM_SELECTED, self.appendItem)
self.font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem)
self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.startDrag)
@@ -679,14 +681,27 @@ class FittingView(d.Display):
slot = Slot.getValue(slotType)
slotMap[slot] = fit.getSlotsFree(slot) < 0
font = wx.Font(self.GetClassDefaultAttributes().font)
for i, mod in enumerate(self.mods):
self.SetItemBackgroundColour(i, self.GetBackgroundColour())
# only consider changing color if we're dealing with a Module
if type(mod) is Module:
if slotMap[mod.slot] or getattr(mod, 'restrictionOverridden', None): # Color too many modules as red
hasRestrictionOverriden = getattr(mod, 'restrictionOverridden', None)
# If module had broken fitting restrictions but now doesn't,
# ensure it is now valid, and remove restrictionOverridden
# variable. More in #1519
if not fit.ignoreRestrictions and hasRestrictionOverriden:
clean = False
if mod.fits(fit, False):
if not mod.hardpoint:
clean = True
elif fit.getHardpointsFree(mod.hardpoint) >= 0:
clean = True
if clean:
del mod.restrictionOverridden
hasRestrictionOverriden = not hasRestrictionOverriden
if slotMap[mod.slot] or hasRestrictionOverriden: # Color too many modules as red
self.SetItemBackgroundColour(i, wx.Colour(204, 51, 51))
elif sFit.serviceFittingOptions["colorFitBySlot"]: # Color by slot it enabled
self.SetItemBackgroundColour(i, self.slotColour(mod.slot))
@@ -695,11 +710,11 @@ class FittingView(d.Display):
if isinstance(mod, Rack) and \
sFit.serviceFittingOptions["rackSlots"] and \
sFit.serviceFittingOptions["rackLabels"]:
font.SetWeight(wx.FONTWEIGHT_BOLD)
self.SetItemFont(i, font)
self.font.SetWeight(wx.FONTWEIGHT_BOLD)
self.SetItemFont(i, self.font)
else:
font.SetWeight(wx.FONTWEIGHT_NORMAL)
self.SetItemFont(i, font)
self.font.SetWeight(wx.FONTWEIGHT_NORMAL)
self.SetItemFont(i, self.font)
self.Thaw()
self.itemCount = self.GetItemCount()
@@ -726,13 +741,12 @@ class FittingView(d.Display):
# noinspection PyPropertyAccess
def MakeSnapshot(self, maxColumns=1337):
if self.FVsnapshot:
del self.FVsnapshot
self.FVsnapshot = None
tbmp = wx.Bitmap(16, 16)
tdc = wx.MemoryDC()
tdc.SelectObject(tbmp)
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
tdc.SetFont(font)
tdc.SetFont(self.font)
columnsWidths = []
for i in range(len(self.DEFAULT_COLS)):
@@ -828,7 +842,7 @@ class FittingView(d.Display):
mdc.SetBackground(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)))
mdc.Clear()
mdc.SetFont(font)
mdc.SetFont(self.font)
mdc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
cx = padding

View File

@@ -20,6 +20,7 @@ from gui.bitmap_loader import BitmapLoader
from gui.utils import draw
from gui.utils import color as color_utils
from service.fit import Fit
from gui.utils import fonts
_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent()
_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent()
@@ -357,7 +358,7 @@ class _TabRenderer:
self.tab_bitmap = None
self.tab_back_bitmap = None
self.padding = 4
self.font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False)
self.font = wx.Font(fonts.NORMAL, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
self.tab_img = img
self.position = (0, 0) # Not used internally for rendering - helper for tab container
@@ -1322,7 +1323,7 @@ class PFNotebookPagePreview(wx.Frame):
self.padding = 15
self.transp = 0
hfont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False)
hfont = wx.Font(fonts.NORMAL, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
self.SetFont(hfont)
tx, ty = self.GetTextExtent(self.title)
@@ -1384,7 +1385,7 @@ class PFNotebookPagePreview(wx.Frame):
mdc.SetBackground(wx.Brush(color))
mdc.Clear()
font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False)
font = wx.Font(11, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
mdc.SetFont(font)
x, y = mdc.GetTextExtent(self.title)

View File

@@ -157,6 +157,19 @@ class EveFittings(wx.Frame):
self.statusbar.SetStatusText(msg)
class ESIServerExceptionHandler(object):
def __init__(self, parentWindow, ex):
dlg = wx.MessageDialog(parentWindow,
"There was an issue starting up the localized server, try setting "
"Login Authentication Method to Manual by going to Preferences -> EVE SS0 -> "
"Login Authentication Method. If this doesn't fix the problem please file an "
"issue on Github.",
"Add Character Error",
wx.OK | wx.ICON_ERROR)
dlg.ShowModal()
pyfalog.error(ex)
class ESIExceptionHandler(object):
# todo: make this a generate excetpion handler for all calls
def __init__(self, parentWindow, ex):
@@ -325,10 +338,12 @@ class SsoCharacterMgmt(wx.Dialog):
self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE)
self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE)
@staticmethod
def addChar(event):
sEsi = Esi.getInstance()
sEsi.login()
def addChar(self, event):
try:
sEsi = Esi.getInstance()
sEsi.login()
except Exception as ex:
ESIServerExceptionHandler(self, ex)
def delChar(self, event):
item = self.lcCharacters.GetFirstSelected()

View File

@@ -34,7 +34,8 @@ from codecs import open
import config
from eos.config import gamedata_version
from eos.config import gamedata_version, gamedata_date
import datetime
import gui.aboutData
from gui.chrome_tabs import ChromeNotebook
@@ -369,7 +370,8 @@ class MainFrame(wx.Frame):
def ShowAboutBox(self, evt):
info = wx.adv.AboutDialogInfo()
info.Name = "pyfa"
info.Version = config.getVersion() # gui.aboutData.versionString
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

View File

@@ -189,7 +189,7 @@ class ShipBrowser(wx.Panel):
"amarr", "caldari", "gallente", "minmatar",
"sisters", "ore",
"serpentis", "angel", "blood", "sansha", "guristas", "mordu",
"jove", "upwell", None
"jove", "upwell", "triglavian", None
]
def raceNameKey(self, ship):

View File

@@ -90,7 +90,7 @@ class exportHtmlThread(threading.Thread):
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css" />
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
<script>
// http://stackoverflow.com/questions/32453806/uncaught-securityerror-failed-to-execute-replacestate-on-history-cannot-be
//http://stackoverflow.com/questions/32453806/uncaught-securityerror-failed-to-execute-replacestate-on-history-cannot-be
$(document).bind('mobileinit',function(){
$.mobile.changePage.defaults.changeHash = false;
$.mobile.hashListeningEnabled = false;
@@ -195,55 +195,51 @@ class exportHtmlThread(threading.Thread):
if len(fits) > 0:
groupFits += len(fits)
HTMLship = (
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" '
'data-corners="false">\n'
' <h2>' + ship.name + ' <span class="ui-li-count">' + str(
len(fits)) + '</span></h2>\n'
' <ul data-role="listview" data-shadow="false" data-inset="true" '
'data-corners="false">\n'
)
if len(fits) == 1:
for fit in fits:
if self.stopRunning:
return
fit = fits[0]
try:
dnaFit = Port.exportDna(getFit(fit[0]))
HTMLgroup += ' <li><a data-dna="' + dnaFit + '" target="_blank">' + ship.name + ": " + \
fit[1] + '</a></li>\n'
eftFit = Port.exportEft(getFit(fit[0]))
print(eftFit)
HTMLfit = (
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" '
'data-corners="false">\n'
' <h2>' + fit[1] + '</h2>\n'
' <ul data-role="listview" data-shadow="false" data-inset="true" '
'data-corners="false">\n'
)
HTMLfit += ' <li><pre>' + eftFit + '\n </pre></li>\n'
HTMLfit += ' </ul>\n </li>\n'
HTMLship += HTMLfit
except:
pyfalog.warning("Failed to export line")
pass
continue
finally:
if self.callback:
wx.CallAfter(self.callback, count)
count += 1
else:
# Ship group header
HTMLship = (
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">\n'
' <h2>' + ship.name + ' <span class="ui-li-count">' + str(
len(fits)) + '</span></h2>\n'
' <ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">\n'
)
for fit in fits:
if self.stopRunning:
return
try:
dnaFit = Port.exportDna(getFit(fit[0]))
print(dnaFit)
HTMLship += ' <li><a data-dna="' + dnaFit + '" target="_blank">' + fit[
1] + '</a></li>\n'
except:
pyfalog.warning("Failed to export line")
continue
finally:
if self.callback:
wx.CallAfter(self.callback, count)
count += 1
HTMLgroup += HTMLship + (' </ul>\n'
' </li>\n')
HTMLgroup += HTMLship + (' </ul>\n'
' </li>\n')
if groupFits > 0:
# Market group header
HTML += (
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">\n'
' <h2>' + group.groupName + ' <span class="ui-li-count">' + str(groupFits) + '</span></h2>\n'
' <ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">\n' + HTMLgroup +
' <ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">\n'
+ HTMLgroup +
' </ul>\n'
' </li>'
)
@@ -279,7 +275,8 @@ class exportHtmlThread(threading.Thread):
return
try:
dnaFit = Port.exportDna(getFit(fit[0]))
HTML += '<a class="outOfGameBrowserLink" target="_blank" href="' + dnaUrl + dnaFit + '">' + ship.name + ': ' + \
HTML += '<a class="outOfGameBrowserLink" target="_blank" href="' + dnaUrl + dnaFit + '">' \
+ ship.name + ': ' + \
fit[1] + '</a><br> \n'
except:
pyfalog.error("Failed to export line")