Merge branch 'pyfa-org:master' into master

This commit is contained in:
正汰
2024-01-12 11:00:13 +08:00
committed by GitHub
263 changed files with 455210 additions and 113977 deletions

View File

@@ -1,71 +1,48 @@
image:
- Visual Studio 2019
- Ubuntu
- macos
clone_depth: 1
- Ubuntu2204
- Visual Studio 2022
- macos-catalina
for:
-
matrix:
only:
- image: Ubuntu
- image: Ubuntu2204
environment:
APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJDW/+oYNGOiPvwuwAL9tc/LQgg58aosIVpMYfepQZ20V+VZnHpZh8IRDA8Jo5xht19p2PksA+hFgqA0kpKtrSkuiWdE8rATQItfk4gf7yB0yGasJGGQZYazy9k/9XtmYkq2HHOOeEqdxvrICddJQ88MLCLT9lJENSUP/YS/yGcjZFXVxE11pTeIcqlCRU+3eYa1v7BeNvXIKNhZoK5orXWrtuH3cy8jrSns/u70aYfJ6B2jA8CnWnDbuvpeQtEY61SQqlKUsSArNa8NAsXj41wr3Ar9gAG9330w7EMTqlutk8HZO35uHI0q5qinUhaQYufPPrVkb2L/N+ZCfu0fnh appveyor"
APPIMAGE_TOOL: appimagetool-x86_64.AppImage
PYTHON_APPIMAGE: python3.7.13-cp37-cp37m-manylinux2014_x86_64.AppImage
APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDhb96UEXy8yOy/f+riX/8kKbNx/lOfIZ4pP4Cw3Gj3DmnTwEnxtRtyc+xtaxOsKbt+7+EAXFpCzYX+jHMhtd0QtWB7dbey8DBg31g0f8C5EPquqROibVbhzr/F3f6/d52FFfq6Y/CWaAvLjezvipr+zOOsIFcVusqtXdPJQ/LtUJ0LS5d4lFiw5ELHSxHIpqwGwyb7PbR3ufEFoqbr8eYiCH+vlBob72ArPfo2f3u0sMvpGYmjVVu2jj4FEY2h89sLrGyFdNWBoyumRhkb38+WSAuyPa/Y21+g+S8sRzIlkwbxicGNMtrMIi6zHEIGAgA06Sw2psP807h730PPOVaWjUcU3ojNW8hH3nPizF74pT82+iP7/fFC4PXLP+tBa+8OoHC5yiO7QKUKprMSqVa1qOm8fHbrzglplKJXfzSfUtSE+AQ+HtHhuUWKI+0LBLDrsOJwI5hbsPOAuiZ5I3VfqfAOck6SH9TcmlapVmQEypc7d7oeeUtZSOuIWKXp068= dfx@aw"
APPIMAGE_TOOL: appimage-builder-x86_64.AppImage
DEPLOY_DIR: AppDir/opt/pyfa
# APPVEYOR_SSH_BLOCK: true
# APPVEYOR_SSH_BLOCK: true
cache:
- /home/appveyor/.cache/pip -> requirements.txt
init:
- sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
# init:
# - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
install:
- sh: git fetch --prune --unshallow # to fix the version dump issues
- sh: sudo DEBIAN_FRONTEND=noninteractive apt-get -y update
- sh: sudo DEBIAN_FRONTEND=noninteractive apt-get -y install python3.7-dev libgtk-3-dev python3-pip libwebkit2gtk-4.0-dev
# AppImage dependencies
- sh: sudo DEBIAN_FRONTEND=noninteractive apt-get -y install libfuse2
# Preparation script dependencies
- sh: sudo DEBIAN_FRONTEND=noninteractive apt-get -y install python3-wxgtk4.0 python3-sqlalchemy python3-logbook
before_build:
- sh: mkdir build && cd build
- sh: curl -LO https://github.com/AppImage/AppImageKit/releases/download/13/$APPIMAGE_TOOL && chmod +x $APPIMAGE_TOOL
- sh: curl -LO https://github.com/niess/python-appimage/releases/download/python3.7/$PYTHON_APPIMAGE && chmod +x $PYTHON_APPIMAGE
build_script:
# Prepare Python base AppImage, stripping Python metadata
- sh: ./$PYTHON_APPIMAGE --appimage-extract
- sh: mv squashfs-root AppDir
- sh: rm AppDir/python*.desktop
- sh: rm AppDir/usr/share/applications/*.desktop
- sh: rm AppDir/usr/share/metainfo/*.appdata.xml
- sh: mkdir -p $DEPLOY_DIR
# run install pyfa packages and any other requirements
- sh: AppDir/usr/bin/python -s -m pip install -U pip setuptools==41.6.0 wheel pathlib2
- sh: AppDir/usr/bin/python -s -m pip install -r ../requirements.txt
# Run scripts to prep pyfa data and build database
- sh: cd ../
# Prepare pyfa data
- sh: find locale/ -type f -name "*.po" -exec msgen "{}" -o "{}" \;
- sh: build/AppDir/usr/bin/python scripts/compile_lang.py
- sh: build/AppDir/usr/bin/python scripts/dump_crowdin_progress.py
- sh: build/AppDir/usr/bin/python db_update.py
- sh: export PYFA_VERSION="$(python3.7 scripts/dump_version.py)"
# Copy pyfa files to host
- sh: cp -r eos graphs gui imgs locale service utils eve.db config.py pyfa.py db_update.py README.md LICENSE version.yml ./build/$DEPLOY_DIR
- sh: find ./build/$DEPLOY_DIR | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf
# Copy static AppImage files
- sh: cd dist_assets/linux
- sh: cp AppRun pyfa.desktop ../../build/AppDir/
- sh: cp pyfa.desktop ../../build/AppDir/usr/share/applications/
- sh: cp org.pyfa.pyfa.appdata.xml ../../build/AppDir/usr/share/metainfo/
- sh: chmod +x pyfa && cp pyfa ../../build/AppDir/usr/bin
- sh: cd ../../
# Package it all up
- sh: mkdir dist
- sh: ./build/$APPIMAGE_TOOL build/AppDir dist/pyfa-$PYFA_VERSION-linux.AppImage
- sh: python3 -B scripts/compile_lang.py
- sh: python3 -B scripts/dump_crowdin_progress.py
- sh: python3 -B db_update.py
- sh: export PYFA_VERSION="$(python3 -B scripts/dump_version.py)"
- sh: mkdir build
# Download packaging tool
- sh: curl -o $APPIMAGE_TOOL -L https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
- sh: chmod +x $APPIMAGE_TOOL
build_script:
- sh: mkdir -p AppDir/opt/pyfa
- sh: cp -r eos graphs gui imgs locale service utils eve.db config.py pyfa.py db_update.py README.md LICENSE version.yml AppDir/opt/pyfa/
- sh: mkdir -p AppDir/usr/share/icons/hicolor/64x64/apps/
- sh: cp imgs/gui/pyfa64.png AppDir/usr/share/icons/hicolor/64x64/apps/pyfa.png
- sh: ./$APPIMAGE_TOOL --recipe dist_assets/linux/AppImageBuilder.yml
after_build:
- sh: ls -la build
- sh: ls -la
artifacts:
- path: dist/pyfa-$PYFA_VERSION-linux.AppImage
- path: pyfa-$PYFA_VERSION-linux.AppImage
deploy:
tag: $PYFA_VERSION
release: pyfa $PYFA_VERSION
@@ -81,17 +58,15 @@ for:
-
matrix:
only:
- image: Visual Studio 2019
- image: Visual Studio 2022
environment:
PYTHON: "C:\\Python37-x64"
PYTHON: "C:\\Python311-x64"
# Should be enabled only for build process debugging
# init:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
cache:
- C:\users\appveyor\appdata\local\pip\cache\ -> requirements.txt
install:
- cmd: git fetch --prune --unshallow # to fix the version dump issues
- ps: echo("OS version:")
- ps: "[System.Environment]::OSVersion.Version"
@@ -127,10 +102,8 @@ for:
# pip will build them from source using the MSVC compiler matching the
# target Python version and architecture
- ps: echo("Install pip requirements:")
# This one is needed to build wxpython 4.0.6 on windows
- cmd: "python -m pip install pathlib2"
- cmd: "python -m pip install -r requirements.txt"
- cmd: "python -m pip install PyInstaller==3.6"
- cmd: "python -m pip install PyInstaller==6.0.0"
before_build:
# directory that will contain the built files
- ps: $env:PYFA_DIST_DIR = "c:\projects\$env:APPVEYOR_PROJECT_SLUG\dist"
@@ -146,7 +119,7 @@ for:
# Build gamedata DB
- cmd: "python db_update.py"
# Build command for PyInstaller
- cmd: "python -m PyInstaller --noupx --clean --windowed --noconsole -y pyfa.spec"
- cmd: "python -m PyInstaller --clean -y pyfa.spec"
# Copy over manifest (See pyfa-org/pyfa#1622)
- ps: xcopy /y dist_assets\win\pyfa.exe.manifest $env:PYFA_DIST_DIR\pyfa\
# InnoScript EXE building. This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
@@ -173,16 +146,15 @@ for:
-
matrix:
only:
- image: macos
- image: macos-catalina
environment:
APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJDW/+oYNGOiPvwuwAL9tc/LQgg58aosIVpMYfepQZ20V+VZnHpZh8IRDA8Jo5xht19p2PksA+hFgqA0kpKtrSkuiWdE8rATQItfk4gf7yB0yGasJGGQZYazy9k/9XtmYkq2HHOOeEqdxvrICddJQ88MLCLT9lJENSUP/YS/yGcjZFXVxE11pTeIcqlCRU+3eYa1v7BeNvXIKNhZoK5orXWrtuH3cy8jrSns/u70aYfJ6B2jA8CnWnDbuvpeQtEY61SQqlKUsSArNa8NAsXj41wr3Ar9gAG9330w7EMTqlutk8HZO35uHI0q5qinUhaQYufPPrVkb2L/N+ZCfu0fnh appveyor"
cache:
- /Users/appveyor/Library/Caches/pip/ -> requirements.txt
init:
# - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
- sh: source ~/venv3.7/bin/activate
- sh: source ~/venv3.11/bin/activate
install:
- sh: git fetch --prune --unshallow # to fix the version dump issues
- sh: bash scripts/osx-setup.sh
build_script:
- sh: bash scripts/osx-translations.sh
@@ -211,4 +183,4 @@ for:
force_update: false
# deploy on tag push only
on:
APPVEYOR_REPO_TAG: true
APPVEYOR_REPO_TAG: true

4
.gitignore vendored
View File

@@ -90,6 +90,7 @@ target/
# pyenv
.python-version
PyfaEnv/
# celery beat schedule file
celerybeat-schedule
@@ -123,3 +124,6 @@ gitversion
*.fsdbinary
/locale/progress.json
# vscode settings
.vscode

View File

@@ -1,6 +1,6 @@
# pyfa
[![Join us on Slack!](https://pyfainvite.azurewebsites.net/badge.svg)](https://pyfainvite.azurewebsites.net/) [![Build Status](https://travis-ci.org/pyfa-org/Pyfa.svg?branch=master)](https://travis-ci.org/pyfa-org/Pyfa)
[![Build Status](https://ci.appveyor.com/api/projects/status/github/pyfa-org/pyfa?branch=master&svg=true)]([https://travis-ci.org/pyfa-org/Pyfa](https://ci.appveyor.com/project/pyfa-org/pyfa))
![pyfa](https://user-images.githubusercontent.com/275209/66119992-864be080-e5e2-11e9-994a-3a4368c9fad7.png)

View File

@@ -29,6 +29,7 @@ saveInRoot = False
evemonMinVersion = "4081"
minItemSearchLength = 3
minItemSearchLengthCjk = 1
pyfaPath = None
savePath = None

View File

@@ -117,8 +117,7 @@ def update_db():
for k, v in compiled_data.items():
row = {}
row.update(v)
if keyIdName not in row:
row[keyIdName] = int(k)
row[keyIdName] = int(k)
data.append(row)
return data
@@ -139,16 +138,18 @@ def update_db():
for row in data:
if (
# Apparently people really want Civilian modules available
(row['typeName_en-us'].startswith('Civilian') and "Shuttle" not in row['typeName_en-us']) or
row['typeName_en-us'] == 'Capsule' or
row['groupID'] == 4033 # destructible effect beacons
(row['typeName_en-us'].startswith('Civilian') and "Shuttle" not in row['typeName_en-us'])
or row['typeName_en-us'] == 'Capsule'
or row['groupID'] == 4033 # destructible effect beacons
or re.match('AIR .+Booster.*', row['typeName_en-us'])
):
row['published'] = True
# Nearly useless and clutter search results too much
elif (
row['typeName_en-us'].startswith('Limited Synth ') or
row['typeName_en-us'].startswith('Expired ') or
row['typeName_en-us'].endswith(' Filament') and (
row['typeName_en-us'].startswith('Limited Synth ')
or row['typeName_en-us'].startswith('Expired ')
or re.match('Mining Blitz .+ Booster Dose .+', row['typeName_en-us'])
or row['typeName_en-us'].endswith(' Filament') and (
"'Needlejack'" not in row['typeName_en-us'] and
"'Devana'" not in row['typeName_en-us'] and
"'Pochven'" not in row['typeName_en-us'] and
@@ -616,6 +617,16 @@ def update_db():
eos.db.gamedata_session.delete(cat)
# Unused normally, can be useful for customizing items
def _copyItem(srcName, tgtTypeID, tgtName):
eveType = eos.db.gamedata_session.query(eos.gamedata.Item).filter(eos.gamedata.Item.name == srcName).one()
eos.db.gamedata_session.expunge(eveType)
sqlalchemy.orm.make_transient(eveType)
eveType.ID = tgtTypeID
for suffix in eos.config.translation_mapping.values():
setattr(eveType, f'typeName{suffix}', tgtName)
eos.db.gamedata_session.add(eveType)
eos.db.gamedata_session.flush()
def _hardcodeAttribs(typeID, attrMap):
for attrName, value in attrMap.items():
try:
@@ -624,7 +635,7 @@ def update_db():
except sqlalchemy.orm.exc.NoResultFound:
attrInfo = eos.db.gamedata_session.query(eos.gamedata.AttributeInfo).filter(eos.gamedata.AttributeInfo.name == attrName).one()
attr = eos.gamedata.Attribute()
attr.ID = attrInfo.ID
attr.attributeID = attrInfo.ID
attr.typeID = typeID
attr.value = value
eos.db.gamedata_session.add(attr)
@@ -640,6 +651,152 @@ def update_db():
effect.effectName = effectName
item.effects[effectName] = effect
def hardcodeShapash():
shapashTypeID = 1000000
_copyItem(srcName='Utu', tgtTypeID=shapashTypeID, tgtName='Shapash')
attrMap = {
# Fitting
'powerOutput': 50,
'cpuOutput': 225,
'capacitorCapacity': 420,
'rechargeRate': 187500,
# Slots
'hiSlots': 3,
'medSlots': 4,
'lowSlots': 4,
'launcherSlotsLeft': 0,
'turretSlotsLeft': 3,
# Rigs
'rigSlots': 2,
'rigSize': 1,
'upgradeCapacity': 400,
# Shield
'shieldCapacity': 575,
'shieldRechargeRate': 625000,
'shieldEmDamageResonance': 1 - 0.0,
'shieldThermalDamageResonance': 1 - 0.6,
'shieldKineticDamageResonance': 1 - 0.85,
'shieldExplosiveDamageResonance': 1 - 0.5,
# Armor
'armorHP': 1015,
'armorEmDamageResonance': 1 - 0.5,
'armorThermalDamageResonance': 1 - 0.675,
'armorKineticDamageResonance': 1 - 0.8375,
'armorExplosiveDamageResonance': 1 - 0.1,
# Structure
'hp': 1274,
'emDamageResonance': 1 - 0.33,
'thermalDamageResonance': 1 - 0.33,
'kineticDamageResonance': 1 - 0.33,
'explosiveDamageResonance': 1 - 0.33,
'mass': 1215000,
'volume': 29500,
'capacity': 165,
# Navigation
'maxVelocity': 325,
'agility': 3.467,
'warpSpeedMultiplier': 5.5,
# Drones
'droneCapacity': 75,
'droneBandwidth': 25,
# Targeting
'maxTargetRange': 49000,
'maxLockedTargets': 6,
'scanRadarStrength': 0,
'scanLadarStrength': 0,
'scanMagnetometricStrength': 9,
'scanGravimetricStrength': 0,
'signatureRadius': 39,
'scanResolution': 550,
# Misc
'energyWarfareResistance': 0,
'stasisWebifierResistance': 0,
'weaponDisruptionResistance': 0}
effectMap = {
100100: 'pyfaCustomShapashAfArAmount',
100101: 'pyfaCustomShapashAfShtTrackingOptimal',
100102: 'pyfaCustomShapashGfShtDamage',
100103: 'pyfaCustomShapashGfPointRange',
100104: 'pyfaCustomShapashGfPropOverheat',
100105: 'pyfaCustomShapashRolePlateMass',
100106: 'pyfaCustomShapashRoleHeat'}
_hardcodeAttribs(shapashTypeID, attrMap)
_hardcodeEffects(shapashTypeID, effectMap)
def hardcodeCybele():
cybeleTypeID = 1000001
_copyItem(srcName='Adrestia', tgtTypeID=cybeleTypeID, tgtName='Cybele')
attrMap = {
# Fitting
'powerOutput': 1284,
'cpuOutput': 400,
'capacitorCapacity': 2400,
'rechargeRate': 334000,
'hiSlots': 5,
'medSlots': 4,
'lowSlots': 6,
'launcherSlotsLeft': 0,
'turretSlotsLeft': 5,
# Rigs
'rigSlots': 2,
'rigSize': 2,
'upgradeCapacity': 400,
# Shield
'shieldCapacity': 1200,
'shieldRechargeRate': 1250000,
'shieldEmDamageResonance': 1 - 0.0,
'shieldThermalDamageResonance': 1 - 0.5,
'shieldKineticDamageResonance': 1 - 0.9,
'shieldExplosiveDamageResonance': 1 - 0.5,
# Armor
'armorHP': 1900,
'armorEmDamageResonance': 1 - 0.5,
'armorThermalDamageResonance': 1 - 0.69,
'armorKineticDamageResonance': 1 - 0.85,
'armorExplosiveDamageResonance': 1 - 0.1,
# Structure
'hp': 2300,
'emDamageResonance': 1 - 0.33,
'thermalDamageResonance': 1 - 0.33,
'kineticDamageResonance': 1 - 0.33,
'explosiveDamageResonance': 1 - 0.33,
'mass': 11100000,
'volume': 112000,
'capacity': 450,
# Navigation
'maxVelocity': 235,
'agility': 0.457,
'warpSpeedMultiplier': 4.5,
# Drones
'droneCapacity': 100,
'droneBandwidth': 50,
# Targeting
'maxTargetRange': 60000,
'maxLockedTargets': 6,
'scanRadarStrength': 0,
'scanLadarStrength': 0,
'scanMagnetometricStrength': 15,
'scanGravimetricStrength': 0,
'signatureRadius': 115,
'scanResolution': 330,
# Misc
'energyWarfareResistance': 0,
'stasisWebifierResistance': 0,
'weaponDisruptionResistance': 0}
effectMap = {
100200: 'pyfaCustomCybeleHacMhtFalloff',
100201: 'pyfaCustomCybeleHacMhtTracking',
100202: 'pyfaCustomCybeleGcMhtDamage',
100203: 'pyfaCustomCybeleGcArAmount',
100204: 'pyfaCustomCybeleGcPointRange',
100205: 'pyfaCustomCybeleRoleVelocity',
100206: 'pyfaCustomCybeleRolePlateMass'}
_hardcodeAttribs(cybeleTypeID, attrMap)
_hardcodeEffects(cybeleTypeID, effectMap)
# hardcodeShapash()
# hardcodeCybele()
eos.db.gamedata_session.commit()
eos.db.gamedata_engine.execute('VACUUM')

View File

@@ -0,0 +1,70 @@
version: 1
AppDir:
path: ./AppDir
app_info:
id: pyfa
name: pyfa
icon: pyfa
version: '{{PYFA_VERSION}}'
exec: usr/bin/python3.11
exec_args: "-s $APPDIR/opt/pyfa/pyfa.py $@"
apt:
arch: [ amd64 ]
sources:
- sourceline: 'deb http://us.archive.ubuntu.com/ubuntu jammy main restricted universe multiverse'
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920d1991bc93c'
- sourceline: 'deb http://us.archive.ubuntu.com/ubuntu jammy-updates main restricted universe multiverse'
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920d1991bc93c'
- sourceline: 'deb http://us.archive.ubuntu.com/ubuntu jammy-backports main restricted universe multiverse'
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920d1991bc93c'
- sourceline: 'deb http://us.archive.ubuntu.com/ubuntu jammy-security main restricted universe multiverse'
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x871920d1991bc93c'
- sourceline: 'deb https://ppa.launchpadcontent.net/deadsnakes/ppa/ubuntu jammy main'
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xf23c5a6cf475977595c89f51ba6932366a755776'
include:
- python3.11
# wx dependencies
- libgtk-3-0
- librsvg2-common # GTK3 recommendation; without it, search in char editor crashes
- libwebkit2gtk-4.0-37 # Needed for wx's HTML lib
exclude:
- hicolor-icon-theme
- humanity-icon-theme
- ubuntu-mono
after_bundle:
# Install python dependencies to bundled interpreter
- export PYTHONHOME="AppDir/usr"
- export PYTHONPATH="AppDir/usr/lib/python3.11/site-packages"
- curl -L https://bootstrap.pypa.io/get-pip.py -o get-pip.py
- AppDir/usr/bin/python3.11 get-pip.py
# Just to bundle certificates with AppImage
- AppDir/usr/bin/python3.11 -s -m pip install certifi
- AppDir/usr/bin/python3.11 -s -m pip install -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-22.04 -r requirements.txt
files:
exclude:
- usr/lib/x86_64-linux-gnu/gconv
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
- usr/include
runtime:
env:
PYTHONHOME: '${APPDIR}/usr'
PYTHONPATH: '${APPDIR}/usr/lib/python3.11/site-packages'
SSL_CERT_FILE: '${APPDIR}/usr/local/lib/python3.11/dist-packages/certifi/cacert.pem'
# Workaround for https://github.com/AppImageCrafters/appimage-builder/issues/336
XDG_DATA_DIRS: '${APPDIR}/usr/local/share:${APPDIR}/usr/share:/usr/local/share:/usr/share:$XDG_DATA_DIRS'
AppImage:
sign-key: None
arch: x86_64
file_name: 'pyfa-{{PYFA_VERSION}}-linux.AppImage'

View File

@@ -1,19 +0,0 @@
#! /bin/bash -i
# Export APPRUN if running from an extracted image
self="$(readlink -f -- $0)"
here="${self%/*}"
APPDIR="${APPDIR:-${here}}"
# Export TCl/Tk
export TCL_LIBRARY="${APPDIR}/usr/share/tcltk/tcl8.4"
export TK_LIBRARY="${APPDIR}/usr/share/tcltk/tk8.4"
export TKPATH="${TK_LIBRARY}"
# Export SSL certificate
export SSL_CERT_FILE="${APPDIR}/opt/_internal/certs.pem"
# Call the entry point
#! /bin/bash -i
${APPDIR}/usr/bin/pyfa "$@"

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>org.pyfa.pyfa</id>
<metadata_license>MIT</metadata_license>
<project_license>GPL-3</project_license>
<name>Pyfa</name>
<summary>Pyfa </summary>
<description>
<p> Python Fitting Assitant for EVE Online
</p>
</description>
<launchable type="desktop-id">pyfa.desktop</launchable>
<url type="homepage">https://github.com/pyfa-org/Pyfa</url>
<provides>
<binary>pyfa</binary>
</provides>
</component>

View File

@@ -1,3 +0,0 @@
#! /bin/bash
${APPDIR}/usr/bin/python3.7 -s "${APPDIR}/opt/pyfa/pyfa.py" "$@"

View File

@@ -1,7 +0,0 @@
[Desktop Entry]
Type=Application
Name=Pyfa
Exec=pyfa
Comment=Python Fitting Assistant for EVE: Online
Icon=python
Categories=Game;

View File

@@ -80,6 +80,7 @@ exe = EXE(pyz,
app = BUNDLE(
exe,
name='pyfa.app',
version=os.getenv('PYFA_VERSION'),
icon=icon,
bundle_identifier=None,
info_plist={
@@ -88,5 +89,7 @@ app = BUNDLE(
'CFBundleName': 'pyfa',
'CFBundleDisplayName': 'pyfa',
'CFBundleIdentifier': 'org.pyfaorg.pyfa',
'CFBundleVersion': os.getenv('PYFA_VERSION'),
'CFBundleShortVersionString': os.getenv('PYFA_VERSION'),
}
)

View File

@@ -42,10 +42,10 @@ CloseApplications=yes
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
AllowNoIcons=yes
LicenseFile={#MyAppDir}\LICENSE
LicenseFile={#MyAppDir}\app\LICENSE
OutputDir={#MyOutputDir}
OutputBaseFilename={#MyOutputFile}
SetupIconFile={#MyAppDir}\pyfa.ico
SetupIconFile={#MyAppDir}\app\pyfa.ico
SolidCompression=yes
[Languages]

View File

@@ -51,7 +51,7 @@ mapper(DynamicItemAttribute, dynamicAttributes_table,
properties={"info": relation(AttributeInfo, lazy=False)})
mapper(DynamicItemItem, dynamicApplicable_table, properties={
"mutaplasmid": relation(DynamicItem),
"mutaplasmid": relation(DynamicItem, viewonly=True),
})
DynamicItemAttribute.ID = association_proxy("info", "attributeID")

View File

@@ -69,7 +69,8 @@ props = {
primaryjoin=dynamicApplicable_table.c.applicableTypeID == items_table.c.typeID,
secondaryjoin=dynamicApplicable_table.c.typeID == DynamicItem.typeID,
secondary=dynamicApplicable_table,
backref="applicableItems"
backref="applicableItems",
viewonly=True
)
}

View File

@@ -175,12 +175,13 @@ mapper(es_Fit, fits_table,
collection_class=HandledModuleList,
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == False), # noqa
order_by=modules_table.c.position,
overlaps='owner',
cascade='all, delete, delete-orphan'),
"_Fit__projectedModules": relation(
Module,
collection_class=HandledProjectedModList,
overlaps='owner, _Fit__modules',
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(modules_table.c.fitID == fits_table.c.ID, modules_table.c.projected == True)), # noqa
"owner": relation(
User,
@@ -190,37 +191,37 @@ mapper(es_Fit, fits_table,
"_Fit__boosters": relation(
Booster,
collection_class=HandledBoosterList,
cascade='all, delete, delete-orphan',
single_parent=True),
overlaps='owner',
cascade='all, delete, delete-orphan'),
"_Fit__drones": relation(
Drone,
collection_class=HandledDroneCargoList,
overlaps='owner',
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == False)), # noqa
"_Fit__fighters": relation(
Fighter,
collection_class=HandledDroneCargoList,
overlaps='owner',
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == False)), # noqa
"_Fit__cargo": relation(
Cargo,
collection_class=HandledDroneCargoList,
overlaps='owner',
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(cargo_table.c.fitID == fits_table.c.ID)),
"_Fit__projectedDrones": relation(
Drone,
collection_class=HandledProjectedDroneList,
overlaps='owner, _Fit__drones',
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(drones_table.c.fitID == fits_table.c.ID, drones_table.c.projected == True)), # noqa
"_Fit__projectedFighters": relation(
Fighter,
collection_class=HandledProjectedDroneList,
overlaps='owner, _Fit__fighters',
cascade='all, delete, delete-orphan',
single_parent=True,
primaryjoin=and_(fighters_table.c.fitID == fits_table.c.ID, fighters_table.c.projected == True)), # noqa
"_Fit__implants": relation(
Implant,

File diff suppressed because it is too large Load Diff

View File

@@ -66,7 +66,7 @@ BUILTINS = OrderedDict([
(-25, (_c(_t('Condenser Packs')) + _t('SlamBolt'), 23376, 0, 76624, 0)),
(-26, (_c(_t('Condenser Packs')) + _t('BlastShot'), 19820, 0, 80180, 0)),
(-27, (_c(_t('Condenser Packs')) + _t('GalvaSurge'), 80206, 0, 19794, 0)),
(-28, (_c(_t('Condenser Packs')) + '|' + _t('[T2] ElectroPunch'), 0, 50547, 0, 49453)),
(-28, (_c(_t('Condenser Packs')) + '|' + _t('[T2] ElectroPunch'), 50547, 0, 49453, 0)),
(-29, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Spike'), 0, 4, 4, 0)),
(-30, (_c(_t('Hybrid Charges')) + '|' + _t('[T2] Null'), 0, 6, 5, 0)),
@@ -216,11 +216,11 @@ class DamagePattern:
pattern.builtin = True
cls._builtins[id] = pattern
def calculateEhp(self, fit):
def calculateEhp(self, item):
ehp = {}
for (type, attr) in (('shield', 'shieldCapacity'), ('armor', 'armorHP'), ('hull', 'hp')):
rawCapacity = fit.ship.getModifiedItemAttr(attr)
ehp[type] = self.effectivify(fit, rawCapacity, type)
rawCapacity = item.getModifiedItemAttr(attr)
ehp[type] = self.effectivify(item, rawCapacity, type)
return ehp
@@ -236,10 +236,10 @@ class DamagePattern:
ereps = {}
for field in tankInfo:
if field in typeMap:
ereps[field] = self.effectivify(fit, tankInfo[field], typeMap[field])
ereps[field] = self.effectivify(fit.ship, tankInfo[field], typeMap[field])
return ereps
def effectivify(self, fit, amount, type):
def effectivify(self, item, amount, type):
type = type if type != "hull" else ""
totalDamage = sum((self.emAmount, self.thermalAmount, self.kineticAmount, self.explosiveAmount))
specificDivider = 0
@@ -248,7 +248,7 @@ class DamagePattern:
attrName = "%s%sDamageResonance" % (type, damageType.capitalize())
attrName = attrName[0].lower() + attrName[1:]
resonance = fit.ship.getModifiedItemAttr(attrName)
resonance = item.getModifiedItemAttr(attrName)
damage = getattr(self, "%sAmount" % damageType)
specificDivider += damage / float(totalDamage or 1) * resonance

View File

@@ -82,6 +82,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, Mu
self.__baseRRAmount = None
self.__miningYield = None
self.__miningWaste = None
self.__ehp = None
self.__itemModifiedAttributes = ModifiedAttributeDict()
self.__itemModifiedAttributes.original = self._item.attributes
self.__itemModifiedAttributes.overrides = self._item.overrides
@@ -287,6 +288,29 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, Mu
if delay is not None and speed is not None:
return delay / 1000.0 * speed
@property
def hp(self):
hp = {}
for (type, attr) in (('shield', 'shieldCapacity'), ('armor', 'armorHP'), ('hull', 'hp')):
hp[type] = self.getModifiedItemAttr(attr)
return hp
@property
def ehp(self):
if self.__ehp is None:
if self.owner is None or self.owner.damagePattern is None:
ehp = self.hp
else:
ehp = self.owner.damagePattern.calculateEhp(self)
self.__ehp = ehp
return self.__ehp
def calculateShieldRecharge(self):
capacity = self.getModifiedItemAttr("shieldCapacity")
rechargeRate = self.getModifiedItemAttr("shieldRechargeRate") / 1000.0
return 10 / rechargeRate * math.sqrt(0.25) * (1 - math.sqrt(0.25)) * capacity
# Had to add this to match the falloff property in modules.py
# Fscking ship scanners. If you find any other falloff attributes,
# Put them in the attrs tuple.
@@ -318,6 +342,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, Mu
self.__baseRRAmount = None
self.__miningYield = None
self.__miningWaste = None
self.__ehp = None
self.itemModifiedAttributes.clear()
self.chargeModifiedAttributes.clear()

View File

@@ -96,6 +96,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
self.__charge = None
self.__baseVolley = None
self.__miningyield = None
self.__ehp = None
self.__itemModifiedAttributes = ModifiedAttributeDict()
self.__chargeModifiedAttributes = ModifiedAttributeDict()
@@ -345,6 +346,29 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
if falloff is not None:
return falloff
@property
def hp(self):
hp = {}
for (type, attr) in (('shield', 'shieldCapacity'), ('armor', 'armorHP'), ('hull', 'hp')):
hp[type] = self.getModifiedItemAttr(attr)
return hp
@property
def ehp(self):
if self.__ehp is None:
if self.owner is None or self.owner.damagePattern is None:
ehp = self.hp
else:
ehp = self.owner.damagePattern.calculateEhp(self)
self.__ehp = ehp
return self.__ehp
def calculateShieldRecharge(self):
capacity = self.getModifiedItemAttr("shieldCapacity")
rechargeRate = self.getModifiedItemAttr("shieldRechargeRate") / 1000.0
return 10 / rechargeRate * math.sqrt(0.25) * (1 - math.sqrt(0.25)) * capacity
@validates("ID", "itemID", "chargeID", "amount")
def validator(self, key, val):
map = {
@@ -361,6 +385,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut):
def clear(self):
self.__baseVolley = None
self.__miningyield = None
self.__ehp = None
self.itemModifiedAttributes.clear()
self.chargeModifiedAttributes.clear()
[x.clear() for x in self.abilities]

View File

@@ -159,6 +159,12 @@ class Fit:
self.gangBoosts = None
self.__ecmProjectedList = []
self.commandBonuses = {}
# Reps received, as a list of (amount, cycle time in seconds)
self._hullRr = []
self._armorRr = []
self._armorRrPreSpool = []
self._armorRrFullSpool = []
self._shieldRr = []
def clearFactorReloadDependentData(self):
# Here we clear all data known to rely on cycle parameters
@@ -550,6 +556,12 @@ class Fit:
if stuff is not None and stuff != self:
stuff.clear()
self._hullRr.clear()
self._armorRr.clear()
self._armorRrPreSpool.clear()
self._armorRrFullSpool.clear()
self._shieldRr.clear()
# If this is the active fit that we are clearing, not a projected fit,
# then this will run and clear the projected ships and flag the next
# iteration to skip this part to prevent recursion.
@@ -621,7 +633,7 @@ class Fit:
"duration", value)
if warfareBuffID == 12: # Shield Burst: Shield Extension: Shield HP
self.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
self.ship.boostItemAttr("shieldCapacity", value)
if warfareBuffID == 13: # Armor Burst: Armor Energizing: Armor Resistance
for damageType in ("Em", "Thermal", "Explosive", "Kinetic"):
@@ -640,7 +652,7 @@ class Fit:
"duration", value)
if warfareBuffID == 15: # Armor Burst: Armor Reinforcement: Armor HP
self.ship.boostItemAttr("armorHP", value, stackingPenalties=True)
self.ship.boostItemAttr("armorHP", value)
if warfareBuffID == 16: # Information Burst: Sensor Optimization: Scan Resolution
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
@@ -734,7 +746,7 @@ class Fit:
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 42: # Erebus Effect Generator : Armor HP bonus
self.ship.boostItemAttr("armorHP", value, stackingPenalties=True)
self.ship.boostItemAttr("armorHP", value)
if warfareBuffID == 43: # Erebus Effect Generator : Explosive resistance bonus
for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "explosiveDamageResonance"):
@@ -756,7 +768,7 @@ class Fit:
self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 48: # Leviathan Effect Generator : Shield HP bonus
self.ship.boostItemAttr("shieldCapacity", value, stackingPenalties=True)
self.ship.boostItemAttr("shieldCapacity", value)
if warfareBuffID == 49: # Leviathan Effect Generator : EM resistance bonus
for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "emDamageResonance"):
@@ -870,6 +882,14 @@ class Fit:
if warfareBuffID == 100: # Weather_caustic_toxin_scan_resolution_bonus
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
if warfareBuffID == 2405: # Insurgency Suppression Bonus: Interdiction Range
self.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill("Navigation"),
"maxRange", value, stackingPenalties=True)
self.modules.filteredItemBoost(
lambda mod: mod.item.group.name == "Stasis Web",
"maxRange", value, stackingPenalties=True)
del self.commandBonuses[warfareBuffID]
def __resetDependentCalcs(self):
@@ -1469,7 +1489,7 @@ class Fit:
if self.damagePattern is None:
ehp = self.hp
else:
ehp = self.damagePattern.calculateEhp(self)
ehp = self.damagePattern.calculateEhp(self.ship)
self.__ehp = ehp
return self.__ehp
@@ -1478,11 +1498,11 @@ class Fit:
def tank(self):
reps = {
"passiveShield": self.calculateShieldRecharge(),
"shieldRepair": self.extraAttributes["shieldRepair"],
"armorRepair": self.extraAttributes["armorRepair"],
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"],
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"],
"hullRepair": self.extraAttributes["hullRepair"]
"shieldRepair": self.extraAttributes["shieldRepair"] + self._getAppliedShieldRr(),
"armorRepair": self.extraAttributes["armorRepair"] + self._getAppliedArmorRr(),
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"] + self._getAppliedArmorPreSpoolRr(),
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"] + self._getAppliedArmorFullSpoolRr(),
"hullRepair": self.extraAttributes["hullRepair"] + self._getAppliedHullRr()
}
return reps
@@ -1519,11 +1539,11 @@ class Fit:
if self.__sustainableTank is None:
sustainable = {
"passiveShield": self.calculateShieldRecharge(),
"shieldRepair": self.extraAttributes["shieldRepair"],
"armorRepair": self.extraAttributes["armorRepair"],
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"],
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"],
"hullRepair": self.extraAttributes["hullRepair"]
"shieldRepair": self.extraAttributes["shieldRepair"] + self._getAppliedShieldRr(),
"armorRepair": self.extraAttributes["armorRepair"] + self._getAppliedArmorRr(),
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"] + self._getAppliedArmorPreSpoolRr(),
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"] + self._getAppliedArmorFullSpoolRr(),
"hullRepair": self.extraAttributes["hullRepair"] + self._getAppliedHullRr()
}
if not self.capStable or self.factorReload:
# Map a local repairer type to the attribute it uses
@@ -1760,6 +1780,38 @@ class Fit:
mults.setdefault(stackingGroup, []).append((1 + strength / 100, None))
return calculateMultiplier(mults)
def _getAppliedHullRr(self):
return self.__getAppliedRr(self._hullRr)
def _getAppliedArmorRr(self):
return self.__getAppliedRr(self._armorRr)
def _getAppliedArmorPreSpoolRr(self):
return self.__getAppliedRr(self._armorRrPreSpool)
def _getAppliedArmorFullSpoolRr(self):
return self.__getAppliedRr(self._armorRrFullSpool)
def _getAppliedShieldRr(self):
return self.__getAppliedRr(self._shieldRr)
@staticmethod
def __getAppliedRr(rrList):
totalRaw = 0
for amount, cycleTime in rrList:
# That's right, for considerations of RR diminishing returns cycle time is rounded this way
totalRaw += amount / int(cycleTime)
RR_ADDITION = 7000
RR_MULTIPLIER = 10
appliedRr = 0
for amount, cycleTime in rrList:
rrps = amount / int(cycleTime)
modified_rrps = RR_ADDITION + (rrps * RR_MULTIPLIER)
rrps_mult = 1 - (((rrps + modified_rrps) / (totalRaw + modified_rrps)) - 1) ** 2
appliedRr += rrps_mult * amount / cycleTime
return appliedRr
def __deepcopy__(self, memo=None):
fitCopy = Fit()
# Character and owner are not copied

View File

@@ -477,7 +477,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
# Some delay attributes have non-0 default value, so we have to pick according to effects
if {'superWeaponAmarr', 'superWeaponCaldari', 'superWeaponGallente', 'superWeaponMinmatar', 'lightningWeapon'}.intersection(self.item.effects):
dmgDelay = self.getModifiedItemAttr("damageDelayDuration", 0)
elif {'doomsdayBeamDOT', 'doomsdaySlash', 'doomsdayConeDOT'}.intersection(self.item.effects):
elif {'doomsdayBeamDOT', 'doomsdaySlash', 'doomsdayConeDOT', 'debuffLance'}.intersection(self.item.effects):
dmgDelay = self.getModifiedItemAttr("doomsdayWarningDuration", 0)
else:
dmgDelay = 0
@@ -696,16 +696,22 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut, M
return False
# Check max group fitted
max = self.getModifiedItemAttr("maxGroupFitted")
if max:
current = 0 # if self.owner != fit else -1 # Disabled, see #1278
for mod in fit.modules:
if (mod.item and mod.item.groupID == self.item.groupID and
self.getModPosition(fit) != mod.getModPosition(fit)):
current += 1
# use raw value, since it seems what EVE uses. Example is FAXes with their capacitor boosters,
# which have unmodified value of 10, and modified of 1, and you can actually fit multiples
try:
max = self.item.attributes.get('maxGroupFitted').value
except AttributeError:
pass
else:
if max:
current = 0 # if self.owner != fit else -1 # Disabled, see #1278
for mod in fit.modules:
if (mod.item and mod.item.groupID == self.item.groupID and
self.getModPosition(fit) != mod.getModPosition(fit)):
current += 1
if current >= max:
return False
if current >= max:
return False
# Check this only if we're told to do so
if hardpointLimit:

View File

@@ -211,7 +211,7 @@ def getDoomsdayMult(mod, tgt, distance, tgtSigRadius):
# Disallow only against subcaps, allow against caps and tgt profiles
if tgt.isFit and not tgt.item.ship.item.requiresSkill('Capital Ships'):
return 0
damageSig = mod.getModifiedItemAttr('doomsdayDamageRadius') or mod.getModifiedItemAttr('signatureRadius')
damageSig = mod.getModifiedItemAttr('signatureRadius')
if not damageSig:
return 1
return min(1, tgtSigRadius / damageSig)

View File

@@ -66,7 +66,7 @@ class FitShieldRegenGraph(FitGraph):
('shieldAmount', '%'): lambda v, src, tgt: v / 100 * src.item.ship.getModifiedItemAttr('shieldCapacity'),
('shieldAmountT0', '%'): lambda v, src, tgt: None if v is None else v / 100 * src.item.ship.getModifiedItemAttr('shieldCapacity'),
# Needed only for "x mark" support, to convert EHP x into normalized value
('shieldAmount', 'EHP'): lambda v, src, tgt: v / src.item.damagePattern.effectivify(src.item, 1, 'shield')}
('shieldAmount', 'EHP'): lambda v, src, tgt: v / src.item.damagePattern.effectivify(src.item.ship, 1, 'shield')}
_limiters = {
'shieldAmount': lambda src, tgt: (0, src.item.ship.getModifiedItemAttr('shieldCapacity')),
'shieldAmountT0': lambda src, tgt: (0, src.item.ship.getModifiedItemAttr('shieldCapacity'))}
@@ -77,5 +77,5 @@ class FitShieldRegenGraph(FitGraph):
('shieldAmount', 'shieldRegen'): ShieldAmount2ShieldRegenGetter}
_denormalizers = {
('shieldAmount', '%'): lambda v, src, tgt: v * 100 / src.item.ship.getModifiedItemAttr('shieldCapacity'),
('shieldAmount', 'EHP'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item, v, 'shield'),
('shieldRegen', 'EHP/s'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item, v, 'shield')}
('shieldAmount', 'EHP'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item.ship, v, 'shield'),
('shieldRegen', 'EHP/s'): lambda v, src, tgt: src.item.damagePattern.effectivify(src.item.ship, v, 'shield')}

View File

@@ -114,7 +114,7 @@ class GraphFrame(AuxiliaryFrame):
newW = max(curW, bestW)
newH = max(curH, bestH)
if newW > curW or newH > curH:
newSize = wx.Size(newW, newH)
newSize = wx.Size(round(newW), round(newH))
self.SetSize(newSize)
self.SetMinSize(newSize)

View File

@@ -39,7 +39,7 @@ class VectorPicker(wx.Window):
self._directionOnly = kwargs.pop('directionOnly', False)
super().__init__(*args, **kwargs)
self._fontsize = max(1, float(kwargs.pop('fontsize', 8 / self.GetContentScaleFactor())))
self._font = wx.Font(self._fontsize, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
self._font = wx.Font(round(self._fontsize), wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False)
self._angle = 0
self.__length = 1
self._left = False
@@ -76,7 +76,7 @@ class VectorPicker(wx.Window):
self.__length = newLength
def DoGetBestSize(self):
return wx.Size(self._size, self._size)
return wx.Size(round(self._size), round(self._size))
def AcceptsFocusFromKeyboard(self):
return False
@@ -121,35 +121,37 @@ class VectorPicker(wx.Window):
radius = min(width, height) / 2 - 2
dc.SetBrush(wx.WHITE_BRUSH)
dc.DrawCircle(radius + 2, radius + 2, radius)
dc.DrawCircle(round(radius + 2), round(radius + 2), round(radius))
a = math.radians(self._angle + self._offset)
x = math.cos(a) * radius
y = math.sin(a) * radius
# See PR #2260 on why this is needed
pointRadius = 2 / self.GetContentScaleFactor() if 'wxGTK' in wx.PlatformInfo else 2
dc.DrawLine(radius + 2, radius + 2, radius + 2 + x * self._length, radius + 2 - y * self._length)
dc.DrawLine(
round(radius + 2), round(radius + 2),
round(radius + 2 + x * self._length), round(radius + 2 - y * self._length))
dc.SetBrush(wx.BLACK_BRUSH)
dc.DrawCircle(radius + 2 + x * self._length, radius + 2 - y * self._length, pointRadius)
dc.DrawCircle(round(radius + 2 + x * self._length), round(radius + 2 - y * self._length), round(pointRadius))
if self._label:
labelText = self._label
labelTextW, labelTextH = dc.GetTextExtent(labelText)
labelTextX = (radius * 2 + 4 - labelTextW) if (self._labelpos & 1) else 0
labelTextY = (radius * 2 + 4 - labelTextH) if (self._labelpos & 2) else 0
dc.DrawText(labelText, labelTextX, labelTextY)
dc.DrawText(labelText, round(labelTextX), round(labelTextY))
if not self._directionOnly:
lengthText = '%d%%' % (100 * self._length,)
lengthTextW, lengthTextH = dc.GetTextExtent(lengthText)
lengthTextX = radius + 2 + x / 2 - y / 3 - lengthTextW / 2
lengthTextY = radius + 2 - y / 2 - x / 3 - lengthTextH / 2
dc.DrawText(lengthText, lengthTextX, lengthTextY)
dc.DrawText(lengthText, round(lengthTextX), round(lengthTextY))
angleText = '%d\u00B0' % (self._angle,)
angleTextW, angleTextH = dc.GetTextExtent(angleText)
angleTextX = radius + 2 - x / 2 - angleTextW / 2
angleTextY = radius + 2 + y / 2 - angleTextH / 2
dc.DrawText(angleText, angleTextX, angleTextY)
dc.DrawText(angleText, round(angleTextX), round(angleTextY))
def OnEraseBackground(self, event):
pass

View File

@@ -199,7 +199,7 @@ def _getAutoResists(fit):
armorHp = hpData['armor']
hullHp = hpData['hull']
uniformDamagePattern = DamagePattern(emAmount=25, thermalAmount=25, kineticAmount=25, explosiveAmount=25)
ehpData = uniformDamagePattern.calculateEhp(fit)
ehpData = uniformDamagePattern.calculateEhp(fit.ship)
shieldEhp = ehpData['shield']
armorEhp = ehpData['armor']
hullEhp = ehpData['hull']

View File

@@ -212,7 +212,7 @@ class AttributeGauge(wx.Window):
for x in range(1, 20):
dc.SetBrush(wx.Brush(wx.LIGHT_GREY))
dc.SetPen(wx.Pen(wx.LIGHT_GREY))
dc.DrawRectangle(x * 10, 1, 1, rect.height)
dc.DrawRectangle(round(x * 10), 1, 1, round(rect.height))
dc.SetBrush(wx.Brush(colour))
dc.SetPen(wx.Pen(colour))
@@ -222,19 +222,19 @@ class AttributeGauge(wx.Window):
if value >= 0:
padding = (half if is_even else math.ceil(half - 1)) + 1
dc.DrawRectangle(padding, 1, w, rect.height)
dc.DrawRectangle(round(padding), 1, round(w), round(rect.height))
else:
padding = half - w + 1 if is_even else math.ceil(half) - (w - 1)
dc.DrawRectangle(padding, 1, w, rect.height)
dc.DrawRectangle(round(padding), 1, round(w), round(rect.height))
if self.leading_edge and (self.edge_on_neutral or value != 0):
dc.SetPen(wx.Pen(wx.WHITE))
dc.SetBrush(wx.Brush(wx.WHITE))
if value > 0:
dc.DrawRectangle(min(padding + w, rect.width), 1, 1, rect.height)
dc.DrawRectangle(round(min(padding + w, rect.width)), 1, 1, round(rect.height))
else:
dc.DrawRectangle(max(padding - 1, 1), 1, 1, rect.height)
dc.DrawRectangle(round(max(padding - 1, 1)), 1, 1, round(rect.height))
def OnTimer(self, event):
old_value = self._old_percentage

View File

@@ -103,10 +103,9 @@ class BitmapLoader:
pyfalog.warning("Missing icon file: {0}/{1}".format(location, filename))
return None
bmp: wx.Bitmap = img.ConvertToBitmap()
if scale > 1:
bmp.SetSize((bmp.GetWidth() // scale, bmp.GetHeight() // scale))
return bmp
return img.Scale(round(img.GetWidth() // scale), round(img.GetHeight() // scale)).ConvertToBitmap()
return img.ConvertToBitmap()
@classmethod
def loadScaledBitmap(cls, name, location, scale=0):

View File

@@ -66,6 +66,8 @@ class DroneView(Display):
"Max Range",
"Miscellanea",
"attr:maxVelocity",
"Drone HP",
"Drone Regen",
"Price",
]

View File

@@ -151,6 +151,8 @@ class FighterDisplay(d.Display):
# "Max Range",
# "Miscellanea",
"attr:maxVelocity",
"Drone HP",
"Drone Regen",
"Fighter Abilities",
"Price",
]

View File

@@ -44,7 +44,6 @@ class AddCommandFit(ContextMenuUnconditional):
def display(self, callingWindow, srcContext):
if self.mainFrame.getActiveFit() is None or len(self.__class__.commandFits) == 0 or srcContext != "commandView":
return False
return True
def getText(self, callingWindow, itmContext):
@@ -52,6 +51,8 @@ class AddCommandFit(ContextMenuUnconditional):
def addFit(self, menu, fit, includeShip=False):
label = fit.name if not includeShip else "({}) {}".format(fit.ship.item.name, fit.name)
if not label:
label = ' '
id = ContextMenuUnconditional.nextID()
self.fitMenuItemIds[id] = fit
menuItem = wx.MenuItem(menu, id, label)

View File

@@ -123,9 +123,9 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
data.groups[_t('Abyssal Weather')] = self.getAbyssalWeather()
data.groups[_t('Sansha Incursion')] = self.getEffectBeacons(
_t('ContextMenu|ProjectedEffectManipulation|Sansha Incursion'))
data.groups[_t('Triglavian Invasion')] = self.getEffectBeacons(
_t('ContextMenu|ProjectedEffectManipulation|Triglavian Invasion'))
data.groups[_t('Triglavian Invasion')].groups[_t('Destructible Beacons')] = self.getDestructibleBeacons()
data.groups[_t('Triglavian Invasion')] = self.getInvasionBeacons()
# data.groups[_t('Pirate Insurgency')] = self.getEffectBeacons(
# _t('ContextMenu|ProjectedEffectManipulation|Insurgency'))
return data
def getEffectBeacons(self, *groups, extra_garbage=()):
@@ -233,5 +233,19 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
data.sort()
return data
def getInvasionBeacons(self):
data = self.getDestructibleBeacons()
# Turnur weather
item = Market.getInstance().getItem(74002)
data.items.append(Entry(item.ID, item.name, item.name))
return data
def getInsurgencyBeacons(self):
data = self.getDestructibleBeacons()
# Suppression Interdiction Range Beacon
item = Market.getInstance().getItem(79839)
data.items.append(Entry(item.ID, item.name, item.name))
return data
AddEnvironmentEffect.register()

View File

@@ -10,7 +10,10 @@ class AutoListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowH
wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
listmix.ListCtrlAutoWidthMixin.__init__(self)
listmix.ListRowHighlighter.__init__(self)
if wx.SystemSettings.GetAppearance().IsDark():
listcol = wx.SystemSettings.GetColour(wx.SYS_COLOUR_LISTBOX)
highlight = listcol.ChangeLightness(110)
listmix.ListRowHighlighter.SetHighlightColor(self, highlight)
class AutoListCtrlNoHighlight(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter):
def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):

View File

@@ -36,6 +36,8 @@ class ItemCompare(wx.Panel):
self.item = item
self.items = sorted(items, key=defaultSort)
self.attrs = {}
self.HighlightOn = wx.Colour(255, 255, 0, wx.ALPHA_OPAQUE)
self.highlightedNames = []
# get a dict of attrName: attrInfo of all unique attributes across all items
for item in self.items:
@@ -88,6 +90,21 @@ class ItemCompare(wx.Panel):
self.toggleViewBtn.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleViewMode)
self.Bind(wx.EVT_LIST_COL_CLICK, self.SortCompareCols)
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.HighlightRow)
def HighlightRow(self, event):
itemIdx = event.GetIndex()
name = self.paramList.GetItem(itemIdx).Text
if name in self.highlightedNames:
self.highlightedNames.remove(name)
else:
self.highlightedNames.append(name)
self.Freeze()
self.paramList.ClearAll()
self.PopulateList()
self.Thaw()
event.Skip()
def SortCompareCols(self, event):
self.Freeze()
self.paramList.ClearAll()
@@ -155,6 +172,8 @@ class ItemCompare(wx.Panel):
self.paramList.InsertColumn(len(self.attrs) + 1, _t("Price"))
self.paramList.SetColumnWidth(len(self.attrs) + 1, 60)
toHighlight = []
for item in self.items:
i = self.paramList.InsertItem(self.paramList.GetItemCount(), item.name)
for x, attr in enumerate(self.attrs.keys()):
@@ -172,10 +191,19 @@ class ItemCompare(wx.Panel):
# Add prices
self.paramList.SetItem(i, len(self.attrs) + 1, formatAmount(item.price.price, 3, 3, 9, currency=True) if item.price.price else "")
if item.name in self.highlightedNames:
toHighlight.append(i)
self.paramList.RefreshRows()
self.Layout()
# Highlight after layout, otherwise colors are getting overwritten
for itemIdx in toHighlight:
listItem = self.paramList.GetItem(itemIdx)
listItem.SetBackgroundColour(self.HighlightOn)
listItem.SetFont(listItem.GetFont().MakeBold())
self.paramList.SetItem(listItem)
@staticmethod
def TranslateValueUnit(value, unitName, unitDisplayName):
def itemIDCallback():

View File

@@ -253,8 +253,8 @@ class PFSearchBox(wx.Window):
else:
spad = 0
dc.DrawBitmap(self.searchBitmapShadow, self.searchButtonX + 1, self.searchButtonY + 1)
dc.DrawBitmap(self.searchBitmap, self.searchButtonX + spad, self.searchButtonY + spad)
dc.DrawBitmap(self.searchBitmapShadow, round(self.searchButtonX + 1), round(self.searchButtonY + 1))
dc.DrawBitmap(self.searchBitmap, round(self.searchButtonX + spad), round(self.searchButtonY + spad))
if self.isCancelButtonVisible:
if self.cancelBitmap:
@@ -262,8 +262,8 @@ class PFSearchBox(wx.Window):
cpad = 1
else:
cpad = 0
dc.DrawBitmap(self.cancelBitmapShadow, self.cancelButtonX + 1, self.cancelButtonY + 1)
dc.DrawBitmap(self.cancelBitmap, self.cancelButtonX + cpad, self.cancelButtonY + cpad)
dc.DrawBitmap(self.cancelBitmapShadow, round(self.cancelButtonX + 1), round(self.cancelButtonY + 1))
dc.DrawBitmap(self.cancelBitmap, round(self.cancelButtonX + cpad), round(self.cancelButtonY + cpad))
dc.SetPen(wx.Pen(sepColor, 1))
dc.DrawLine(0, rect.height - 1, rect.width, rect.height - 1)

View File

@@ -40,7 +40,7 @@ class PFGeneralPref(PreferenceView):
langSizer = wx.BoxSizer(wx.HORIZONTAL)
self.langChoices = sorted([langInfo for lang, langInfo in LocaleSettings.supported_langauges().items()], key=lambda x: x.Description)
self.langChoices = sorted([langInfo for lang, langInfo in LocaleSettings.supported_languages().items()], key=lambda x: x.Description)
pyfaLangsEnabled = bool(self.langChoices)
if pyfaLangsEnabled:
@@ -64,7 +64,7 @@ class PFGeneralPref(PreferenceView):
langBox.Add(hl.HyperLinkCtrl(panel, -1,
_t("Interested in helping with translations?"),
URL="https://github.com/pyfa-org/Pyfa/blob/master/locale/README.md"
), 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 15)
), 0, wx.LEFT, 15)
else:
self.stLangLabel = wx.StaticText(panel, wx.ID_ANY, _t("Pyfa language selection disabled. Please check if .mo files have been generated.\nRefer to locale/README.md for info."), wx.DefaultPosition, wx.DefaultSize, 0)
self.stLangLabel.Wrap(-1)
@@ -93,7 +93,7 @@ class PFGeneralPref(PreferenceView):
langBox.Add(wx.StaticText(panel, wx.ID_ANY,
_t("Auto will use the same language pyfa uses if available, otherwise English"),
wx.DefaultPosition,
wx.DefaultSize, 0), 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 15)
wx.DefaultSize, 0), 0, wx.LEFT, 15)
self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, _t("Use global character"), wx.DefaultPosition, wx.DefaultSize,
0)

View File

@@ -104,14 +104,14 @@ class CategoryItem(SFBrowserItem):
textColor = colorUtils.GetSuitable(windowColor, 1)
mdc.SetTextForeground(textColor)
mdc.DrawBitmap(self.dropShadowBitmap, self.shipBmpx + 1, self.shipBmpy + 1)
mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0)
mdc.DrawBitmap(self.dropShadowBitmap, round(self.shipBmpx + 1), round(self.shipBmpy + 1))
mdc.DrawBitmap(self.shipBmp, round(self.shipBmpx), round(self.shipBmpy), 0)
mdc.SetFont(self.fontBig)
categoryName, fittings = self.fittingInfo
mdc.DrawText(categoryName, self.catx, self.caty)
mdc.DrawText(categoryName, round(self.catx), round(self.caty))
# =============================================================================

View File

@@ -416,9 +416,18 @@ class FitItem(SFItem.SFBrowserItem):
if self.dragging:
if not self.dragged:
if self.dragMotionTrigger < 0:
if not self.dragTLFBmp:
tdc = wx.MemoryDC()
bmpWidth = self.toolbarx if self.toolbarx < 200 else 200
self.dragTLFBmp = wx.Bitmap(round(bmpWidth), round(self.GetRect().height))
tdc.SelectObject(self.dragTLFBmp)
tdc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)))
tdc.DrawRectangle(0, 0, bmpWidth, self.GetRect().height)
self.DrawItem(tdc)
tdc.SelectObject(wx.NullBitmap)
if not self.HasCapture():
self.CaptureMouse()
self.dragWindow = PFBitmapFrame(self, pos, self.dragTLFBmp)
self.dragWindow = PFBitmapFrame(self, pos, self.dragTLFBmp)
self.dragWindow.Show()
self.dragged = True
self.dragMotionTrigger = self.dragMotionTrail
@@ -493,9 +502,9 @@ class FitItem(SFItem.SFBrowserItem):
else:
shipEffBk = self.shipEffBk
mdc.DrawBitmap(shipEffBk, self.shipEffx, self.shipEffy, 0)
mdc.DrawBitmap(shipEffBk, round(self.shipEffx), round(self.shipEffy), 0)
mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0)
mdc.DrawBitmap(self.shipBmp, round(self.shipBmpx), round(self.shipBmpy), 0)
mdc.SetFont(self.fontNormal)
@@ -504,26 +513,21 @@ class FitItem(SFItem.SFBrowserItem):
pfdate = drawUtils.GetPartialText(mdc, fitLocalDate,
self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw)
mdc.DrawText(pfdate, self.textStartx, self.timestampy)
mdc.DrawText(pfdate, round(self.textStartx), round(self.timestampy))
mdc.SetFont(self.fontSmall)
mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery)
mdc.DrawText(self.toolbar.hoverLabel, round(self.thoverx), round(self.thovery))
mdc.SetFont(self.fontBig)
psname = drawUtils.GetPartialText(mdc, self.fitName,
self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw)
mdc.DrawText(psname, self.textStartx, self.fitNamey)
mdc.DrawText(psname, round(self.textStartx), round(self.fitNamey))
if self.tcFitName.IsShown():
self.AdjustControlSizePos(self.tcFitName, self.textStartx, self.toolbarx - self.editWidth - self.padding)
tdc = wx.MemoryDC()
self.dragTLFBmp = wx.Bitmap((self.toolbarx if self.toolbarx < 200 else 200), rect.height, 24)
tdc.SelectObject(self.dragTLFBmp)
tdc.Blit(0, 0, (self.toolbarx if self.toolbarx < 200 else 200), rect.height, mdc, 0, 0, wx.COPY)
tdc.SelectObject(wx.NullBitmap)
def AdjustControlSizePos(self, editCtl, start, end):
fnEditSize = editCtl.GetSize()

View File

@@ -231,7 +231,7 @@ class NavigationPanel(SFItem.SFBrowserItem):
self.toolbar.SetPosition((self.toolbarx, self.toolbary))
mdc.SetFont(self.fontSmall)
mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery)
mdc.DrawText(self.toolbar.hoverLabel, round(self.thoverx), round(self.thovery))
mdc.SetPen(wx.Pen(sepColor, 1))
mdc.DrawLine(0, rect.height - 1, rect.width, rect.height - 1)

View File

@@ -55,7 +55,7 @@ class PFBitmapFrame(wx.Frame):
# todo: evaluate wx.DragImage, might make this class obsolete, however might also lose our customizations
# (like the sexy fade-in animation)
rect = self.GetRect()
canvas = wx.Bitmap(rect.width, rect.height)
canvas = wx.Bitmap(round(rect.width), round(rect.height))
# todo: convert to context manager after updating to wxPython >v4.0.1 (4.0.1 has a bug, see #1421)
# See #1418 for discussion
mdc = wx.BufferedPaintDC(self)
@@ -63,4 +63,4 @@ class PFBitmapFrame(wx.Frame):
mdc.DrawBitmap(self.bitmap, 0, 0)
mdc.SetPen(wx.Pen("#000000", width=1))
mdc.SetBrush(wx.TRANSPARENT_BRUSH)
mdc.DrawRectangle(0, 0, rect.width, rect.height)
mdc.DrawRectangle(0, 0, round(rect.width), round(rect.height))

View File

@@ -57,7 +57,7 @@ class PFListPane(wx.ScrolledWindow):
posy = self.GetScrollPos(wx.VERTICAL)
posy -= self.itemsHeight
self.Scroll(0, posy)
self.Scroll(0, round(posy))
event.Skip()
@@ -65,7 +65,7 @@ class PFListPane(wx.ScrolledWindow):
posy = self.GetScrollPos(wx.VERTICAL)
posy += self.itemsHeight
self.Scroll(0, posy)
self.Scroll(0, round(posy))
event.Skip()
@@ -109,7 +109,7 @@ class PFListPane(wx.ScrolledWindow):
# if we need to adjust
if new_vs_x != -1 or new_vs_y != -1:
self.Scroll(new_vs_x, new_vs_y)
self.Scroll(round(new_vs_x), round(new_vs_y))
def AddWidget(self, widget):
widget.Reparent(self)

View File

@@ -68,7 +68,7 @@ class RaceSelector(wx.Window):
img = img.Rotate90(False)
img.Replace(0, 0, 0, sysTextColour[0], sysTextColour[1], sysTextColour[2])
if layout == wx.VERTICAL:
img = img.Scale(self.minWidth, 8, wx.IMAGE_QUALITY_HIGH)
img = img.Scale(round(self.minWidth), 8, wx.IMAGE_QUALITY_HIGH)
self.bmpArrow = wx.Bitmap(img)
@@ -194,25 +194,25 @@ class RaceSelector(wx.Window):
bmp = wx.Bitmap(img)
if self.layout == wx.VERTICAL:
mdc.DrawBitmap(dropShadow, rect.width - self.buttonsPadding - bmp.GetWidth() + 1, y + 1)
mdc.DrawBitmap(bmp, rect.width - self.buttonsPadding - bmp.GetWidth(), y)
mdc.DrawBitmap(dropShadow, round(rect.width - self.buttonsPadding - bmp.GetWidth() + 1), round(y + 1))
mdc.DrawBitmap(bmp, round(rect.width - self.buttonsPadding - bmp.GetWidth()), round(y))
y += raceBmp.GetHeight() + self.buttonsPadding
mdc.SetPen(wx.Pen(sepColor, 1))
mdc.DrawLine(rect.width - 1, 0, rect.width - 1, rect.height)
else:
mdc.DrawBitmap(dropShadow, x + 1, self.buttonsPadding + 1)
mdc.DrawBitmap(bmp, x, self.buttonsPadding)
mdc.DrawBitmap(dropShadow, round(x + 1), round(self.buttonsPadding + 1))
mdc.DrawBitmap(bmp, round(x), round(self.buttonsPadding))
x += raceBmp.GetWidth() + self.buttonsPadding
mdc.SetPen(wx.Pen(sepColor, 1))
mdc.DrawLine(0, 0, rect.width, 0)
if self.direction < 1:
if self.layout == wx.VERTICAL:
mdc.DrawBitmap(self.bmpArrow, -2, (rect.height - self.bmpArrow.GetHeight()) / 2)
mdc.DrawBitmap(self.bmpArrow, -2, round((rect.height - self.bmpArrow.GetHeight()) / 2))
else:
mdc.SetPen(wx.Pen(sepColor, 1))
mdc.DrawLine(0, 0, rect.width, 0)
mdc.DrawBitmap(self.bmpArrow, (rect.width - self.bmpArrow.GetWidth()) / 2, -2)
mdc.DrawBitmap(self.bmpArrow, round((rect.width - self.bmpArrow.GetWidth()) / 2), -2)
def OnTimer(self, event):
if event.GetId() == self.animTimerID:

View File

@@ -233,8 +233,8 @@ class PFToolbar:
bmpWidth = bmp.GetWidth()
pdc.DrawBitmap(dropShadowBmp, bx + self.padding / 2, self.toolbarY + self.padding / 2)
pdc.DrawBitmap(bmp, tbx, by)
pdc.DrawBitmap(dropShadowBmp, round(bx + self.padding / 2), round(self.toolbarY + self.padding / 2))
pdc.DrawBitmap(bmp, round(tbx), round(by))
bx += bmpWidth + self.padding

View File

@@ -247,12 +247,12 @@ class ShipItem(SFItem.SFBrowserItem):
else:
shipEffBk = self.shipEffBk
mdc.DrawBitmap(shipEffBk, self.shipEffx, self.shipEffy, 0)
mdc.DrawBitmap(shipEffBk, round(self.shipEffx), round(self.shipEffy), 0)
mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0)
mdc.DrawBitmap(self.shipBmp, round(self.shipBmpx), round(self.shipBmpy), 0)
mdc.DrawBitmap(self.raceDropShadowBmp, self.raceBmpx + 1, self.raceBmpy + 1)
mdc.DrawBitmap(self.raceBmp, self.raceBmpx, self.raceBmpy)
mdc.DrawBitmap(self.raceDropShadowBmp, round(self.raceBmpx + 1), round(self.raceBmpy + 1))
mdc.DrawBitmap(self.raceBmp, round(self.raceBmpx), round(self.raceBmpy))
shipName, shipTrait, fittings = self.shipFittingInfo
@@ -264,17 +264,17 @@ class ShipItem(SFItem.SFBrowserItem):
fformat = "%d fits"
mdc.SetFont(self.fontNormal)
mdc.DrawText(fformat % fittings if fittings > 0 else fformat, self.textStartx, self.fittingsy)
mdc.DrawText(fformat % fittings if fittings > 0 else fformat, round(self.textStartx), round(self.fittingsy))
mdc.SetFont(self.fontSmall)
mdc.DrawText(self.toolbar.hoverLabel, self.thoverx, self.thovery)
mdc.DrawText(self.toolbar.hoverLabel, round(self.thoverx), round(self.thovery))
mdc.SetFont(self.fontBig)
psname = drawUtils.GetPartialText(mdc, shipName,
self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw)
mdc.DrawText(psname, self.textStartx, self.shipNamey)
mdc.DrawText(psname, round(self.textStartx), round(self.shipNamey))
if self.tcFitName.IsShown():
self.AdjustControlSizePos(self.tcFitName, self.textStartx, self.toolbarx - self.editWidth - self.padding)

View File

@@ -146,8 +146,8 @@ class ResistancesViewFull(StatsView):
lbl = PyGauge(contentPanel, font, 100)
lbl.SetMinSize((48, 16))
lbl.SetBackgroundColour(wx.Colour(bc[0], bc[1], bc[2]))
lbl.SetBarColour(wx.Colour(fc[0], fc[1], fc[2]))
lbl.SetBackgroundColour(wx.Colour(round(bc[0]), round(bc[1]), round(bc[2])))
lbl.SetBarColour(wx.Colour(round(fc[0]), round(fc[1]), round(fc[2])))
lbl.SetBarGradient()
lbl.SetFractionDigits(1)

View File

@@ -127,7 +127,9 @@ class TargetingMiscViewMinimal(StatsView):
("specialSalvageHoldCapacity", _t("Salvage hold")),
("specialCommandCenterHoldCapacity", _t("Command center hold")),
("specialPlanetaryCommoditiesHoldCapacity", _t("Planetary goods hold")),
("specialQuafeHoldCapacity", _t("Quafe hold"))))
("specialQuafeHoldCapacity", _t("Quafe hold")),
("specialMobileDepotHoldCapacity", _t("Mobile depot hold")),
))
cargoValues = {
"main": lambda: fit.ship.getModifiedItemAttr("capacity"),
@@ -148,7 +150,8 @@ class TargetingMiscViewMinimal(StatsView):
"specialSalvageHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialSalvageHoldCapacity"),
"specialCommandCenterHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialCommandCenterHoldCapacity"),
"specialPlanetaryCommoditiesHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialPlanetaryCommoditiesHoldCapacity"),
"specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity")
"specialQuafeHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialQuafeHoldCapacity"),
"specialMobileDepotHoldCapacity": lambda: fit.ship.getModifiedItemAttr("specialMobileDepotHoldCapacity"),
}
stats = (("labelTargets", {"main": lambda: fit.maxTargets}, 3, 0, 0, ""),

View File

@@ -0,0 +1,87 @@
# =============================================================================
# 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/>.
# =============================================================================
# noinspection PyPackageRequirements
import wx
import gui.mainFrame
from eos.saveddata.drone import Drone
from eos.saveddata.fighter import Fighter
from service.attribute import Attribute
from gui.viewColumn import ViewColumn
from gui.bitmap_loader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
_t = wx.GetTranslation
class DroneEhpColumn(ViewColumn):
name = "Drone HP"
def __init__(self, fittingView, params=None):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
if params is None:
params = {"showIcon": True, "displayName": False}
ViewColumn.__init__(self, fittingView)
sAttr = Attribute.getInstance()
info = sAttr.getAttributeInfo("shieldCapacity")
self.info = info
if params["showIcon"]:
iconFile = info.iconID
if iconFile:
self.imageId = fittingView.imageList.GetImageIndex(iconFile, "icons")
self.bitmap = BitmapLoader.getBitmap(iconFile, "icons")
else:
self.imageId = -1
self.mask = wx.LIST_MASK_IMAGE
else:
self.imageId = -1
if params["displayName"] or self.imageId == -1:
self.columnText = info.displayName if info.displayName != "" else info.name
self.mask |= wx.LIST_MASK_TEXT
def getText(self, stuff):
if not isinstance(stuff, (Drone, Fighter)):
return ""
if self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective:
ehp = sum(stuff.ehp.values())
else:
ehp = sum(stuff.hp.values())
return formatAmount(ehp, 3, 0, 9)
def getImageId(self, mod):
return -1
def getParameters(self):
return ("displayName", bool, False), ("showIcon", bool, True)
def getToolTip(self, stuff):
if not isinstance(stuff, (Drone, Fighter)):
return ""
if self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective:
return _t("Effective HP")
else:
return _t("HP")
DroneEhpColumn.register()

View File

@@ -0,0 +1,81 @@
# =============================================================================
# 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/>.
# =============================================================================
# noinspection PyPackageRequirements
import wx
import gui.mainFrame
from eos.saveddata.drone import Drone
from eos.saveddata.fighter import Fighter
from gui.viewColumn import ViewColumn
from gui.bitmap_loader import BitmapLoader
from gui.utils.numberFormatter import formatAmount
_t = wx.GetTranslation
class DroneRegenColumn(ViewColumn):
name = "Drone Regen"
def __init__(self, fittingView, params=None):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
if params is None:
params = {"showIcon": True, "displayName": False}
ViewColumn.__init__(self, fittingView)
if params["showIcon"]:
self.imageId = fittingView.imageList.GetImageIndex("shieldPassive_small", "gui")
self.bitmap = BitmapLoader.getBitmap("shieldPassive_small", "gui")
self.mask = wx.LIST_MASK_IMAGE
else:
self.imageId = -1
if params["displayName"] or self.imageId == -1:
self.columnText = _("Misc data")
self.mask |= wx.LIST_MASK_TEXT
def getText(self, stuff):
if not isinstance(stuff, (Drone, Fighter)):
return ""
regen = stuff.calculateShieldRecharge()
if (
self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective
and stuff.owner and stuff.owner.damagePattern is not None
):
regen = stuff.owner.damagePattern.effectivify(stuff, regen, 'shield')
return '{}/s'.format(formatAmount(regen, 3, 0, 9))
def getImageId(self, mod):
return -1
def getParameters(self):
return ("displayName", bool, False), ("showIcon", bool, True)
def getToolTip(self, stuff):
if not isinstance(stuff, (Drone, Fighter)):
return ""
if self.mainFrame.statsPane.nameViewMap["resistancesViewFull"].showEffective:
return _t("Effective Shield Regeneration")
else:
return _t("Shield Regeneration")
DroneRegenColumn.register()

View File

@@ -167,7 +167,7 @@ class Miscellanea(ViewColumn):
text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3))
tooltip = "Energy neutralization per second"
return text, tooltip
elif itemGroup == "Salvager":
elif itemGroup in ("Salvager", "Salvage Drone"):
chance = stuff.getModifiedItemAttr("accessDifficultyBonus")
if not chance:
return "", None
@@ -182,6 +182,13 @@ class Miscellanea(ViewColumn):
text = "{0} | {1}".format(formatAmount(strength, 3, 0, 3), formatAmount(coherence, 3, 0, 3))
tooltip = "Virus strength and coherence"
return text, tooltip
elif itemGroup == "Damage Control":
duration = stuff.getModifiedItemAttr("duration")
if not duration:
return "", None
text = "{0}s".format(formatAmount(duration / 1000, 3, 0, 0))
tooltip = "Assault ability duration"
return text, tooltip
elif itemGroup in ("Warp Scrambler", "Warp Core Stabilizer", "Structure Warp Scrambler"):
scramStr = stuff.getModifiedItemAttr("warpScrambleStrength")
if not scramStr:

View File

@@ -895,7 +895,7 @@ class FittingView(d.Display):
opts.m_labelText = name
if imgId != -1:
opts.m_labelBitmap = wx.Bitmap(isize, isize)
opts.m_labelBitmap = wx.Bitmap(round(isize), round(isize))
width = render.DrawHeaderButton(self, tdc, (0, 0, 16, 16), sortArrow=wx.HDR_SORT_ICON_NONE, params=opts)
@@ -911,7 +911,7 @@ class FittingView(d.Display):
maxWidth += columnsWidths[i]
mdc = wx.MemoryDC()
mbmp = wx.Bitmap(maxWidth, maxRowHeight * rows + padding * 4 + headerSize)
mbmp = wx.Bitmap(round(maxWidth), round(maxRowHeight * rows + padding * 4 + headerSize))
mdc.SelectObject(mbmp)
@@ -956,7 +956,7 @@ class FittingView(d.Display):
cx = padding
if slotMap[st.slot]:
mdc.DrawRectangle(cx, cy, maxWidth - cx, maxRowHeight)
mdc.DrawRectangle(round(cx), round(cy), round(maxWidth - cx), round(maxRowHeight))
for i, col in enumerate(self.activeColumns):
if i > maxColumns:

View File

@@ -371,7 +371,7 @@ class SkillTreeView(wx.Panel):
bSizerButtons.AddStretchSpacer()
importExport = ((_t("Import skills from clipboard"), wx.ART_FILE_OPEN, "import"),
(_t("Export skills from clipboard"), wx.ART_FILE_SAVE_AS, "export"))
(_t("Export skills to clipboard"), wx.ART_FILE_SAVE_AS, "export"))
for tooltip, art, attr in importExport:
bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON)
@@ -446,6 +446,7 @@ class SkillTreeView(wx.Panel):
text = fromClipboard().strip()
if text:
sCharacter = Character.getInstance()
char = self.charEditor.entityEditor.getActiveEntity()
try:
lines = text.splitlines()
@@ -455,7 +456,7 @@ class SkillTreeView(wx.Panel):
skill, level = s.rsplit(None, 1)[0], arabicOrRomanToInt(s.rsplit(None, 1)[1])
skill = char.getSkill(skill)
if skill:
skill.setLevel(level, ignoreRestrict=True)
sCharacter.changeLevel(char.ID, skill.item.ID, level)
except (KeyboardInterrupt, SystemExit):
raise
@@ -516,7 +517,10 @@ class SkillTreeView(wx.Panel):
def populateSkillTreeSkillSearch(self, event=None):
sChar = Character.getInstance()
char = self.charEditor.entityEditor.getActiveEntity()
search = self.searchInput.GetLineText(0)
try:
search = self.searchInput.GetLineText(0)
except AttributeError:
search = self.searchInput.GetValue()
root = self.root
tree = self.skillTreeListCtrl
@@ -530,7 +534,7 @@ class SkillTreeView(wx.Panel):
iconId = self.skillBookDirtyImageId
childId = tree.AppendItem(root, name, iconId, data=('skill', id))
tree.SetItemText(childId, 1, _t("Level {}d").format(int(level)) if isinstance(level, float) else level)
tree.SetItemText(childId, 1, _t("Level {}").format(int(level)) if isinstance(level, float) else level)
def populateSkillTree(self, event=None):
sChar = Character.getInstance()
@@ -588,7 +592,6 @@ class SkillTreeView(wx.Panel):
iconId = self.skillBookDirtyImageId
childId = tree.AppendItem(root, name, iconId, data=('skill', id))
tree.SetItemText(childId, 1, _t("Level {}").format(int(level)) if isinstance(level, float) else level)
def spawnMenu(self, event):

View File

@@ -22,6 +22,7 @@ import traceback
# noinspection PyPackageRequirements
import wx
import roman
from logbook import Logger
import config
@@ -105,6 +106,9 @@ class CharacterSelection(wx.Panel):
exportItem = menu.Append(wx.ID_ANY, _t("Copy Missing Skills"))
self.Bind(wx.EVT_MENU, self.exportSkills, exportItem)
exportItem = menu.Append(wx.ID_ANY, _t("Copy Missing Skills (EVEMon)"))
self.Bind(wx.EVT_MENU, self.exportSkillsEveMon, exportItem)
self.PopupMenu(menu, pos)
event.Skip()
@@ -264,6 +268,15 @@ class CharacterSelection(wx.Panel):
toClipboard(list)
def exportSkillsEveMon(self, evt):
skillsMap = self._buildSkillsTooltipCondensed(self.reqs, skillsMap={})
list = ""
for key in sorted(skillsMap):
list += "%s %s\n" % (key, roman.toRoman(skillsMap[key][0]))
toClipboard(list)
def _buildSkillsTooltip(self, reqs, currItem="", tabulationLevel=0):
tip = ""
sCharacter = Character.getInstance()

View File

@@ -514,7 +514,7 @@ class _TabRenderer:
Creates the tab background bitmap based upon calculated dimension values
and modified bitmaps via InitBitmaps()
"""
bk_bmp = wx.Bitmap(self.tab_width, self.tab_height)
bk_bmp = wx.Bitmap(round(self.tab_width), round(self.tab_height))
mdc = wx.MemoryDC()
mdc.SelectObject(bk_bmp)
@@ -525,16 +525,16 @@ class _TabRenderer:
# convert middle bitmap and scale to tab width
cm = self.ctab_middle_bmp.ConvertToImage()
mimg = cm.Scale(self.content_width, self.ctab_middle.GetHeight(),
mimg = cm.Scale(round(self.content_width), round(self.ctab_middle.GetHeight()),
wx.IMAGE_QUALITY_NORMAL)
mbmp = wx.Bitmap(mimg)
# draw middle bitmap, offset by left
mdc.DrawBitmap(mbmp, self.left_width, 0)
mdc.DrawBitmap(mbmp, round(self.left_width), 0)
# draw right bitmap offset by left + middle
mdc.DrawBitmap(self.ctab_right_bmp,
self.content_width + self.left_width, 0)
round(self.content_width + self.left_width), 0)
mdc.SelectObject(wx.NullBitmap)
@@ -555,7 +555,7 @@ class _TabRenderer:
+ self.left_width \
- self.ctab_close_bmp.GetWidth() / 2
y_offset = (self.tab_height - self.ctab_close_bmp.GetHeight()) / 2
self.close_region.Offset(x_offset, y_offset)
self.close_region.Offset(round(x_offset), round(y_offset))
def InitColors(self):
"""Determines colors used for tab, based on system settings"""
@@ -573,7 +573,7 @@ class _TabRenderer:
height = self.tab_height
canvas = wx.Bitmap(self.tab_width, self.tab_height, 24)
canvas = wx.Bitmap(round(self.tab_width), round(self.tab_height), 24)
mdc = wx.MemoryDC()
@@ -590,8 +590,8 @@ class _TabRenderer:
# Draw tab icon
mdc.DrawBitmap(
bmp,
self.left_width + self.padding - bmp.GetWidth() / 2,
(height - bmp.GetHeight()) / 2)
round(self.left_width + self.padding - bmp.GetWidth() / 2),
round((height - bmp.GetHeight()) / 2))
# draw close button
if self.closeable:
@@ -604,8 +604,8 @@ class _TabRenderer:
mdc.DrawBitmap(
cbmp,
self.content_width + self.left_width - cbmp.GetWidth() / 2,
(height - cbmp.GetHeight()) / 2)
round(self.content_width + self.left_width - cbmp.GetWidth() / 2),
round((height - cbmp.GetHeight()) / 2))
mdc.SelectObject(wx.NullBitmap)
@@ -640,7 +640,7 @@ class _TabRenderer:
# draw text (with no ellipses)
text = draw.GetPartialText(dc, self.text, maxsize, "")
tx, ty = dc.GetTextExtent(text)
dc.DrawText(text, text_start + self.padding, height / 2 - ty / 2)
dc.DrawText(text, round(text_start + self.padding), round(height / 2 - ty / 2))
def __repr__(self):
return "_TabRenderer(text={}, disabled={}) at {}".format(
@@ -1005,7 +1005,7 @@ class _TabsContainer(wx.Panel):
region = tab.GetCloseButtonRegion()
posx, posy = tab.GetPosition()
region.Offset(posx, posy)
region.Offset(round(posx), round(posy))
if region.Contains(x, y):
index = self.tabs.index(tab)
@@ -1036,7 +1036,7 @@ class _TabsContainer(wx.Panel):
region = self.add_button.GetRegion()
ax, ay = self.add_button.GetPosition()
region.Offset(ax, ay)
region.Offset(round(ax), round(ay))
if region.Contains(x, y):
ev = PageAdding()
@@ -1058,7 +1058,7 @@ class _TabsContainer(wx.Panel):
for tab in self.tabs:
region = tab.GetCloseButtonRegion()
posx, posy = tab.GetPosition()
region.Offset(posx, posy)
region.Offset(round(posx), round(posy))
if region.Contains(x, y):
if not tab.GetCloseButtonHoverStatus():
@@ -1093,7 +1093,7 @@ class _TabsContainer(wx.Panel):
tabRegion = tab.GetTabRegion()
tabPos = tab.GetPosition()
tabPosX, tabPosY = tabPos
tabRegion.Offset(tabPosX, tabPosY)
tabRegion.Offset(round(tabPosX), round(tabPosY))
if tabRegion.Contains(x, y):
return True
@@ -1166,7 +1166,7 @@ class _TabsContainer(wx.Panel):
region = self.add_button.GetRegion()
ax, ay = self.add_button.GetPosition()
region.Offset(ax, ay)
region.Offset(round(ax), round(ay))
if region.Contains(x, y):
if not self.add_button.IsHighlighted():
@@ -1198,7 +1198,7 @@ class _TabsContainer(wx.Panel):
if self.show_add_button:
ax, ay = self.add_button.GetPosition()
mdc.DrawBitmap(self.add_button.Render(), ax, ay, True)
mdc.DrawBitmap(self.add_button.Render(), round(ax), round(ay), True)
for i in range(len(self.tabs) - 1, -1, -1):
tab = self.tabs[i]
@@ -1206,14 +1206,14 @@ class _TabsContainer(wx.Panel):
if not tab.IsSelected():
# drop shadow first
mdc.DrawBitmap(self.fxBmps[tab], posx, posy, True)
mdc.DrawBitmap(self.fxBmps[tab], round(posx), (posy), True)
bmp = tab.Render()
img = bmp.ConvertToImage()
img = img.AdjustChannels(1, 1, 1, 0.85)
bmp = wx.Bitmap(img)
mdc.DrawBitmap(bmp, posx, posy, True)
mdc.DrawBitmap(bmp, round(posx), (posy), True)
mdc.SetDeviceOrigin(posx, posy)
mdc.SetDeviceOrigin(round(posx), round(posy))
tab.DrawText(mdc)
mdc.SetDeviceOrigin(0, 0)
else:
@@ -1224,7 +1224,7 @@ class _TabsContainer(wx.Panel):
if selected:
posx, posy = selected.GetPosition()
# drop shadow first
mdc.DrawBitmap(self.fxBmps[selected], posx, posy, True)
mdc.DrawBitmap(self.fxBmps[selected], round(posx), round(posy), True)
bmp = selected.Render()
@@ -1233,9 +1233,9 @@ class _TabsContainer(wx.Panel):
img = img.AdjustChannels(1.2, 1.2, 1.2, 0.7)
bmp = wx.Bitmap(img)
mdc.DrawBitmap(bmp, posx, posy, True)
mdc.DrawBitmap(bmp, round(posx), round(posy), True)
mdc.SetDeviceOrigin(posx, posy)
mdc.SetDeviceOrigin(round(posx), round(posy))
selected.DrawText(mdc)
mdc.SetDeviceOrigin(0, 0)
@@ -1501,7 +1501,7 @@ class PFNotebookPagePreview(wx.Frame):
def OnWindowPaint(self, event):
rect = self.GetRect()
canvas = wx.Bitmap(rect.width, rect.height)
canvas = wx.Bitmap(round(rect.width), round(rect.height))
mdc = wx.BufferedPaintDC(self)
mdc.SelectObject(canvas)
color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)
@@ -1514,7 +1514,7 @@ class PFNotebookPagePreview(wx.Frame):
x, y = mdc.GetTextExtent(self.title)
mdc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)))
mdc.DrawRectangle(0, 0, rect.width, 16)
mdc.DrawRectangle(0, 0, round(rect.width), 16)
mdc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
@@ -1523,4 +1523,4 @@ class PFNotebookPagePreview(wx.Frame):
mdc.SetPen(wx.Pen("#000000", width=1))
mdc.SetBrush(wx.TRANSPARENT_BRUSH)
mdc.DrawRectangle(0, 16, rect.width, rect.height - 16)
mdc.DrawRectangle(0, 16, round(rect.width), round(rect.height - 16))

View File

@@ -193,7 +193,7 @@ class CopySelectDialog(wx.Dialog):
def exportEsi(self, options, callback):
fit = getFit(self.mainFrame.getActiveFit())
Port.exportESI(fit, True, callback)
Port.exportESI(fit, False, False, False, callback)
def exportXml(self, options, callback):
fit = getFit(self.mainFrame.getActiveFit())

View File

@@ -29,8 +29,9 @@ class Display(wx.ListCtrl):
DEFAULT_COLS = None
def __init__(self, parent, size=wx.DefaultSize, style=0):
wx.ListCtrl.__init__(self, parent, size=size, style=wx.LC_REPORT | style)
wx.ListCtrl.__init__(self)
self.EnableSystemTheme(False)
self.Create(parent, size=size, style=wx.LC_REPORT | style)
self.imageList = CachingImageList(16, 16)
self.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL)
self.activeColumns = []

View File

@@ -268,7 +268,7 @@ class ExportToEve(AuxiliaryFrame):
def __init__(self, parent):
super().__init__(
parent, id=wx.ID_ANY, title=_t("Export fit to EVE"), pos=wx.DefaultPosition,
size=wx.Size(400, 140) if "wxGTK" in wx.PlatformInfo else wx.Size(350, 115), resizeable=True)
size=wx.Size(400, 175) if "wxGTK" in wx.PlatformInfo else wx.Size(350, 145), resizeable=True)
self.mainFrame = parent
@@ -290,6 +290,16 @@ class ExportToEve(AuxiliaryFrame):
self.exportChargesCb.Bind(wx.EVT_CHECKBOX, self.OnChargeExportChange)
mainSizer.Add(self.exportChargesCb, 0, 0, 5)
self.exportImplantsCb = wx.CheckBox(self, wx.ID_ANY, _t('Export Implants'), wx.DefaultPosition, wx.DefaultSize, 0)
self.exportImplantsCb.SetValue(EsiSettings.getInstance().get('exportImplants'))
self.exportImplantsCb.Bind(wx.EVT_CHECKBOX, self.OnImplantsExportChange)
mainSizer.Add(self.exportImplantsCb, 0, 0, 5)
self.exportBoostersCb = wx.CheckBox(self, wx.ID_ANY, _t('Export Boosters'), wx.DefaultPosition, wx.DefaultSize, 0)
self.exportBoostersCb.SetValue(EsiSettings.getInstance().get('exportBoosters'))
self.exportBoostersCb.Bind(wx.EVT_CHECKBOX, self.OnBoostersExportChange)
mainSizer.Add(self.exportBoostersCb, 0, 0, 5)
self.exportBtn.Bind(wx.EVT_BUTTON, self.exportFitting)
self.statusbar = wx.StatusBar(self)
@@ -309,6 +319,14 @@ class ExportToEve(AuxiliaryFrame):
EsiSettings.getInstance().set('exportCharges', self.exportChargesCb.GetValue())
event.Skip()
def OnImplantsExportChange(self, event):
EsiSettings.getInstance().set('exportImplants', self.exportImplantsCb.GetValue())
event.Skip()
def OnBoostersExportChange(self, event):
EsiSettings.getInstance().set('exportBoosters', self.exportBoostersCb.GetValue())
event.Skip()
def updateCharList(self):
sEsi = Esi.getInstance()
chars = sEsi.getSsoCharacters()
@@ -345,8 +363,10 @@ class ExportToEve(AuxiliaryFrame):
sFit = Fit.getInstance()
exportCharges = self.exportChargesCb.GetValue()
exportImplants = self.exportImplantsCb.GetValue()
exportBoosters = self.exportBoostersCb.GetValue()
try:
data = sPort.exportESI(sFit.getFit(fitID), exportCharges)
data = sPort.exportESI(sFit.getFit(fitID), exportCharges, exportImplants, exportBoosters)
except ESIExportException as e:
msg = str(e)
if not msg:

View File

@@ -42,7 +42,7 @@ class DmgPatternNameValidator(BaseValidator):
return DmgPatternNameValidator()
def Validate(self, win):
entityEditor = win.parent
entityEditor = win.Parent.parent
textCtrl = self.GetWindow()
text = textCtrl.GetValue().strip()

View File

@@ -257,7 +257,7 @@ class PyGauge(wx.Window):
else:
w = rect.width * (float(value) / 100)
r = copy.copy(rect)
r.width = w
r.width = round(w)
dc.DrawRectangle(r)
else:
# if bar color is not set, then we use pre-defined transitions
@@ -269,7 +269,7 @@ class PyGauge(wx.Window):
else:
w = rect.width * (float(value) / 100)
r = copy.copy(rect)
r.width = w
r.width = round(w)
# determine transition range number and calculate xv (which is the
# progress between the two transition ranges)
@@ -317,7 +317,7 @@ class PyGauge(wx.Window):
gradient_color
)
if gradient_bitmap is not None:
dc.DrawBitmap(gradient_bitmap, r.left, r.top)
dc.DrawBitmap(gradient_bitmap, round(r.left), round(r.top))
# font stuff begins here
dc.SetFont(self.font)

View File

@@ -39,7 +39,7 @@ class ImplantTextValidor(BaseValidator):
return ImplantTextValidor()
def Validate(self, win):
entityEditor = win.parent
entityEditor = win.Parent.parent
textCtrl = self.GetWindow()
text = textCtrl.GetValue().strip()

View File

@@ -62,7 +62,7 @@ class TargetProfileNameValidator(BaseValidator):
return TargetProfileNameValidator()
def Validate(self, win):
entityEditor = win.parent
entityEditor = win.Parent.parent
textCtrl = self.GetWindow()
text = textCtrl.GetValue().strip()

View File

@@ -80,7 +80,7 @@ class LoadAnimation(wx.Window):
bh = rect.height
y = 0
dc.DrawRectangle(x, y, barWidth, bh)
dc.DrawRectangle(round(x), round(y), round(barWidth), round(bh))
x += barWidth
textColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)

View File

@@ -12,7 +12,7 @@ def Brighten(color, factor):
b += (255 - b) * factor
g += (255 - g) * factor
return wx.Colour(r, g, b, a)
return wx.Colour(round(r), round(g), round(b), round(a))
def Darken(color, factor):
@@ -30,7 +30,7 @@ def Darken(color, factor):
b = min(max(b, 0), 255)
g = min(max(g, 0), 255)
return wx.Colour(r, g, b, a)
return wx.Colour(round(r), round(g), round(b), round(a))
def _getBrightness(color):
@@ -70,4 +70,4 @@ def CalculateTransition(s_color, e_color, delta):
tG = sG + (eG - sG) * delta
tB = sB + (eB - sB) * delta
return wx.Colour(tR, tG, tB, (sA + eA) / 2)
return wx.Colour(round(tR), round(tG), round(tB), round((sA + eA) / 2))

View File

@@ -21,7 +21,7 @@ def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor=None
def DrawFilledBitmap(width, height, color):
canvas = wx.Bitmap(width, height)
canvas = wx.Bitmap(round(width), round(height))
mdc = wx.MemoryDC()
mdc.SelectObject(canvas)
@@ -37,20 +37,20 @@ def DrawFilledBitmap(width, height, color):
def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4):
if width == 0 or height == 0:
return None
canvas = wx.Bitmap(width, height)
canvas = wx.Bitmap(round(width), round(height))
mdc = wx.MemoryDC()
mdc.SelectObject(canvas)
r = wx.Rect(0, 0, width, height)
r.SetHeight(height / fillRatio)
r.SetHeight(round(height / fillRatio))
if gMid is None:
gMid = gStart
mdc.GradientFillLinear(r, gStart, gMid, wx.SOUTH)
r.SetTop(r.GetHeight())
r.SetHeight(height * (fillRatio - 1) / fillRatio + (1 if height % fillRatio != 0 else 0))
r.SetHeight(round(height * (fillRatio - 1) / fillRatio + (1 if height % fillRatio != 0 else 0)))
mdc.GradientFillLinear(r, gMid, gEnd, wx.SOUTH)

View File

@@ -244,7 +244,7 @@ class exportHtmlThread(threading.Thread):
# 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'
' <h2>' + group.name + ' <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>\n'

View File

@@ -78,6 +78,8 @@ from gui.builtinViewColumns import ( # noqa: E402, F401
baseName,
capacitorUse,
dampScanRes,
droneEhp,
droneRegen,
graphColor,
graphLightness,
graphLineStyle,

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

BIN
imgs/icons/1168@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

BIN
imgs/icons/1168@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
imgs/icons/21831@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

BIN
imgs/icons/21831@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
imgs/icons/21894@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

BIN
imgs/icons/21894@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
imgs/icons/24296@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 667 B

BIN
imgs/icons/24296@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
imgs/icons/24404@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

BIN
imgs/icons/24404@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
imgs/icons/24411@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

BIN
imgs/icons/24411@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
imgs/icons/24419@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

BIN
imgs/icons/24419@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
imgs/icons/24585@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
imgs/icons/24585@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
imgs/icons/25328@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

BIN
imgs/icons/25328@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
imgs/icons/25329@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 B

BIN
imgs/icons/25329@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
imgs/icons/25330@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

BIN
imgs/icons/25330@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
imgs/icons/25331@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

BIN
imgs/icons/25331@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
imgs/icons/25355@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

BIN
imgs/icons/25355@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
imgs/icons/25356@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

BIN
imgs/icons/25356@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
imgs/icons/25467@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

BIN
imgs/icons/25467@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
imgs/icons/25468@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

BIN
imgs/icons/25468@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
imgs/icons/25621@1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

BIN
imgs/icons/25621@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Some files were not shown because too many files have changed in this diff Show More