Merge branch 'master' into mutaplasmids

# Conflicts:
#	eve.db
This commit is contained in:
blitzmann
2018-06-03 16:31:20 -04:00
302 changed files with 1514 additions and 1829 deletions

View File

@@ -32,12 +32,15 @@ logging = Logger(__name__)
class BitmapLoader(object):
try:
archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r')
logging.info("Using zipped image files.")
except (IOError, TypeError):
logging.info("Using local image files.")
archive = None
# try:
# archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r')
# logging.info("Using zipped image files.")
# except (IOError, TypeError):
# logging.info("Using local image files.")
# archive = None
logging.info("Using local image files.")
archive = None
cached_bitmaps = OrderedDict()
dont_use_cached_bitmaps = False

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("Uninteractable Localized Effect Beacon")
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

@@ -31,3 +31,36 @@ class ItemDescription(wx.Panel):
mainSizer.Add(self.description, 1, wx.ALL | wx.EXPAND, 0)
self.Layout()
self.description.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
self.description.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
self.popupMenu = wx.Menu()
copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy')
self.popupMenu.Append(copyItem)
self.popupMenu.Bind(wx.EVT_MENU, self.menuClickHandler, copyItem)
def onPopupMenu(self, event):
self.PopupMenu(self.popupMenu)
def menuClickHandler(self, event):
selectedMenuItem = event.GetId()
if selectedMenuItem == 1: # Copy was chosen
self.copySelectionToClipboard()
def onKeyDown(self, event):
keyCode = event.GetKeyCode()
# Ctrl + C
if keyCode == 67 and event.ControlDown():
self.copySelectionToClipboard()
# Ctrl + A
if keyCode == 65 and event.ControlDown():
self.description.SelectAll()
def copySelectionToClipboard(self):
selectedText = self.description.SelectionToText()
if selectedText == '': # if no selection, copy all content
selectedText = self.description.ToText()
if wx.TheClipboard.Open():
wx.TheClipboard.SetData(wx.TextDataObject(selectedText))
wx.TheClipboard.Close()

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

@@ -13,5 +13,38 @@ class ItemTraits(wx.Panel):
self.traits = wx.html.HtmlWindow(self)
self.traits.SetPage(item.traits.traitText)
self.traits.Bind(wx.EVT_CONTEXT_MENU, self.onPopupMenu)
self.traits.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
mainSizer.Add(self.traits, 1, wx.ALL | wx.EXPAND, 0)
self.Layout()
self.popupMenu = wx.Menu()
copyItem = wx.MenuItem(self.popupMenu, 1, 'Copy')
self.popupMenu.Append(copyItem)
self.popupMenu.Bind(wx.EVT_MENU, self.menuClickHandler, copyItem)
def onPopupMenu(self, event):
self.PopupMenu(self.popupMenu)
def menuClickHandler(self, event):
selectedMenuItem = event.GetId()
if selectedMenuItem == 1: # Copy was chosen
self.copySelectionToClipboard()
def onKeyDown(self, event):
keyCode = event.GetKeyCode()
# Ctrl + C
if keyCode == 67 and event.ControlDown():
self.copySelectionToClipboard()
# Ctrl + A
if keyCode == 65 and event.ControlDown():
self.traits.SelectAll()
def copySelectionToClipboard(self):
selectedText = self.traits.SelectionToText()
if selectedText == '': # if no selection, copy all content
selectedText = self.traits.ToText()
if wx.TheClipboard.Open():
wx.TheClipboard.SetData(wx.TextDataObject(selectedText))
wx.TheClipboard.Close()

View File

@@ -53,6 +53,7 @@ class MarketTree(wx.TreeCtrl):
# And add real market group contents
sMkt = self.sMkt
currentMktGrp = sMkt.getMarketGroup(self.GetItemData(root), eager="children")
for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp):
# If market should have items but it doesn't, do not show it
if sMkt.marketGroupValidityCheck(childMktGrp) is False:

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

@@ -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,8 +681,6 @@ 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())
@@ -695,11 +695,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()
@@ -731,8 +731,7 @@ class FittingView(d.Display):
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 +827,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

@@ -357,7 +357,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(8, 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 +1322,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(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
self.SetFont(hfont)
tx, ty = self.GetTextExtent(self.title)
@@ -1384,7 +1384,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(8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
mdc.SetFont(font)
x, y = mdc.GetTextExtent(self.title)

View File

@@ -231,6 +231,7 @@ class ExportToEve(wx.Frame):
return self.charChoice.GetClientData(selection) if selection is not None else None
def exportFitting(self, event):
sPort = Port.getInstance()
fitID = self.mainFrame.getActiveFit()
self.statusbar.SetStatusText("", 0)
@@ -240,27 +241,32 @@ class ExportToEve(wx.Frame):
return
self.statusbar.SetStatusText("Sending request and awaiting response", 1)
sEsi = Esi.getInstance()
sFit = Fit.getInstance()
data = sPort.exportESI(sFit.getFit(fitID))
res = sEsi.postFitting(self.getActiveCharacter(), data)
try:
res.raise_for_status()
self.statusbar.SetStatusText("", 0)
self.statusbar.SetStatusText("", 1)
# try:
# text = json.loads(res.text)
# self.statusbar.SetStatusText(text['message'], 1)
# except ValueError:
# pyfalog.warning("Value error on loading JSON.")
# self.statusbar.SetStatusText("", 1)
self.statusbar.SetStatusText(res.reason, 1)
except requests.exceptions.ConnectionError:
msg = "Connection error, please check your internet connection"
pyfalog.error(msg)
self.statusbar.SetStatusText(msg)
self.statusbar.SetStatusText("ERROR", 0)
self.statusbar.SetStatusText(msg, 1)
except ESIExportException as ex:
pyfalog.error(ex)
self.statusbar.SetStatusText("ERROR", 0)
self.statusbar.SetStatusText(ex.args[0], 1)
self.statusbar.SetStatusText("{} - {}".format(res.status_code, res.reason), 1)
except APIException as ex:
ESIExceptionHandler(self, ex)
try:
ESIExceptionHandler(self, ex)
except Exception as ex:
self.statusbar.SetStatusText("ERROR", 0)
self.statusbar.SetStatusText("{} - {}".format(res.status_code, res.reason), 1)
pyfalog.error(ex)
class SsoCharacterMgmt(wx.Dialog):

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
@@ -98,6 +99,8 @@ except ImportError as e:
pyfalog = Logger(__name__)
pyfalog.debug("Done loading mainframe imports")
# dummy panel(no paint no erasebk)
class PFPanel(wx.Panel):
@@ -365,7 +368,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):