From b2c718d6140028bdff5f4d9c60b9177e0041134f Mon Sep 17 00:00:00 2001 From: Alexander Maryanovsky Date: Sat, 8 Sep 2018 15:06:31 +0300 Subject: [PATCH 01/12] Implemented copying the currently open fit stats to the clipboard. --- eos/saveddata/damagePattern.py | 9 ++ gui/mainFrame.py | 10 ++ gui/mainMenuBar.py | 4 + gui/utils/exportStats.py | 171 +++++++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 gui/utils/exportStats.py diff --git a/eos/saveddata/damagePattern.py b/eos/saveddata/damagePattern.py index d7889f131..f84a1f89b 100644 --- a/eos/saveddata/damagePattern.py +++ b/eos/saveddata/damagePattern.py @@ -73,6 +73,15 @@ class DamagePattern(object): "exp" : "explosive" } + @classmethod + def oneType(cls, damageType, amount=100): + pattern = DamagePattern() + pattern.update(amount if damageType == "em" else 0, + amount if damageType == "thermal" else 0, + amount if damageType == "kinetic" else 0, + amount if damageType == "explosive" else 0) + return pattern + @classmethod def importPatterns(cls, text): lines = re.split('[\n\r]+', text) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 9bd02feee..e50d07041 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -19,6 +19,8 @@ import sys import os.path +from typing import Optional, Any + from logbook import Logger import sqlalchemy @@ -64,6 +66,7 @@ from gui.utils.clipboard import toClipboard, fromClipboard from gui.updateDialog import UpdateDialog # noinspection PyUnresolvedReferences from gui.builtinViews import emptyView, entityEditor, fittingView, implantEditor # noqa: F401 +from gui.utils.exportStats import statsExportText from gui import graphFrame from service.settings import SettingsProvider @@ -538,6 +541,7 @@ class MainFrame(wx.Frame): # Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) + self.Bind(wx.EVT_MENU, self.exportFitStatsToClipboard, id=menuBar.fitStatsToClipboardId) # Fitting Restrictions self.Bind(wx.EVT_MENU, self.toggleIgnoreRestriction, id=menuBar.toggleIgnoreRestrictionID) @@ -759,6 +763,12 @@ class MainFrame(wx.Frame): except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") + def exportFitStatsToClipboard(self, event): + """ Puts fit stats in textual format into the clipboard""" + fit = db_getFit(self.getActiveFit()) + if fit: + toClipboard(statsExportText(fit)) + def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ sCharacter = Character.getInstance() diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index 9c421f779..8e61f2418 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -48,6 +48,7 @@ class MainMenuBar(wx.MenuBar): self.exportHtmlId = wx.NewId() self.wikiId = wx.NewId() self.forumId = wx.NewId() + self.fitStatsToClipboardId = wx.NewId() self.saveCharId = wx.NewId() self.saveCharAsId = wx.NewId() self.revertCharId = wx.NewId() @@ -95,6 +96,8 @@ class MainMenuBar(wx.MenuBar): 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.Append(self.fitStatsToClipboardId, "Fit Stats To Clipboard\tCTRL+ALT+C", + "Export the stats of the current fit to clipboard") editMenu.AppendSeparator() editMenu.Append(self.saveCharId, "Save Character") editMenu.Append(self.saveCharAsId, "Save Character As...") @@ -177,6 +180,7 @@ class MainMenuBar(wx.MenuBar): enable = event.fitID is not None self.Enable(wx.ID_SAVEAS, enable) self.Enable(wx.ID_COPY, enable) + self.Enable(self.fitStatsToClipboardId, enable) self.Enable(self.exportSkillsNeededId, enable) sChar = Character.getInstance() diff --git a/gui/utils/exportStats.py b/gui/utils/exportStats.py new file mode 100644 index 000000000..d0b312792 --- /dev/null +++ b/gui/utils/exportStats.py @@ -0,0 +1,171 @@ +from functools import reduce + +from eos.saveddata.damagePattern import DamagePattern + +from gui.utils.numberFormatter import formatAmount + + +tankTypes = ("shield", "armor", "hull") +damageTypes = ("em", "thermal", "kinetic", "explosive") +damagePatterns = [DamagePattern.oneType(damageType) for damageType in damageTypes] +damageTypeResonanceNames = [damageType.capitalize() + "DamageResonance" for damageType in damageTypes] +resonanceNames = {"shield": ["shield" + s for s in damageTypeResonanceNames], + "armor": ["armor" + s for s in damageTypeResonanceNames], + "hull": [s[0].lower() + s[1:] for s in damageTypeResonanceNames]} + + +def firepowerSection(fit): + """ Returns the text of the firepower section""" + firepower = [fit.totalDPS, fit.weaponDPS, fit.droneDPS, fit.totalVolley] + firepowerStr = [formatAmount(dps, 3, 0, 0) for dps in firepower] + showWeaponAndDroneDps = (fit.weaponDPS > 0) and (fit.droneDPS > 0) + if sum(firepower) == 0: + return "" + return "DPS: {} (".format(firepowerStr[0]) + \ + ("Weapon: {}, Drone: {}, ".format(*firepowerStr[1:3]) if showWeaponAndDroneDps else "") + \ + ("Volley: {})\n".format(firepowerStr[3])) + + +def tankSection(fit): + """ Returns the text of the tank section""" + ehp = [fit.ehp[tank] for tank in tankTypes] if fit.ehp is not None else [0, 0, 0] + ehp.append(sum(ehp)) + ehpStr = [formatAmount(ehpVal, 3, 0, 9) for ehpVal in ehp] + resists = {tankType: [1 - fit.ship.getModifiedItemAttr(s) for s in resonanceNames[tankType]] for tankType in tankTypes} + ehpAgainstDamageType = [sum(pattern.calculateEhp(fit).values()) for pattern in damagePatterns] + ehpAgainstDamageTypeStr = [formatAmount(ehpVal, 3, 0, 9) for ehpVal in ehpAgainstDamageType] + + return \ + " {:>7} {:>7} {:>7} {:>7} {:>7}\n".format("TOTAL", "EM", "THERM", "KIN", "EXP") + \ + "EHP {:>7} {:>7} {:>7} {:>7} {:>7}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ + "Shield {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[0], *resists["shield"]) + \ + "Armor {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[1], *resists["armor"]) + \ + "Hull {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[2], *resists["hull"]) + + +def repsSection(fit): + """ Returns the text of the repairs section""" + selfRep = [fit.effectiveTank[tankType + "Repair"] for tankType in tankTypes] + sustainRep = [fit.effectiveSustainableTank[tankType + "Repair"] for tankType in tankTypes] + remoteRep = [fit.remoteReps[tankType.capitalize()] for tankType in tankTypes] + shieldRegen = [fit.effectiveSustainableTank["passiveShield"], 0, 0] + shieldRechargeModuleMultipliers = [module.item.attributes["shieldRechargeRateMultiplier"].value for module in + fit.modules if + module.item and "shieldRechargeRateMultiplier" in module.item.attributes] + shieldRechargeMultiplierByModules = reduce(lambda x, y: x * y, shieldRechargeModuleMultipliers, 1) + if shieldRechargeMultiplierByModules >= 0.9: # If the total affect of modules on the shield recharge is negative or insignificant, we don't care about it + shieldRegen[0] = 0 + totalRep = list(zip(selfRep, remoteRep, shieldRegen)) + totalRep = list(map(sum, totalRep)) + + selfRep.append(sum(selfRep)) + sustainRep.append(sum(sustainRep)) + remoteRep.append(sum(remoteRep)) + shieldRegen.append(sum(shieldRegen)) + totalRep.append(sum(totalRep)) + + totalSelfRep = selfRep[-1] + totalRemoteRep = remoteRep[-1] + totalShieldRegen = shieldRegen[-1] + + text = "" + + if sum(totalRep) > 0: # Most commonly, there are no reps at all; then we skip this section + singleTypeRep = None + singleTypeRepName = None + if totalRemoteRep == 0 and totalShieldRegen == 0: # Only self rep + singleTypeRep = selfRep[:-1] + singleTypeRepName = "Self" + if totalSelfRep == 0 and totalShieldRegen == 0: # Only remote rep + singleTypeRep = remoteRep[:-1] + singleTypeRepName = "Remote" + if totalSelfRep == 0 and totalRemoteRep == 0: # Only shield regen + singleTypeRep = shieldRegen[:-1] + singleTypeRepName = "Regen" + if singleTypeRep and sum( + x > 0 for x in singleTypeRep) == 1: # Only one type of reps and only one tank type is repaired + index = next(i for i, v in enumerate(singleTypeRep) if v > 0) + if singleTypeRepName == "Regen": + text += "Shield regeneration: {} EHP/s".format(formatAmount(singleTypeRep[index], 3, 0, 9)) + else: + text += "{} {} repair: {} EHP/s".format(singleTypeRepName, tankTypes[index], + formatAmount(singleTypeRep[index], 3, 0, 9)) + if (singleTypeRepName == "Self") and (sustainRep[index] != singleTypeRep[index]): + text += " (Sustained: {} EHP/s)".format(formatAmount(sustainRep[index], 3, 0, 9)) + text += "\n" + else: # Otherwise show a table + selfRepStr = [formatAmount(rep, 3, 0, 9) for rep in selfRep] + sustainRepStr = [formatAmount(rep, 3, 0, 9) for rep in sustainRep] + remoteRepStr = [formatAmount(rep, 3, 0, 9) for rep in remoteRep] + shieldRegenStr = [formatAmount(rep, 3, 0, 9) if rep != 0 else "" for rep in shieldRegen] + totalRepStr = [formatAmount(rep, 3, 0, 9) for rep in totalRep] + + header = "REPS " + lines = [ + "Shield ", + "Armor ", + "Hull ", + "Total " + ] + + showSelfRepColumn = totalSelfRep > 0 + showSustainRepColumn = sustainRep != selfRep + showRemoteRepColumn = totalRemoteRep > 0 + showShieldRegenColumn = totalShieldRegen > 0 + + if showSelfRepColumn + showSustainRepColumn + showRemoteRepColumn + showShieldRegenColumn > 1: + header += "{:>7} ".format("TOTAL") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, totalRepStr)] + if showSelfRepColumn: + header += "{:>7} ".format("SELF") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, selfRepStr)] + if showSustainRepColumn: + header += "{:>7} ".format("SUST") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, sustainRepStr)] + if showRemoteRepColumn: + header += "{:>7} ".format("REMOTE") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, remoteRepStr)] + if showShieldRegenColumn: + header += "{:>7} ".format("REGEN") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, shieldRegenStr)] + + text += header + "\n" + repsByTank = zip(totalRep, selfRep, sustainRep, remoteRep, shieldRegen) + for line in lines: + reps = next(repsByTank) + if sum(reps) > 0: + text += line + "\n" + return text + + +def miscSection(fit): + text = "" + text += "Speed: {} m/s\n".format(formatAmount(fit.maxSpeed, 3, 0, 0)) + text += "Signature: {} m\n".format(formatAmount(fit.ship.getModifiedItemAttr("signatureRadius"), 3, 0, 9)) + + text += "Capacitor: {} GJ".format(formatAmount(fit.ship.getModifiedItemAttr("capacitorCapacity"), 3, 0, 9)) + capState = fit.capState + if fit.capStable: + text += " (Stable at {0:.0f}%)".format(capState) + else: + text += " (Lasts {})".format("%ds" % capState if capState <= 60 else "%dm%ds" % divmod(capState, 60)) + text += "\n" + + text += "Targeting range: {} km\n".format(formatAmount(fit.maxTargetRange / 1000, 3, 0, 0)) + text += "Scan resolution: {0:.0f} mm\n".format(fit.ship.getModifiedItemAttr("scanResolution")) + text += "Sensor strength: {}\n".format(formatAmount(fit.scanStrength, 3, 0, 0)) + + return text + + +def statsExportText(fit): + """ Returns the text of the stats export of the given fit""" + sections = filter(None, (firepowerSection(fit), # Prune empty sections + tankSection(fit), + repsSection(fit), + miscSection(fit))) + + text = "{} ({})\n".format(fit.name, fit.ship.name) + "\n" + text += "\n".join(sections) + + return text From 47434c68f95eae7499e6a01810b0ee98bb2ff539 Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Sun, 20 Oct 2019 15:25:06 +0300 Subject: [PATCH 02/12] Added UI for new type of copying data about fit to the clipboard --- gui/copySelectDialog.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index 7700c79cd..fc64aa91a 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -40,6 +40,7 @@ class CopySelectDialog(wx.Dialog): copyFormatEsi = 3 copyFormatMultiBuy = 4 copyFormatEfs = 5 + copyFormatFitStats = 6 def __init__(self, parent): super().__init__(parent, id=wx.ID_ANY, title="Select a format", size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE) @@ -50,7 +51,8 @@ class CopySelectDialog(wx.Dialog): CopySelectDialog.copyFormatDna : self.exportDna, CopySelectDialog.copyFormatEsi : self.exportEsi, CopySelectDialog.copyFormatMultiBuy: self.exportMultiBuy, - CopySelectDialog.copyFormatEfs : self.exportEfs + CopySelectDialog.copyFormatEfs : self.exportEfs, + CopySelectDialog.copyFormatFitStats: self.exportFitStats } self.mainFrame = parent @@ -62,6 +64,7 @@ class CopySelectDialog(wx.Dialog): ("ESI", (CopySelectDialog.copyFormatEsi, None)), ("DNA", (CopySelectDialog.copyFormatDna, DNA_OPTIONS)), ("EFS", (CopySelectDialog.copyFormatEfs, None)), + ("Fit Stats", (CopySelectDialog.copyFormatFitStats, None)), # ("XML", (CopySelectDialog.copyFormatXml, None)), )) @@ -117,7 +120,7 @@ class CopySelectDialog(wx.Dialog): self.Center() def Validate(self): - # Since this dialog is shown through aa ShowModal(), we hook into the Validate function to veto the closing of the dialog until we're ready. + # Since this dialog is shown through as ShowModal(), we hook into the Validate function to veto the closing of the dialog until we're ready. # This always returns False, and when we're ready will EndModal() selected = self.GetSelected() options = self.GetOptions() @@ -185,3 +188,9 @@ class CopySelectDialog(wx.Dialog): def exportEfs(self, options, callback): fit = getFit(self.mainFrame.getActiveFit()) EfsPort.exportEfs(fit, 0, callback) + + """ + Puts fit stats in textual format into the clipboard + """ + def exportFitStats(self, options, callback): + pass From 384d9f461410fe1e45ff6087e64c859576a3c204 Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Mon, 21 Oct 2019 13:22:00 +0300 Subject: [PATCH 03/12] Fixed wording in contributing.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 647a8b3a6..1becb0b0a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Virtual environment will be created in *PyfaEnv* folder. Project will be cloned ## Setting up the project manually -Clone the repo +Clone the repository ``` git clone PyfaDEV ``` @@ -36,7 +36,7 @@ pip install -r PyfaDEV\requirements.txt ``` > For some Linux distributions, you may need to install separate wxPython bindings, such as `python-matplotlib-wx` -Check that libs from *requirements.txt* are installed +Check that the libs from *requirements.txt* are installed ``` pip list ``` From ff42c4c711b3f808c6dfaa42808fd5b7a6e6e1c5 Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Sat, 26 Oct 2019 18:36:38 +0300 Subject: [PATCH 04/12] Added UI for new type of copying data about fit to the clipboard --- gui/copySelectDialog.py | 10 +++++-- service/port/port.py | 5 ++++ .../port/shipstats.py | 30 ++++++++++++------- 3 files changed, 32 insertions(+), 13 deletions(-) rename gui/utils/exportStats.py => service/port/shipstats.py (92%) diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index fc64aa91a..18fbf4d9a 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -64,7 +64,7 @@ class CopySelectDialog(wx.Dialog): ("ESI", (CopySelectDialog.copyFormatEsi, None)), ("DNA", (CopySelectDialog.copyFormatDna, DNA_OPTIONS)), ("EFS", (CopySelectDialog.copyFormatEfs, None)), - ("Fit Stats", (CopySelectDialog.copyFormatFitStats, None)), + ("Fit stats", (CopySelectDialog.copyFormatFitStats, None)), # ("XML", (CopySelectDialog.copyFormatXml, None)), )) @@ -120,7 +120,8 @@ class CopySelectDialog(wx.Dialog): self.Center() def Validate(self): - # Since this dialog is shown through as ShowModal(), we hook into the Validate function to veto the closing of the dialog until we're ready. + # Since this dialog is shown through as ShowModal(), + # we hook into the Validate function to veto the closing of the dialog until we're ready. # This always returns False, and when we're ready will EndModal() selected = self.GetSelected() options = self.GetOptions() @@ -192,5 +193,8 @@ class CopySelectDialog(wx.Dialog): """ Puts fit stats in textual format into the clipboard """ + # noinspection PyUnusedLocal def exportFitStats(self, options, callback): - pass + fit = getFit(self.mainFrame.getActiveFit()) + Port.exportFitStats(fit, callback) + diff --git a/service/port/port.py b/service/port/port.py index 2db627bbc..2fc4d879a 100644 --- a/service/port/port.py +++ b/service/port/port.py @@ -39,6 +39,7 @@ from service.port.eft import ( from service.port.esi import exportESI, importESI from service.port.multibuy import exportMultiBuy from service.port.shared import IPortUser, UserCancelException, processing_notify +from service.port.shipstats import exportFitStats from service.port.xml import importXml, exportXml from service.port.muta import parseMutant @@ -317,3 +318,7 @@ class Port: @staticmethod def exportMultiBuy(fit, options, callback=None): return exportMultiBuy(fit, options, callback=callback) + + @staticmethod + def exportFitStats(fit, callback=None): + return exportFitStats(fit, callback=callback) \ No newline at end of file diff --git a/gui/utils/exportStats.py b/service/port/shipstats.py similarity index 92% rename from gui/utils/exportStats.py rename to service/port/shipstats.py index d0b312792..190c964ec 100644 --- a/gui/utils/exportStats.py +++ b/service/port/shipstats.py @@ -1,10 +1,8 @@ from functools import reduce - from eos.saveddata.damagePattern import DamagePattern - from gui.utils.numberFormatter import formatAmount - +#todo remove these enums. Not sure they are not needed tankTypes = ("shield", "armor", "hull") damageTypes = ("em", "thermal", "kinetic", "explosive") damagePatterns = [DamagePattern.oneType(damageType) for damageType in damageTypes] @@ -16,13 +14,19 @@ resonanceNames = {"shield": ["shield" + s for s in damageTypeResonanceNames], def firepowerSection(fit): """ Returns the text of the firepower section""" - firepower = [fit.totalDPS, fit.weaponDPS, fit.droneDPS, fit.totalVolley] + totalDps = fit.getTotalDps().total + weaponDps = fit.getWeaponDps().total + droneDps = fit.getDroneDps().total + totalVolley = fit.getTotalVolley().total + firepower = [totalDps, weaponDps, droneDps, totalVolley] + firepowerStr = [formatAmount(dps, 3, 0, 0) for dps in firepower] - showWeaponAndDroneDps = (fit.weaponDPS > 0) and (fit.droneDPS > 0) + # showWeaponAndDroneDps = (weaponDps > 0) and (droneDps > 0) if sum(firepower) == 0: return "" + return "DPS: {} (".format(firepowerStr[0]) + \ - ("Weapon: {}, Drone: {}, ".format(*firepowerStr[1:3]) if showWeaponAndDroneDps else "") + \ + ("Weapon: {}, Drone: {}, ".format(*firepowerStr[1:3])) + \ ("Volley: {})\n".format(firepowerStr[3])) @@ -47,7 +51,8 @@ def repsSection(fit): """ Returns the text of the repairs section""" selfRep = [fit.effectiveTank[tankType + "Repair"] for tankType in tankTypes] sustainRep = [fit.effectiveSustainableTank[tankType + "Repair"] for tankType in tankTypes] - remoteRep = [fit.remoteReps[tankType.capitalize()] for tankType in tankTypes] + remoteRepObj = fit.getRemoteReps() + remoteRep = [remoteRepObj.shield, remoteRepObj.armor, remoteRepObj.hull] shieldRegen = [fit.effectiveSustainableTank["passiveShield"], 0, 0] shieldRechargeModuleMultipliers = [module.item.attributes["shieldRechargeRateMultiplier"].value for module in fit.modules if @@ -158,8 +163,10 @@ def miscSection(fit): return text -def statsExportText(fit): - """ Returns the text of the stats export of the given fit""" +def exportFitStats(fit, callback): + """ + Returns the text of the stats export of the given fit + """ sections = filter(None, (firepowerSection(fit), # Prune empty sections tankSection(fit), repsSection(fit), @@ -168,4 +175,7 @@ def statsExportText(fit): text = "{} ({})\n".format(fit.name, fit.ship.name) + "\n" text += "\n".join(sections) - return text + if callback: + callback(text) + else: + return text From c052297bf7a4a10d8f039712ff23c148fda4770b Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Wed, 30 Oct 2019 09:17:38 +0200 Subject: [PATCH 05/12] Added stats that were more or less agreed on in [Issue #2065] --- service/port/shipstats.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/service/port/shipstats.py b/service/port/shipstats.py index 190c964ec..ba71b0826 100644 --- a/service/port/shipstats.py +++ b/service/port/shipstats.py @@ -2,7 +2,6 @@ from functools import reduce from eos.saveddata.damagePattern import DamagePattern from gui.utils.numberFormatter import formatAmount -#todo remove these enums. Not sure they are not needed tankTypes = ("shield", "armor", "hull") damageTypes = ("em", "thermal", "kinetic", "explosive") damagePatterns = [DamagePattern.oneType(damageType) for damageType in damageTypes] @@ -39,12 +38,23 @@ def tankSection(fit): ehpAgainstDamageType = [sum(pattern.calculateEhp(fit).values()) for pattern in damagePatterns] ehpAgainstDamageTypeStr = [formatAmount(ehpVal, 3, 0, 9) for ehpVal in ehpAgainstDamageType] - return \ - " {:>7} {:>7} {:>7} {:>7} {:>7}\n".format("TOTAL", "EM", "THERM", "KIN", "EXP") + \ - "EHP {:>7} {:>7} {:>7} {:>7} {:>7}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ - "Shield {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[0], *resists["shield"]) + \ - "Armor {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[1], *resists["armor"]) + \ - "Hull {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[2], *resists["hull"]) + # not used for now. maybe will be improved later + def formattedOutput(): + return \ + " {:>7} {:>7} {:>7} {:>7} {:>7}\n".format("TOTAL", "EM", "THERM", "KIN", "EXP") + \ + "EHP {:>7} {:>7} {:>7} {:>7} {:>7}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ + "Shield {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[0], *resists["shield"]) + \ + "Armor {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[1], *resists["armor"]) + \ + "Hull {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[2], *resists["hull"]) + + def generalOutput(): + return \ + "EHP: {:>} (Em: {:>}, Th: {:>}, Kin: {:>}, Exp: {:>}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ + "Shield: {:>} (Em: {:.0%}, Th: {:.0%}, Kin: {:.0%}, Exp: {:.0%}\n".format(ehpStr[0], *resists["shield"]) + \ + "Armor: {:>} (Em: {:.0%}, Th: {:.0%}, Kin: {:.0%}, Exp: {:.0%}\n".format(ehpStr[1], *resists["armor"]) + \ + "Hull: {:>} (Em: {:.0%}, Th: {:.0%}, Kin: {:.0%}, Exp: {:.0%}\n".format(ehpStr[2], *resists["hull"]) + + return generalOutput() def repsSection(fit): From 58f853de5ba1606afa13152c98f663ff14e67b51 Mon Sep 17 00:00:00 2001 From: Alexander Maryanovsky Date: Sat, 8 Sep 2018 15:06:31 +0300 Subject: [PATCH 06/12] Implemented copying the currently open fit stats to the clipboard. --- eos/saveddata/damagePattern.py | 9 ++ gui/mainFrame.py | 9 ++ gui/utils/exportStats.py | 171 +++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 gui/utils/exportStats.py diff --git a/eos/saveddata/damagePattern.py b/eos/saveddata/damagePattern.py index f518be127..bd0e1cd0e 100644 --- a/eos/saveddata/damagePattern.py +++ b/eos/saveddata/damagePattern.py @@ -78,6 +78,15 @@ class DamagePattern: "exp" : "explosive" } + @classmethod + def oneType(cls, damageType, amount=100): + pattern = DamagePattern() + pattern.update(amount if damageType == "em" else 0, + amount if damageType == "thermal" else 0, + amount if damageType == "kinetic" else 0, + amount if damageType == "explosive" else 0) + return pattern + @classmethod def importPatterns(cls, text): lines = re.split('[\n\r]+', text) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 05744a504..dd748ea11 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -81,6 +81,8 @@ except ImportError as e: pyfalog.warning("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message) disableOverrideEditor = True +pyfalog = Logger(__name__) + pyfalog.debug("Done loading mainframe imports") @@ -549,6 +551,7 @@ class MainFrame(wx.Frame): # Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) + self.Bind(wx.EVT_MENU, self.exportFitStatsToClipboard, id=menuBar.fitStatsToClipboardId) # Fitting Restrictions self.Bind(wx.EVT_MENU, self.toggleIgnoreRestriction, id=menuBar.toggleIgnoreRestrictionID) @@ -780,6 +783,12 @@ class MainFrame(wx.Frame): with CopySelectDialog(self) as dlg: dlg.ShowModal() + def exportFitStatsToClipboard(self, event): + """ Puts fit stats in textual format into the clipboard""" + fit = db_getFit(self.getActiveFit()) + if fit: + toClipboard(statsExportText(fit)) + def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ sCharacter = Character.getInstance() diff --git a/gui/utils/exportStats.py b/gui/utils/exportStats.py new file mode 100644 index 000000000..d0b312792 --- /dev/null +++ b/gui/utils/exportStats.py @@ -0,0 +1,171 @@ +from functools import reduce + +from eos.saveddata.damagePattern import DamagePattern + +from gui.utils.numberFormatter import formatAmount + + +tankTypes = ("shield", "armor", "hull") +damageTypes = ("em", "thermal", "kinetic", "explosive") +damagePatterns = [DamagePattern.oneType(damageType) for damageType in damageTypes] +damageTypeResonanceNames = [damageType.capitalize() + "DamageResonance" for damageType in damageTypes] +resonanceNames = {"shield": ["shield" + s for s in damageTypeResonanceNames], + "armor": ["armor" + s for s in damageTypeResonanceNames], + "hull": [s[0].lower() + s[1:] for s in damageTypeResonanceNames]} + + +def firepowerSection(fit): + """ Returns the text of the firepower section""" + firepower = [fit.totalDPS, fit.weaponDPS, fit.droneDPS, fit.totalVolley] + firepowerStr = [formatAmount(dps, 3, 0, 0) for dps in firepower] + showWeaponAndDroneDps = (fit.weaponDPS > 0) and (fit.droneDPS > 0) + if sum(firepower) == 0: + return "" + return "DPS: {} (".format(firepowerStr[0]) + \ + ("Weapon: {}, Drone: {}, ".format(*firepowerStr[1:3]) if showWeaponAndDroneDps else "") + \ + ("Volley: {})\n".format(firepowerStr[3])) + + +def tankSection(fit): + """ Returns the text of the tank section""" + ehp = [fit.ehp[tank] for tank in tankTypes] if fit.ehp is not None else [0, 0, 0] + ehp.append(sum(ehp)) + ehpStr = [formatAmount(ehpVal, 3, 0, 9) for ehpVal in ehp] + resists = {tankType: [1 - fit.ship.getModifiedItemAttr(s) for s in resonanceNames[tankType]] for tankType in tankTypes} + ehpAgainstDamageType = [sum(pattern.calculateEhp(fit).values()) for pattern in damagePatterns] + ehpAgainstDamageTypeStr = [formatAmount(ehpVal, 3, 0, 9) for ehpVal in ehpAgainstDamageType] + + return \ + " {:>7} {:>7} {:>7} {:>7} {:>7}\n".format("TOTAL", "EM", "THERM", "KIN", "EXP") + \ + "EHP {:>7} {:>7} {:>7} {:>7} {:>7}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ + "Shield {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[0], *resists["shield"]) + \ + "Armor {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[1], *resists["armor"]) + \ + "Hull {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[2], *resists["hull"]) + + +def repsSection(fit): + """ Returns the text of the repairs section""" + selfRep = [fit.effectiveTank[tankType + "Repair"] for tankType in tankTypes] + sustainRep = [fit.effectiveSustainableTank[tankType + "Repair"] for tankType in tankTypes] + remoteRep = [fit.remoteReps[tankType.capitalize()] for tankType in tankTypes] + shieldRegen = [fit.effectiveSustainableTank["passiveShield"], 0, 0] + shieldRechargeModuleMultipliers = [module.item.attributes["shieldRechargeRateMultiplier"].value for module in + fit.modules if + module.item and "shieldRechargeRateMultiplier" in module.item.attributes] + shieldRechargeMultiplierByModules = reduce(lambda x, y: x * y, shieldRechargeModuleMultipliers, 1) + if shieldRechargeMultiplierByModules >= 0.9: # If the total affect of modules on the shield recharge is negative or insignificant, we don't care about it + shieldRegen[0] = 0 + totalRep = list(zip(selfRep, remoteRep, shieldRegen)) + totalRep = list(map(sum, totalRep)) + + selfRep.append(sum(selfRep)) + sustainRep.append(sum(sustainRep)) + remoteRep.append(sum(remoteRep)) + shieldRegen.append(sum(shieldRegen)) + totalRep.append(sum(totalRep)) + + totalSelfRep = selfRep[-1] + totalRemoteRep = remoteRep[-1] + totalShieldRegen = shieldRegen[-1] + + text = "" + + if sum(totalRep) > 0: # Most commonly, there are no reps at all; then we skip this section + singleTypeRep = None + singleTypeRepName = None + if totalRemoteRep == 0 and totalShieldRegen == 0: # Only self rep + singleTypeRep = selfRep[:-1] + singleTypeRepName = "Self" + if totalSelfRep == 0 and totalShieldRegen == 0: # Only remote rep + singleTypeRep = remoteRep[:-1] + singleTypeRepName = "Remote" + if totalSelfRep == 0 and totalRemoteRep == 0: # Only shield regen + singleTypeRep = shieldRegen[:-1] + singleTypeRepName = "Regen" + if singleTypeRep and sum( + x > 0 for x in singleTypeRep) == 1: # Only one type of reps and only one tank type is repaired + index = next(i for i, v in enumerate(singleTypeRep) if v > 0) + if singleTypeRepName == "Regen": + text += "Shield regeneration: {} EHP/s".format(formatAmount(singleTypeRep[index], 3, 0, 9)) + else: + text += "{} {} repair: {} EHP/s".format(singleTypeRepName, tankTypes[index], + formatAmount(singleTypeRep[index], 3, 0, 9)) + if (singleTypeRepName == "Self") and (sustainRep[index] != singleTypeRep[index]): + text += " (Sustained: {} EHP/s)".format(formatAmount(sustainRep[index], 3, 0, 9)) + text += "\n" + else: # Otherwise show a table + selfRepStr = [formatAmount(rep, 3, 0, 9) for rep in selfRep] + sustainRepStr = [formatAmount(rep, 3, 0, 9) for rep in sustainRep] + remoteRepStr = [formatAmount(rep, 3, 0, 9) for rep in remoteRep] + shieldRegenStr = [formatAmount(rep, 3, 0, 9) if rep != 0 else "" for rep in shieldRegen] + totalRepStr = [formatAmount(rep, 3, 0, 9) for rep in totalRep] + + header = "REPS " + lines = [ + "Shield ", + "Armor ", + "Hull ", + "Total " + ] + + showSelfRepColumn = totalSelfRep > 0 + showSustainRepColumn = sustainRep != selfRep + showRemoteRepColumn = totalRemoteRep > 0 + showShieldRegenColumn = totalShieldRegen > 0 + + if showSelfRepColumn + showSustainRepColumn + showRemoteRepColumn + showShieldRegenColumn > 1: + header += "{:>7} ".format("TOTAL") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, totalRepStr)] + if showSelfRepColumn: + header += "{:>7} ".format("SELF") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, selfRepStr)] + if showSustainRepColumn: + header += "{:>7} ".format("SUST") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, sustainRepStr)] + if showRemoteRepColumn: + header += "{:>7} ".format("REMOTE") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, remoteRepStr)] + if showShieldRegenColumn: + header += "{:>7} ".format("REGEN") + lines = [line + "{:>7} ".format(rep) for line, rep in zip(lines, shieldRegenStr)] + + text += header + "\n" + repsByTank = zip(totalRep, selfRep, sustainRep, remoteRep, shieldRegen) + for line in lines: + reps = next(repsByTank) + if sum(reps) > 0: + text += line + "\n" + return text + + +def miscSection(fit): + text = "" + text += "Speed: {} m/s\n".format(formatAmount(fit.maxSpeed, 3, 0, 0)) + text += "Signature: {} m\n".format(formatAmount(fit.ship.getModifiedItemAttr("signatureRadius"), 3, 0, 9)) + + text += "Capacitor: {} GJ".format(formatAmount(fit.ship.getModifiedItemAttr("capacitorCapacity"), 3, 0, 9)) + capState = fit.capState + if fit.capStable: + text += " (Stable at {0:.0f}%)".format(capState) + else: + text += " (Lasts {})".format("%ds" % capState if capState <= 60 else "%dm%ds" % divmod(capState, 60)) + text += "\n" + + text += "Targeting range: {} km\n".format(formatAmount(fit.maxTargetRange / 1000, 3, 0, 0)) + text += "Scan resolution: {0:.0f} mm\n".format(fit.ship.getModifiedItemAttr("scanResolution")) + text += "Sensor strength: {}\n".format(formatAmount(fit.scanStrength, 3, 0, 0)) + + return text + + +def statsExportText(fit): + """ Returns the text of the stats export of the given fit""" + sections = filter(None, (firepowerSection(fit), # Prune empty sections + tankSection(fit), + repsSection(fit), + miscSection(fit))) + + text = "{} ({})\n".format(fit.name, fit.ship.name) + "\n" + text += "\n".join(sections) + + return text From 1c2c8cc5f97cfb9610640d59cb74a38f51f5d6fc Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Sun, 20 Oct 2019 15:25:06 +0300 Subject: [PATCH 07/12] Added UI for new type of copying data about fit to the clipboard --- gui/copySelectDialog.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index 7700c79cd..fc64aa91a 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -40,6 +40,7 @@ class CopySelectDialog(wx.Dialog): copyFormatEsi = 3 copyFormatMultiBuy = 4 copyFormatEfs = 5 + copyFormatFitStats = 6 def __init__(self, parent): super().__init__(parent, id=wx.ID_ANY, title="Select a format", size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE) @@ -50,7 +51,8 @@ class CopySelectDialog(wx.Dialog): CopySelectDialog.copyFormatDna : self.exportDna, CopySelectDialog.copyFormatEsi : self.exportEsi, CopySelectDialog.copyFormatMultiBuy: self.exportMultiBuy, - CopySelectDialog.copyFormatEfs : self.exportEfs + CopySelectDialog.copyFormatEfs : self.exportEfs, + CopySelectDialog.copyFormatFitStats: self.exportFitStats } self.mainFrame = parent @@ -62,6 +64,7 @@ class CopySelectDialog(wx.Dialog): ("ESI", (CopySelectDialog.copyFormatEsi, None)), ("DNA", (CopySelectDialog.copyFormatDna, DNA_OPTIONS)), ("EFS", (CopySelectDialog.copyFormatEfs, None)), + ("Fit Stats", (CopySelectDialog.copyFormatFitStats, None)), # ("XML", (CopySelectDialog.copyFormatXml, None)), )) @@ -117,7 +120,7 @@ class CopySelectDialog(wx.Dialog): self.Center() def Validate(self): - # Since this dialog is shown through aa ShowModal(), we hook into the Validate function to veto the closing of the dialog until we're ready. + # Since this dialog is shown through as ShowModal(), we hook into the Validate function to veto the closing of the dialog until we're ready. # This always returns False, and when we're ready will EndModal() selected = self.GetSelected() options = self.GetOptions() @@ -185,3 +188,9 @@ class CopySelectDialog(wx.Dialog): def exportEfs(self, options, callback): fit = getFit(self.mainFrame.getActiveFit()) EfsPort.exportEfs(fit, 0, callback) + + """ + Puts fit stats in textual format into the clipboard + """ + def exportFitStats(self, options, callback): + pass From 76bdefcda681136bae15e92f8c63df40e89ed267 Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Mon, 21 Oct 2019 13:22:00 +0300 Subject: [PATCH 08/12] Fixed wording in contributing.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 647a8b3a6..1becb0b0a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ Virtual environment will be created in *PyfaEnv* folder. Project will be cloned ## Setting up the project manually -Clone the repo +Clone the repository ``` git clone PyfaDEV ``` @@ -36,7 +36,7 @@ pip install -r PyfaDEV\requirements.txt ``` > For some Linux distributions, you may need to install separate wxPython bindings, such as `python-matplotlib-wx` -Check that libs from *requirements.txt* are installed +Check that the libs from *requirements.txt* are installed ``` pip list ``` From 10dfdc362773fbbc2bc64c7abbc368c851d09bec Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Sat, 26 Oct 2019 18:36:38 +0300 Subject: [PATCH 09/12] Added UI for new type of copying data about fit to the clipboard --- gui/copySelectDialog.py | 10 +++++-- service/port/port.py | 5 ++++ .../port/shipstats.py | 30 ++++++++++++------- 3 files changed, 32 insertions(+), 13 deletions(-) rename gui/utils/exportStats.py => service/port/shipstats.py (92%) diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index fc64aa91a..18fbf4d9a 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -64,7 +64,7 @@ class CopySelectDialog(wx.Dialog): ("ESI", (CopySelectDialog.copyFormatEsi, None)), ("DNA", (CopySelectDialog.copyFormatDna, DNA_OPTIONS)), ("EFS", (CopySelectDialog.copyFormatEfs, None)), - ("Fit Stats", (CopySelectDialog.copyFormatFitStats, None)), + ("Fit stats", (CopySelectDialog.copyFormatFitStats, None)), # ("XML", (CopySelectDialog.copyFormatXml, None)), )) @@ -120,7 +120,8 @@ class CopySelectDialog(wx.Dialog): self.Center() def Validate(self): - # Since this dialog is shown through as ShowModal(), we hook into the Validate function to veto the closing of the dialog until we're ready. + # Since this dialog is shown through as ShowModal(), + # we hook into the Validate function to veto the closing of the dialog until we're ready. # This always returns False, and when we're ready will EndModal() selected = self.GetSelected() options = self.GetOptions() @@ -192,5 +193,8 @@ class CopySelectDialog(wx.Dialog): """ Puts fit stats in textual format into the clipboard """ + # noinspection PyUnusedLocal def exportFitStats(self, options, callback): - pass + fit = getFit(self.mainFrame.getActiveFit()) + Port.exportFitStats(fit, callback) + diff --git a/service/port/port.py b/service/port/port.py index 2db627bbc..2fc4d879a 100644 --- a/service/port/port.py +++ b/service/port/port.py @@ -39,6 +39,7 @@ from service.port.eft import ( from service.port.esi import exportESI, importESI from service.port.multibuy import exportMultiBuy from service.port.shared import IPortUser, UserCancelException, processing_notify +from service.port.shipstats import exportFitStats from service.port.xml import importXml, exportXml from service.port.muta import parseMutant @@ -317,3 +318,7 @@ class Port: @staticmethod def exportMultiBuy(fit, options, callback=None): return exportMultiBuy(fit, options, callback=callback) + + @staticmethod + def exportFitStats(fit, callback=None): + return exportFitStats(fit, callback=callback) \ No newline at end of file diff --git a/gui/utils/exportStats.py b/service/port/shipstats.py similarity index 92% rename from gui/utils/exportStats.py rename to service/port/shipstats.py index d0b312792..190c964ec 100644 --- a/gui/utils/exportStats.py +++ b/service/port/shipstats.py @@ -1,10 +1,8 @@ from functools import reduce - from eos.saveddata.damagePattern import DamagePattern - from gui.utils.numberFormatter import formatAmount - +#todo remove these enums. Not sure they are not needed tankTypes = ("shield", "armor", "hull") damageTypes = ("em", "thermal", "kinetic", "explosive") damagePatterns = [DamagePattern.oneType(damageType) for damageType in damageTypes] @@ -16,13 +14,19 @@ resonanceNames = {"shield": ["shield" + s for s in damageTypeResonanceNames], def firepowerSection(fit): """ Returns the text of the firepower section""" - firepower = [fit.totalDPS, fit.weaponDPS, fit.droneDPS, fit.totalVolley] + totalDps = fit.getTotalDps().total + weaponDps = fit.getWeaponDps().total + droneDps = fit.getDroneDps().total + totalVolley = fit.getTotalVolley().total + firepower = [totalDps, weaponDps, droneDps, totalVolley] + firepowerStr = [formatAmount(dps, 3, 0, 0) for dps in firepower] - showWeaponAndDroneDps = (fit.weaponDPS > 0) and (fit.droneDPS > 0) + # showWeaponAndDroneDps = (weaponDps > 0) and (droneDps > 0) if sum(firepower) == 0: return "" + return "DPS: {} (".format(firepowerStr[0]) + \ - ("Weapon: {}, Drone: {}, ".format(*firepowerStr[1:3]) if showWeaponAndDroneDps else "") + \ + ("Weapon: {}, Drone: {}, ".format(*firepowerStr[1:3])) + \ ("Volley: {})\n".format(firepowerStr[3])) @@ -47,7 +51,8 @@ def repsSection(fit): """ Returns the text of the repairs section""" selfRep = [fit.effectiveTank[tankType + "Repair"] for tankType in tankTypes] sustainRep = [fit.effectiveSustainableTank[tankType + "Repair"] for tankType in tankTypes] - remoteRep = [fit.remoteReps[tankType.capitalize()] for tankType in tankTypes] + remoteRepObj = fit.getRemoteReps() + remoteRep = [remoteRepObj.shield, remoteRepObj.armor, remoteRepObj.hull] shieldRegen = [fit.effectiveSustainableTank["passiveShield"], 0, 0] shieldRechargeModuleMultipliers = [module.item.attributes["shieldRechargeRateMultiplier"].value for module in fit.modules if @@ -158,8 +163,10 @@ def miscSection(fit): return text -def statsExportText(fit): - """ Returns the text of the stats export of the given fit""" +def exportFitStats(fit, callback): + """ + Returns the text of the stats export of the given fit + """ sections = filter(None, (firepowerSection(fit), # Prune empty sections tankSection(fit), repsSection(fit), @@ -168,4 +175,7 @@ def statsExportText(fit): text = "{} ({})\n".format(fit.name, fit.ship.name) + "\n" text += "\n".join(sections) - return text + if callback: + callback(text) + else: + return text From 0cf88cf7ca361d1d85ba5a20a0927bc65a014bdb Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Wed, 30 Oct 2019 09:17:38 +0200 Subject: [PATCH 10/12] Added stats that were more or less agreed on in [Issue #2065] --- service/port/shipstats.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/service/port/shipstats.py b/service/port/shipstats.py index 190c964ec..ba71b0826 100644 --- a/service/port/shipstats.py +++ b/service/port/shipstats.py @@ -2,7 +2,6 @@ from functools import reduce from eos.saveddata.damagePattern import DamagePattern from gui.utils.numberFormatter import formatAmount -#todo remove these enums. Not sure they are not needed tankTypes = ("shield", "armor", "hull") damageTypes = ("em", "thermal", "kinetic", "explosive") damagePatterns = [DamagePattern.oneType(damageType) for damageType in damageTypes] @@ -39,12 +38,23 @@ def tankSection(fit): ehpAgainstDamageType = [sum(pattern.calculateEhp(fit).values()) for pattern in damagePatterns] ehpAgainstDamageTypeStr = [formatAmount(ehpVal, 3, 0, 9) for ehpVal in ehpAgainstDamageType] - return \ - " {:>7} {:>7} {:>7} {:>7} {:>7}\n".format("TOTAL", "EM", "THERM", "KIN", "EXP") + \ - "EHP {:>7} {:>7} {:>7} {:>7} {:>7}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ - "Shield {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[0], *resists["shield"]) + \ - "Armor {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[1], *resists["armor"]) + \ - "Hull {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[2], *resists["hull"]) + # not used for now. maybe will be improved later + def formattedOutput(): + return \ + " {:>7} {:>7} {:>7} {:>7} {:>7}\n".format("TOTAL", "EM", "THERM", "KIN", "EXP") + \ + "EHP {:>7} {:>7} {:>7} {:>7} {:>7}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ + "Shield {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[0], *resists["shield"]) + \ + "Armor {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[1], *resists["armor"]) + \ + "Hull {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[2], *resists["hull"]) + + def generalOutput(): + return \ + "EHP: {:>} (Em: {:>}, Th: {:>}, Kin: {:>}, Exp: {:>}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ + "Shield: {:>} (Em: {:.0%}, Th: {:.0%}, Kin: {:.0%}, Exp: {:.0%}\n".format(ehpStr[0], *resists["shield"]) + \ + "Armor: {:>} (Em: {:.0%}, Th: {:.0%}, Kin: {:.0%}, Exp: {:.0%}\n".format(ehpStr[1], *resists["armor"]) + \ + "Hull: {:>} (Em: {:.0%}, Th: {:.0%}, Kin: {:.0%}, Exp: {:.0%}\n".format(ehpStr[2], *resists["hull"]) + + return generalOutput() def repsSection(fit): From 7157e876ca9404e2da19a0834dd37fc83d06925b Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Wed, 30 Oct 2019 11:46:03 +0200 Subject: [PATCH 11/12] Fixed issue with mainFrame after merging --- gui/mainFrame.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index dd748ea11..05744a504 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -81,8 +81,6 @@ except ImportError as e: pyfalog.warning("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message) disableOverrideEditor = True -pyfalog = Logger(__name__) - pyfalog.debug("Done loading mainframe imports") @@ -551,7 +549,6 @@ class MainFrame(wx.Frame): # Clipboard exports self.Bind(wx.EVT_MENU, self.exportToClipboard, id=wx.ID_COPY) - self.Bind(wx.EVT_MENU, self.exportFitStatsToClipboard, id=menuBar.fitStatsToClipboardId) # Fitting Restrictions self.Bind(wx.EVT_MENU, self.toggleIgnoreRestriction, id=menuBar.toggleIgnoreRestrictionID) @@ -783,12 +780,6 @@ class MainFrame(wx.Frame): with CopySelectDialog(self) as dlg: dlg.ShowModal() - def exportFitStatsToClipboard(self, event): - """ Puts fit stats in textual format into the clipboard""" - fit = db_getFit(self.getActiveFit()) - if fit: - toClipboard(statsExportText(fit)) - def exportSkillsNeeded(self, event): """ Exports skills needed for active fit and active character """ sCharacter = Character.getInstance() From 9943f784a8d74a31c04d3669d7d190da869c4c15 Mon Sep 17 00:00:00 2001 From: Gochim <54093496+Gochim@users.noreply.github.com> Date: Wed, 30 Oct 2019 13:34:54 +0200 Subject: [PATCH 12/12] Fixed code auto-checks for pull request --- gui/copySelectDialog.py | 4 +--- service/port/shipstats.py | 14 +++++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index 18fbf4d9a..6ea13ead9 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -190,11 +190,9 @@ class CopySelectDialog(wx.Dialog): fit = getFit(self.mainFrame.getActiveFit()) EfsPort.exportEfs(fit, 0, callback) - """ - Puts fit stats in textual format into the clipboard - """ # noinspection PyUnusedLocal def exportFitStats(self, options, callback): + """ Puts fit stats in textual format into the clipboard """ fit = getFit(self.mainFrame.getActiveFit()) Port.exportFitStats(fit, callback) diff --git a/service/port/shipstats.py b/service/port/shipstats.py index ba71b0826..0ab42c30a 100644 --- a/service/port/shipstats.py +++ b/service/port/shipstats.py @@ -39,13 +39,13 @@ def tankSection(fit): ehpAgainstDamageTypeStr = [formatAmount(ehpVal, 3, 0, 9) for ehpVal in ehpAgainstDamageType] # not used for now. maybe will be improved later - def formattedOutput(): - return \ - " {:>7} {:>7} {:>7} {:>7} {:>7}\n".format("TOTAL", "EM", "THERM", "KIN", "EXP") + \ - "EHP {:>7} {:>7} {:>7} {:>7} {:>7}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ - "Shield {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[0], *resists["shield"]) + \ - "Armor {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[1], *resists["armor"]) + \ - "Hull {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[2], *resists["hull"]) + # def formattedOutput(): + # return \ + # " {:>7} {:>7} {:>7} {:>7} {:>7}\n".format("TOTAL", "EM", "THERM", "KIN", "EXP") + \ + # "EHP {:>7} {:>7} {:>7} {:>7} {:>7}\n".format(ehpStr[3], *ehpAgainstDamageTypeStr) + \ + # "Shield {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[0], *resists["shield"]) + \ + # "Armor {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[1], *resists["armor"]) + \ + # "Hull {:>7} {:>7.0%} {:>7.0%} {:>7.0%} {:>7.0%}\n".format(ehpStr[2], *resists["hull"]) def generalOutput(): return \