Compare commits

...

270 Commits

Author SHA1 Message Date
DarkPhoenix
0fbb318d8a Bump version 2024-04-30 04:10:53 +06:00
DarkPhoenix
cd013e8287 Add new effects 2024-04-30 04:10:33 +06:00
DarkPhoenix
e667453c1e Add new icons 2024-04-30 03:59:25 +06:00
DarkPhoenix
24e2db0f88 Update static data to 2563119 2024-04-30 03:55:34 +06:00
DarkPhoenix
3c47f8c6bb Add OS version comments to windows manifest 2024-04-17 02:42:42 +06:00
DarkPhoenix
42c2b3f253 Bundle extra library with appimage 2024-04-16 22:16:35 +06:00
DarkPhoenix
71d6830ac0 Bundle extra library with appimage 2024-04-16 22:03:25 +06:00
DarkPhoenix
ac2fb01a7c Update icons 2024-04-16 21:32:17 +06:00
DarkPhoenix
fc66c212fc Bump version 2024-04-16 21:31:29 +06:00
DarkPhoenix
9625fafda7 Merge remote-tracking branch 'origin/master' 2024-04-16 21:21:46 +06:00
DarkPhoenix
6b1aca4306 Update static data to 2550121 2024-04-16 21:21:33 +06:00
Anton Vorobyov
fd5a094304 Merge pull request #2597 from josephdouce/patch-2
Searchable exportHtml.py
2024-02-28 20:10:02 +04:00
Joseph Douce
a3142ff62f Update exportHtml.py 2024-02-28 15:36:02 +00:00
Joseph Douce
58ebfd3643 Searchable exportHtml.py
added searchable fits
2024-02-28 15:33:18 +00:00
Anton Vorobyov
e272267842 Merge pull request #2596 from josephdouce/patch-1
Fix exportHtml.py
2024-02-28 19:25:20 +04:00
Joseph Douce
b0256542bc Fix exportHtml.py 2024-02-28 13:17:23 +00:00
DarkPhoenix
9b8fd24987 Update pot 2024-02-27 16:30:06 +06:00
DarkPhoenix
9c68889a6d Call SelectAll() after Fit() since that's what seems to be causing issues under windows 2024-02-27 08:15:39 +06:00
DarkPhoenix
a4ed6e8066 Move select all call earlier, to see which call breaks it on windows 2024-02-27 07:38:01 +06:00
DarkPhoenix
9fe8163a69 Select all text even later 2024-02-27 05:54:55 +06:00
DarkPhoenix
9f9b496726 Select all text even later 2024-02-27 05:30:03 +06:00
DarkPhoenix
3b8fa68a4c Bump version 2024-02-27 04:11:02 +06:00
DarkPhoenix
9f5a649b04 Change migration to include newly added constraints 2024-02-27 04:05:19 +06:00
DarkPhoenix
fb45736c14 Extend UNIQUE constraint by new server column, to avoid possible collisions 2024-02-27 03:36:43 +06:00
Anton Vorobyov
b8a58fc7a6 Merge pull request #2590 from huangzheng2016/master
Update the SSO Login for Serenity and Singularity server's player
2024-02-26 23:35:31 +04:00
DarkPhoenix
b04c521805 Make sure not to crash character editor during SSO init when char editor is opened 2024-02-27 01:18:01 +06:00
DarkPhoenix
526695fa9a Try setting size for graph input boxes as a workaround for wx bug 2024-02-27 00:30:19 +06:00
Anton Vorobyov
d6be77d107 Merge pull request #2589 from pyfa-org/dependabot/pip/cryptography-42.0.4
Bump cryptography from 42.0.2 to 42.0.4
2024-02-26 22:22:33 +04:00
DarkPhoenix
907da343b1 Rework how progress dialog is used 2024-02-27 00:04:44 +06:00
DarkPhoenix
3fadccc715 Select all after setting focus as an attempt to fix input box not selecting everything under windows 2024-02-26 06:25:07 +06:00
DarkPhoenix
82a912858f Fix items in market's item view 2024-02-26 04:45:18 +06:00
正汰
cc1fdddc0a Merge branch 'pyfa-org:master' into master 2024-02-23 02:50:41 +08:00
正汰
e314ecf887 Fix the esi problem for Serenity when setting 2024-02-23 02:48:54 +08:00
正汰
94b1c8b029 Fix the esi problem for Serenity when setting 2024-02-23 02:47:59 +08:00
正汰
6e3b7ff132 Fix the esi problem for Serenity when setting 2024-02-23 02:47:47 +08:00
正汰
ff14855808 Fix the esi problem for Serenity when setting 2024-02-23 02:47:27 +08:00
dependabot[bot]
4761857b84 Bump cryptography from 42.0.2 to 42.0.4
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.2 to 42.0.4.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.2...42.0.4)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 20:48:49 +00:00
DarkPhoenix
bb53e75bfe Force light rack colors on windows regardless of OS theme 2024-02-21 03:04:36 +06:00
DarkPhoenix
cc146401b6 Bump version 2024-02-21 01:17:32 +06:00
DarkPhoenix
465e43ed45 Change how colors are defined and used 2024-02-21 00:28:21 +06:00
DarkPhoenix
f89a8a9f87 Make use of defined error color 2024-02-21 00:01:55 +06:00
DarkPhoenix
768c417c8e Add alternate colors for fitting pane (for dark mode on macos) 2024-02-21 00:00:44 +06:00
DarkPhoenix
494b6353b8 Fix icons script and update icons 2024-02-20 22:59:32 +06:00
DarkPhoenix
69ef7825af Add perl regexp lib to linux bundle 2024-02-20 22:20:52 +06:00
DarkPhoenix
e0a71754ce Merge remote-tracking branch 'origin/master' 2024-02-20 22:17:06 +06:00
DarkPhoenix
b959d9dd07 Fix mercoxit rig bonus 2024-02-20 22:15:54 +06:00
Anton Vorobyov
2c2455a644 Merge pull request #2573 from salartarium/master
update links
2024-02-20 20:09:39 +04:00
Anton Vorobyov
e2dbfb52e9 Merge pull request #2561 from m-sasha/abyssal-xml
Add mutation info to XML export/import.
2024-02-20 19:43:24 +04:00
Anton Vorobyov
612591b59b Merge pull request #2582 from pyfa-org/dependabot/pip/cryptography-42.0.2
Bump cryptography from 41.0.7 to 42.0.2
2024-02-20 19:36:03 +04:00
DarkPhoenix
3e00d5d4ae Add new effect 2024-02-20 21:18:37 +06:00
DarkPhoenix
539be1527b Clean up effects 2024-02-20 21:00:06 +06:00
DarkPhoenix
d00db1e167 Update static data to 2497879 2024-02-20 20:55:35 +06:00
dependabot[bot]
fc6d5fea48 Bump cryptography from 41.0.7 to 42.0.2
Bumps [cryptography](https://github.com/pyca/cryptography) from 41.0.7 to 42.0.2.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/41.0.7...42.0.2)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-17 00:47:08 +00:00
salartarium
ecf4d6c0de Update upgrade4.py
update link
2024-01-15 05:21:38 -08:00
salartarium
f5f7f92166 Update upgrade25.py
update link, typos in comments
2024-01-15 05:20:26 -08:00
salartarium
f5d7e754a6 Update upgrade1.py
update link
2024-01-15 05:18:33 -08:00
正汰
be85080d8d Merge branch 'pyfa-org:master' into master 2024-01-12 11:00:13 +08:00
Alexander Maryanovsky
6ed34be118 Merge branch 'master' into abyssal-xml 2023-12-25 06:39:51 +02:00
Anton Vorobyov
59dceadfee Merge pull request #2475 from pyfa-org/crowdin_master
New Crowdin updates
2023-12-25 05:08:01 +04:00
Anton Vorobyov
2a74c779c8 Merge pull request #2558 from fruitchewy/dark-style
Improve AutoListCtrl legibility in dark mode
2023-12-25 05:07:36 +04:00
Alexander Maryanovsky
f63331454b Merge branch 'master' into abyssal-xml 2023-12-22 10:36:35 +02:00
DarkPhoenix
d105748348 When importing skills, import their prerequisites as well 2023-12-22 03:43:47 +06:00
Alexander Maryanovsky
cdd7077346 Merge branch 'master' into abyssal-xml 2023-12-21 22:49:06 +02:00
DarkPhoenix
f965497186 Fix tooltip 2023-12-21 10:03:06 +06:00
Alexander Maryanovsky
6b06b5b46c Add mutation info to XML export/import. 2023-12-20 16:51:59 +02:00
akauble
d1e19406d0 Style AutoListCtrl highlighting manually in dark mode 2023-12-12 23:45:16 -06:00
DarkPhoenix
2a3c586be6 Add VTS as a jargon entry 2023-12-12 01:56:59 +06:00
DarkPhoenix
23f014c32e Do not use system theme for ListCtrl under windows 2023-12-09 17:46:31 +06:00
DarkPhoenix
0ac6a14ebb Show salvage chance next to salvage drones too 2023-12-09 17:24:35 +06:00
DarkPhoenix
0dc2632fc4 Bump version 2023-12-08 22:58:39 +06:00
DarkPhoenix
fc43691275 Revert pyinstaller to 6.0.0 for windows 2023-12-08 21:48:09 +06:00
DarkPhoenix
21a9b779f3 Bump version 2023-12-07 19:27:18 +06:00
DarkPhoenix
b8dc55a3a6 Fix HTML export 2023-12-07 18:51:18 +06:00
DarkPhoenix
b7a5b33ff6 Ensure ints are passed to SetDeviceOrigin() 2023-12-06 22:47:59 +06:00
DarkPhoenix
3593d16bd1 Ensure ints are passed to Scale() 2023-12-06 21:19:00 +06:00
DarkPhoenix
57830fd5a5 Bump version 2023-12-06 08:58:33 +06:00
DarkPhoenix
313d793175 Remove debug print 2023-12-06 08:58:17 +06:00
DarkPhoenix
f8e20e23bb Restore debugging in pyfa 2023-12-06 08:35:53 +06:00
DarkPhoenix
1959681250 Attempt to workaround appimage builder issue 2023-12-06 08:35:32 +06:00
DarkPhoenix
0192c77df1 Give up on debugging XDG_DATA_DIRS since it's appimage-builder bug 2023-12-06 08:08:48 +06:00
DarkPhoenix
b709629d7b Try defining substitution without dollar sign 2023-12-06 07:28:07 +06:00
DarkPhoenix
3071aead5c Remove escaping from xdg variable 2023-12-06 06:55:11 +06:00
DarkPhoenix
3f2df69022 Add print to debug XDG issues 2023-12-06 06:35:49 +06:00
DarkPhoenix
e9a48817a7 Try to "escape" xdg variable 2023-12-06 05:30:36 +06:00
DarkPhoenix
ed19ea5f2f Downgrade pyinstaller 1 step 2023-12-06 04:51:33 +06:00
DarkPhoenix
19366a09b4 Exclude some icon resources from linux build 2023-12-06 04:51:14 +06:00
DarkPhoenix
f8f01cd63c Round numbers in calls to bitmaps 2023-12-06 04:19:34 +06:00
DarkPhoenix
21a0e2b9c5 Remove unused import 2023-12-06 02:59:20 +06:00
DarkPhoenix
0ba8ba33ef Append bundled share path to XDG_DATA_DIRS 2023-12-06 02:26:21 +06:00
DarkPhoenix
346184d06a Set default XDG_DATA_DIRS for linux build 2023-12-06 02:00:36 +06:00
DarkPhoenix
46fc9e65bb Do minor file cleanup 2023-12-06 01:44:28 +06:00
DarkPhoenix
2d51da4d02 Update pot file 2023-12-05 19:18:02 +06:00
DarkPhoenix
b5e77415cf Bump version 2023-12-05 18:34:14 +06:00
DarkPhoenix
4c75daf36c Add icons 2023-12-05 18:33:25 +06:00
DarkPhoenix
4a120b8abf Update static data and effects 2023-12-05 18:32:15 +06:00
DarkPhoenix
fc43510fbc Merge remote-tracking branch 'origin/master' 2023-12-05 17:46:03 +06:00
Anton Vorobyov
28c7ff54aa Merge pull request #2485 from kols/fix-proxy-proto-strip
fix proxy proto string strip
2023-12-05 15:11:23 +04:00
DarkPhoenix
89f1e0a126 Remove all the things which are handled by AppImage builder now 2023-12-05 16:36:09 +06:00
DarkPhoenix
b6ff1cada9 Pin current versions of dependencies 2023-12-05 16:17:10 +06:00
DarkPhoenix
cc0d6465d6 Do not crash on failure to fetch localization percentage 2023-12-05 15:31:18 +06:00
DarkPhoenix
c49a914584 Fix crowdin script error printing 2023-12-05 15:26:34 +06:00
DarkPhoenix
e6e6568571 Fix path to certificate bundle 2023-12-05 02:10:27 +06:00
DarkPhoenix
7120000202 Bundle svg image loader lib for linux 2023-12-05 01:36:52 +06:00
DarkPhoenix
c3f7078053 Bundle webkit lib for linux 2023-12-05 01:06:24 +06:00
DarkPhoenix
b70f02fa3e Bump version 2023-12-05 00:17:51 +06:00
DarkPhoenix
8d893ce648 Replace tiff/jpg libs by full GTK package 2023-12-05 00:01:10 +06:00
DarkPhoenix
c230480464 Do not modify package after it was built 2023-12-04 23:11:34 +06:00
DarkPhoenix
da7a4a2df0 Bundle certificates with AppImage 2023-12-04 17:28:18 +06:00
DarkPhoenix
62bde4b8c9 Bump version 2023-12-04 16:35:05 +06:00
DarkPhoenix
43967b70ae Add tiff lib 2023-12-04 15:58:21 +06:00
DarkPhoenix
782c007200 Add libjpeg dependency 2023-12-04 08:51:17 +06:00
DarkPhoenix
5812d333d9 Keep pyfa source in /opt/ 2023-12-04 08:36:00 +06:00
DarkPhoenix
c461bf469e Adjust path to artifact 2023-12-04 08:18:12 +06:00
DarkPhoenix
d5c8c92b48 Build linux binaries first 2023-12-04 08:08:47 +06:00
DarkPhoenix
1abffd6c5d Fix file extension 2023-12-04 08:08:00 +06:00
DarkPhoenix
a824cc5e48 Fix path to python, and disable debugging once again 2023-12-04 08:06:18 +06:00
DarkPhoenix
db7afffd65 Re-enable debugging again 2023-12-04 07:42:22 +06:00
DarkPhoenix
4d02a1292f A few minor fixes, and disable ssh 2023-12-04 07:16:12 +06:00
DarkPhoenix
00f72294d4 Fix variable name 2023-12-04 06:55:52 +06:00
DarkPhoenix
0d45be0597 Add missing dependency 2023-12-04 06:52:57 +06:00
DarkPhoenix
f60090fc79 Move lots of preprocessing out of appimg recipe (to support calculation of app version) 2023-12-04 06:39:28 +06:00
DarkPhoenix
713807df13 Get pip since provided python doesn't have it 2023-12-04 05:53:30 +06:00
DarkPhoenix
c020bd8898 More progress on linux build job 2023-12-04 05:29:11 +06:00
DarkPhoenix
39ee8586d4 Do build process using system interpreter, then install dependencies into bundled one 2023-12-04 04:59:04 +06:00
DarkPhoenix
653a1585dc Use appdir variable instead of "hardcoded" path 2023-12-04 04:03:01 +06:00
DarkPhoenix
2dcf011020 Move script to execute after runtime 2023-12-04 02:26:45 +06:00
DarkPhoenix
555e72a27a Another attempt to bundle python with appimg 2023-12-04 00:57:09 +06:00
DarkPhoenix
85e740d75a Add keys to repositories 2023-12-03 23:41:47 +06:00
DarkPhoenix
d604ed300a Fetch full repo right away 2023-12-03 22:15:05 +06:00
DarkPhoenix
3aea4e5f23 Remove empty section 2023-12-03 21:50:51 +06:00
DarkPhoenix
a54adddc74 Use python package from repo 2023-12-03 21:50:35 +06:00
DarkPhoenix
dda397fea3 Use AppDir variable instead of BuildDir 2023-12-03 17:03:31 +06:00
Anton Vorobyov
3cd718350e Merge pull request #2545 from Grange-Nagy/dep_facelift
fix infinite redraws on macos
2023-12-03 14:01:38 +04:00
Grange Nagy
e87860a700 Merge branch 'pyfa-org:dep_facelift' into dep_facelift 2023-12-03 02:24:54 -05:00
Grange Nagy
451d21eaa0 let go of drag object 2023-12-03 02:24:13 -05:00
Grange Nagy
718f9ef859 allow fit drag bmp to be smaller 2023-12-03 02:18:51 -05:00
Grange Nagy
193adc4ba5 fix fit drag bitmap causing redraws on macos 2023-12-03 01:47:32 -05:00
Grange Nagy
cdfa52e9ed remove drag rendering entirely for now 2023-12-02 21:53:31 -05:00
Anton Vorobyov
2752b288ad Merge pull request #2544 from Grange-Nagy/dep_facelift
fix runtime bitmap scaling error on macos sonoma
2023-12-03 06:49:06 +04:00
DarkPhoenix
8dce968780 Add one dependency just to detect bundle arch 2023-12-03 08:46:12 +06:00
Grange Nagy
b889da7e68 fix runtime bitmap scaling error on mac 2023-12-02 18:09:02 -05:00
DarkPhoenix
0dd7dea708 More work on new appimage build process 2023-12-02 11:16:31 +06:00
DarkPhoenix
8af1c8da7d Add script which extracts config values 2023-12-02 01:09:46 +06:00
DarkPhoenix
8d4f83156d Strip python appimage from linux build 2023-12-01 18:44:46 +06:00
DarkPhoenix
798bdf3fe0 Enable SSH for debugging 2023-12-01 17:25:42 +06:00
DarkPhoenix
a7cf4aab76 Specify architecture 2023-12-01 03:43:36 +06:00
DarkPhoenix
d910e324f3 Specify apt architecture 2023-12-01 01:49:01 +06:00
DarkPhoenix
d800db5ccb Do not specify package name 2023-12-01 00:56:56 +06:00
DarkPhoenix
6c5d0a3e4f Try to make use of AppImageBuilder for linux 2023-12-01 00:41:00 +06:00
DarkPhoenix
9389a0421f Merge branch 'master' into dep_facelift 2023-12-01 00:13:09 +06:00
DarkPhoenix
6f3ab6cee8 Remove evemarketer price source, add evetycoon 2023-12-01 00:11:50 +06:00
DarkPhoenix
258dcd49a0 Merge branch 'master' into dep_facelift 2023-11-30 23:05:34 +06:00
DarkPhoenix
5f0320d1e3 Bump version 2023-11-30 22:59:01 +06:00
DarkPhoenix
54052899f9 Add new suppression effect as dark code (looks like it's not active yet) 2023-11-30 22:58:22 +06:00
DarkPhoenix
eda53e9318 Merge branch 'master' into dep_facelift 2023-11-30 22:32:43 +06:00
DarkPhoenix
4d8a891fd8 Update effects 2023-11-30 22:32:04 +06:00
DarkPhoenix
2d4b97afe6 Update renders 2023-11-30 17:55:01 +06:00
DarkPhoenix
49d1ec17e8 Update static data to 2438956 2023-11-30 17:53:30 +06:00
DarkPhoenix
cfecacacf9 Add appimagebuilder spec file 2023-11-30 17:39:10 +06:00
DarkPhoenix
a9112c04a3 Set it up to debug manually 2023-11-30 06:44:13 +06:00
DarkPhoenix
5f2a74691c Fix python appimage download command 2023-11-30 06:23:25 +06:00
DarkPhoenix
17c22df0f4 Update debug key 2023-11-30 06:08:39 +06:00
DarkPhoenix
c53bc82929 Try out new package tool 2023-11-30 05:51:45 +06:00
DarkPhoenix
7113f41b9a Try adding libjpeg (current appimage fails to launch due to this dep) 2023-11-30 05:02:58 +06:00
DarkPhoenix
aa66d63085 Do not hardcode python version for startup script 2023-11-30 04:28:49 +06:00
DarkPhoenix
140ee70dc3 Update python appimage 2023-11-30 03:50:18 +06:00
DarkPhoenix
5023a6cbae Try to fix binary distributive of wx on linux 2023-11-30 03:20:56 +06:00
DarkPhoenix
aafa77baed Try to fix binary distributive of wx on linux 2023-11-30 02:48:51 +06:00
DarkPhoenix
16dabd57ca Attempt to fix macos pyinstaller 2023-11-30 02:10:48 +06:00
DarkPhoenix
8668d2dd55 Try to fix binary distributive of wx on linux 2023-11-30 02:09:52 +06:00
DarkPhoenix
5c5f37a9ae Use binary distributive of wx on linux 2023-11-30 00:19:15 +06:00
DarkPhoenix
621c36f1cd Use newer pyinstaller on macos 2023-11-29 23:27:17 +06:00
DarkPhoenix
be2cc523c2 Fix skill level suffix 2023-11-29 23:03:32 +06:00
DarkPhoenix
bd83c403a1 Fix spec file again 2023-11-29 22:51:10 +06:00
DarkPhoenix
043f533a10 Move contents-directory specification to spec file 2023-11-29 22:31:09 +06:00
DarkPhoenix
57a23affd0 Adapt to changes in pyinstaller 2023-11-29 21:47:16 +06:00
DarkPhoenix
b678152ac6 Change pyinstaller command for windows 2023-11-29 20:50:14 +06:00
DarkPhoenix
747bbc8200 Revert to python 3.11 2023-11-29 20:36:25 +06:00
DarkPhoenix
871cf42d88 Fix crowdin script on newer python versions 2023-11-29 20:16:17 +06:00
DarkPhoenix
28533d60d3 Update appveyor images 2023-11-29 20:07:39 +06:00
DarkPhoenix
ca12b3c94f Pin older sqlalchemy 2023-11-29 20:01:44 +06:00
DarkPhoenix
81e449dd83 Merge branch 'master' into dep_facelift 2023-11-14 20:13:50 +06:00
DarkPhoenix
368f2c33a9 Attempt to work around postgres issue on linux 2023-11-14 19:45:22 +06:00
DarkPhoenix
c445bb4423 Add some debug commands to work around postgres issue in image 2023-11-14 19:28:44 +06:00
DarkPhoenix
9b216b5d48 Merge branch 'master' into dep_facelift 2023-11-14 18:54:35 +06:00
DarkPhoenix
4b932d210f Bump version 2023-11-14 18:54:09 +06:00
DarkPhoenix
a1ca29fb30 Update effects 2023-11-14 18:53:50 +06:00
DarkPhoenix
876c85ee19 Update icons 2023-11-14 18:41:56 +06:00
DarkPhoenix
ac1d935712 Update static data to 2420589 2023-11-14 18:39:57 +06:00
DarkPhoenix
2ccc829d3b Merge branch 'singularity' 2023-11-14 18:39:01 +06:00
DarkPhoenix
061ab27286 Merge branch 'singularity' into dep_facelift 2023-11-13 21:45:51 +06:00
DarkPhoenix
d127dd9a7e Implement RR diminishing returns 2023-11-13 21:45:40 +06:00
DarkPhoenix
6d13ab8bcb Merge branch 'master' into dep_facelift 2023-10-31 00:06:47 +06:00
DarkPhoenix
e5d95dd4d8 Fix something which was not supposed to be committed 2023-10-31 00:06:31 +06:00
DarkPhoenix
08c5c9a4b7 Fix an omission 2023-10-31 00:05:21 +06:00
DarkPhoenix
115ea9a36c Merge branch 'master' into dep_facelift 2023-10-31 00:02:35 +06:00
DarkPhoenix
a484698a9d Add option to export implants/boosters via ESI 2023-10-31 00:01:16 +06:00
DarkPhoenix
bafaed1d81 Fix character editor search crash 2023-10-30 23:33:35 +06:00
DarkPhoenix
c56f274a5f Merge branch 'singularity' into dep_facelift 2023-10-23 06:46:36 +06:00
DarkPhoenix
02f7fbf1b1 Do not stacking penalize shield/armor HP bonuses 2023-10-23 06:46:00 +06:00
DarkPhoenix
96214eaa27 Merge branch 'singularity' into dep_facelift 2023-10-19 05:46:01 +06:00
DarkPhoenix
2fef60eaaa Merge branch 'master' into dep_facelift 2023-10-10 02:59:39 +06:00
DarkPhoenix
807622a1ec Merge branch 'master' into dep_facelift 2023-09-19 23:56:38 +03:00
DarkPhoenix
d126b62d63 Merge branch 'master' into dep_facelift 2023-09-10 17:30:53 +06:00
DarkPhoenix
9e50de96f3 Merge branch 'master' into dep_facelift 2023-09-10 17:27:49 +06:00
DarkPhoenix
e67083a79b Add effects for new AT ships 2023-09-10 09:11:42 +06:00
DarkPhoenix
b103e576d2 Merge branch 'master' into dep_facelift 2023-09-10 08:42:32 +06:00
DarkPhoenix
b472adb404 Merge branch 'master' into dep_facelift 2023-06-14 01:09:58 +06:00
DarkPhoenix
7e5150be8c Merge branch 'master' into dep_facelift 2023-06-14 01:05:32 +06:00
DarkPhoenix
261d510ccb Merge branch 'master' into dep_facelift 2023-06-13 22:07:40 +06:00
DarkPhoenix
858b0b243a Merge branch 'singularity' into dep_facelift 2023-06-11 06:41:09 +06:00
DarkPhoenix
53385f2458 Merge branch 'singularity' into dep_facelift 2023-06-11 04:28:36 +06:00
DarkPhoenix
7d3371c379 Merge branch 'singularity' into dep_facelift 2023-06-11 00:29:49 +06:00
Anton Vorobyov
d94116ea1c Merge branch 'master' into dep_facelift 2023-05-23 01:11:07 +06:00
kane
5d95663f63 fix proxy proto string strip 2022-11-28 14:20:23 +08:00
Anton Vorobyov
128de58b83 New translations lang.pot (Japanese) 2022-11-05 01:34:48 +03:00
Anton Vorobyov
47eb8d422f New translations lang.pot (Japanese) 2022-11-04 00:10:59 +03:00
Anton Vorobyov
38ddf83da4 New translations lang.pot (Japanese) 2022-11-03 00:01:02 +03:00
Anton Vorobyov
58142a8641 New translations lang.pot (Japanese) 2022-11-02 00:03:26 +03:00
Anton Vorobyov
d86f0f40ac New translations lang.pot (Japanese) 2022-10-31 23:47:48 +03:00
Anton Vorobyov
62944e4ce3 New translations lang.pot (Japanese) 2022-10-30 23:14:52 +03:00
Anton Vorobyov
812201d8b3 New translations lang.pot (Japanese) 2022-10-29 22:53:28 +03:00
Anton Vorobyov
52abd5620f New translations lang.pot (Japanese) 2022-10-28 22:56:28 +03:00
DarkPhoenix
b21b756fa6 Merge branch 'singularity' into dep_facelift 2022-10-25 01:53:45 +04:00
DarkPhoenix
492adb4dfd Merge branch 'singularity' into dep_facelift 2022-10-25 01:33:18 +04:00
Anton Vorobyov
e47112a1f2 New translations lang.pot (Japanese) 2022-10-23 22:57:24 +03:00
DarkPhoenix
5fa7b2c86a Merge branch 'master' into dep_facelift 2022-10-23 10:19:39 +04:00
DarkPhoenix
f6120f09ac Bump version 2022-10-23 07:58:00 +04:00
Anton Vorobyov
00a6835f50 New translations lang.pot (Japanese) 2022-10-22 21:46:14 +03:00
DarkPhoenix
4995c7994f Merge branch 'master' into dep_facelift 2022-10-22 21:36:54 +04:00
DarkPhoenix
620ce89a05 Merge branch 'master' into dep_facelift 2022-10-21 23:09:03 +04:00
DarkPhoenix
73dc056e61 Fix creating of new implant sets / damage patterns / target profiles on new wx 2022-10-21 07:21:57 +04:00
DarkPhoenix
97625d11ab Merge branch 'master' into dep_facelift 2022-10-15 07:12:26 +04:00
DarkPhoenix
21bd4272d9 Merge branch 'master' into dep_facelift 2022-09-30 20:52:40 +04:00
DarkPhoenix
332c91d661 Merge branch 'master' into dep_facelift 2022-09-30 20:50:36 +04:00
DarkPhoenix
719125657f Merge branch 'master' into dep_facelift 2022-09-30 17:50:31 +04:00
DarkPhoenix
9621a54257 Merge branch 'master' into dep_facelift 2022-09-30 16:50:07 +04:00
DarkPhoenix
bfc580cf7c Merge branch 'master' into dep_facelift 2022-08-29 15:49:54 +04:00
DarkPhoenix
fd2f76ee41 Merge branch 'master' into dep_facelift 2022-08-29 02:53:09 +04:00
DarkPhoenix
9e01d15e60 Merge branch 'master' into dep_facelift 2022-08-29 02:32:12 +04:00
DarkPhoenix
23ee164a76 Merge branch 'master' into dep_facelift 2022-07-13 00:58:29 +04:00
DarkPhoenix
99ddc36027 Merge branch 'master' into dep_facelift 2022-07-10 02:52:44 +04:00
DarkPhoenix
a33f48a83b Merge branch 'master' into dep_facelift 2022-07-04 22:32:43 +04:00
DarkPhoenix
11c060103a Merge branch 'master' into dep_facelift 2022-07-04 05:55:43 +04:00
DarkPhoenix
ac6b6f70eb Merge branch 'master' into dep_facelift 2022-07-04 05:24:52 +04:00
DarkPhoenix
baf10b24d6 Merge branch 'master' into dep_facelift 2022-07-04 04:09:49 +04:00
DarkPhoenix
0ab485115c Merge branch 'master' into dep_facelift 2022-06-27 01:13:26 +04:00
blitzmann
27101732ca Add a couple of bug fixes, and make the server option a checkbox 2022-05-08 13:48:38 -04:00
blitzmann
ed48a8b5d0 Re-work the exceptions related to logging a character in 2022-05-08 13:21:23 -04:00
blitzmann
5a6fe373f1 Merge branch 'master' into huangzheng2016-master
# Conflicts:
#	gui/builtinPreferenceViews/pyfaEsiPreferences.py
2022-05-08 12:35:46 -04:00
blitzmann
3c6eb6d054 Some updates to allow code / token paste regardless of server setting 2022-05-08 12:24:12 -04:00
blitzmann
dc997f0dc4 Various updates to support server-aware calls per character 2022-05-07 12:35:16 -04:00
DarkPhoenix
f8895a14c5 Merge branch 'master' into dep_facelift 2022-05-05 21:54:14 +04:00
DarkPhoenix
22eabcea8d Merge branch 'master' into dep_facelift 2022-05-05 21:17:17 +04:00
DarkPhoenix
ccd3498594 Even more fixes for silent coercion, which is no more 2022-05-05 18:41:17 +04:00
blitzmann
14afc83e86 Fix client ID that is used 2022-04-30 10:17:51 -04:00
hz2016
216dd2a787 Update the SSO Login for Serenity and Singularity server's player 2022-04-28 15:58:33 +08:00
DarkPhoenix
71d088b90a More fixes for silent coercion, which is no more 2022-04-24 02:19:26 +04:00
DarkPhoenix
17712d8b7d More fixes for silent coercion, which is no more 2022-04-20 15:58:46 +04:00
DarkPhoenix
4a224ea9a5 Fix preferences panel 2022-04-20 15:25:52 +04:00
DarkPhoenix
6664b5620a Fix type casts for graphs
See #2391 for more info
2022-04-20 15:23:03 +04:00
DarkPhoenix
a98e898bd8 Cast coordinates passed to various wx objects into ints
See #2391 for more info
2022-04-20 02:12:46 +04:00
DarkPhoenix
e98ae5de39 Cast colors into ints before passing to wx
See #2391 for more info
2022-04-20 01:26:52 +04:00
DarkPhoenix
a51fbbc238 Merge branch 'master' into dep_facelift
# Conflicts:
#	.appveyor.yml
#	requirements.txt
2022-04-20 00:42:04 +04:00
DarkPhoenix
f9b4c7b5ea Merge branch 'singularity' into dep_facelift 2022-03-01 13:23:03 +03:00
DarkPhoenix
4e83f52532 Silence more SQLAlchemy warnings 2022-02-22 23:54:33 +03:00
DarkPhoenix
ec0b543393 Silence some of SQLAlchemy warnings 2022-02-22 23:45:09 +03:00
DarkPhoenix
cff94a12ee Set a few gamedata relations view-only
New sqlalchemy complains about them, and we never set them via those relations anyway
2022-02-22 01:12:02 +03:00
DarkPhoenix
da39975ba8 Update requirements specified in CI specification 2022-02-21 23:26:27 +03:00
DarkPhoenix
82515343ee Update requirements to "latest" version of every package 2022-02-21 23:00:25 +03:00
177 changed files with 66136 additions and 5920 deletions

View File

@@ -1,75 +1,48 @@
image: image:
- Visual Studio 2019 - Ubuntu2204
- Ubuntu - Visual Studio 2022
- macos - macos-catalina
clone_depth: 1
for: for:
- -
matrix: matrix:
only: only:
- image: Ubuntu - image: Ubuntu2204
environment: environment:
APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJDW/+oYNGOiPvwuwAL9tc/LQgg58aosIVpMYfepQZ20V+VZnHpZh8IRDA8Jo5xht19p2PksA+hFgqA0kpKtrSkuiWdE8rATQItfk4gf7yB0yGasJGGQZYazy9k/9XtmYkq2HHOOeEqdxvrICddJQ88MLCLT9lJENSUP/YS/yGcjZFXVxE11pTeIcqlCRU+3eYa1v7BeNvXIKNhZoK5orXWrtuH3cy8jrSns/u70aYfJ6B2jA8CnWnDbuvpeQtEY61SQqlKUsSArNa8NAsXj41wr3Ar9gAG9330w7EMTqlutk8HZO35uHI0q5qinUhaQYufPPrVkb2L/N+ZCfu0fnh appveyor" 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: appimagetool-x86_64.AppImage APPIMAGE_TOOL: appimage-builder-x86_64.AppImage
PYTHON_APPIMAGE: python3.7.17-cp37-cp37m-manylinux2014_x86_64.AppImage
DEPLOY_DIR: AppDir/opt/pyfa DEPLOY_DIR: AppDir/opt/pyfa
# APPVEYOR_SSH_BLOCK: true # APPVEYOR_SSH_BLOCK: true
cache: cache:
- /home/appveyor/.cache/pip -> requirements.txt - /home/appveyor/.cache/pip -> requirements.txt
init: # init:
- sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e - # - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e -
install: 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 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: before_build:
- sh: mkdir build && cd build # Prepare pyfa data
- 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: unlink AppDir/AppRun
- 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
# Speedup, but causes runtime incompatiblities
#- sh: AppDir/usr/bin/python -s -m pip install -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 -r ../requirements.txt
# Run scripts to prep pyfa data and build database
- sh: cd ../
- sh: find locale/ -type f -name "*.po" -exec msgen "{}" -o "{}" \; - sh: find locale/ -type f -name "*.po" -exec msgen "{}" -o "{}" \;
- sh: build/AppDir/usr/bin/python scripts/compile_lang.py - sh: python3 -B scripts/compile_lang.py
- sh: build/AppDir/usr/bin/python scripts/dump_crowdin_progress.py - sh: python3 -B scripts/dump_crowdin_progress.py
- sh: build/AppDir/usr/bin/python db_update.py - sh: python3 -B db_update.py
- sh: export PYFA_VERSION="$(python3.7 scripts/dump_version.py)" - sh: export PYFA_VERSION="$(python3 -B scripts/dump_version.py)"
- sh: mkdir build
# Copy pyfa files to host # Download packaging tool
- 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: 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: find ./build/$DEPLOY_DIR | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf - sh: chmod +x $APPIMAGE_TOOL
build_script:
# Copy static AppImage files - sh: mkdir -p AppDir/opt/pyfa
- sh: cd dist_assets/linux - 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: chmod +x AppRun - sh: mkdir -p AppDir/usr/share/icons/hicolor/64x64/apps/
- sh: cp AppRun pyfa.desktop ../../build/AppDir/ - sh: cp imgs/gui/pyfa64.png AppDir/usr/share/icons/hicolor/64x64/apps/pyfa.png
- sh: cp pyfa.desktop ../../build/AppDir/usr/share/applications/ - sh: ./$APPIMAGE_TOOL --recipe dist_assets/linux/AppImageBuilder.yml
- sh: cp 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
after_build: after_build:
- sh: ls -la build - sh: ls -la
artifacts: artifacts:
- path: dist/pyfa-$PYFA_VERSION-linux.AppImage - path: pyfa-$PYFA_VERSION-linux.AppImage
deploy: deploy:
tag: $PYFA_VERSION tag: $PYFA_VERSION
release: pyfa $PYFA_VERSION release: pyfa $PYFA_VERSION
@@ -85,17 +58,15 @@ for:
- -
matrix: matrix:
only: only:
- image: Visual Studio 2019 - image: Visual Studio 2022
environment: environment:
PYTHON: "C:\\Python37-x64" PYTHON: "C:\\Python311-x64"
# Should be enabled only for build process debugging # Should be enabled only for build process debugging
# init: # init:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
cache: cache:
- C:\users\appveyor\appdata\local\pip\cache\ -> requirements.txt - C:\users\appveyor\appdata\local\pip\cache\ -> requirements.txt
install: install:
- cmd: git fetch --prune --unshallow # to fix the version dump issues
- ps: echo("OS version:") - ps: echo("OS version:")
- ps: "[System.Environment]::OSVersion.Version" - ps: "[System.Environment]::OSVersion.Version"
@@ -131,10 +102,8 @@ for:
# pip will build them from source using the MSVC compiler matching the # pip will build them from source using the MSVC compiler matching the
# target Python version and architecture # target Python version and architecture
- ps: echo("Install pip requirements:") - 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 -r requirements.txt"
- cmd: "python -m pip install PyInstaller==3.6" - cmd: "python -m pip install PyInstaller==6.0.0"
before_build: before_build:
# directory that will contain the built files # directory that will contain the built files
- ps: $env:PYFA_DIST_DIR = "c:\projects\$env:APPVEYOR_PROJECT_SLUG\dist" - ps: $env:PYFA_DIST_DIR = "c:\projects\$env:APPVEYOR_PROJECT_SLUG\dist"
@@ -150,7 +119,7 @@ for:
# Build gamedata DB # Build gamedata DB
- cmd: "python db_update.py" - cmd: "python db_update.py"
# Build command for PyInstaller # 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) # Copy over manifest (See pyfa-org/pyfa#1622)
- ps: xcopy /y dist_assets\win\pyfa.exe.manifest $env:PYFA_DIST_DIR\pyfa\ - 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... # InnoScript EXE building. This is in a separate script because I don't feel like copying over the logic to AppVeyor script right now...
@@ -177,16 +146,15 @@ for:
- -
matrix: matrix:
only: only:
- image: macos - image: macos-catalina
environment: environment:
APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJDW/+oYNGOiPvwuwAL9tc/LQgg58aosIVpMYfepQZ20V+VZnHpZh8IRDA8Jo5xht19p2PksA+hFgqA0kpKtrSkuiWdE8rATQItfk4gf7yB0yGasJGGQZYazy9k/9XtmYkq2HHOOeEqdxvrICddJQ88MLCLT9lJENSUP/YS/yGcjZFXVxE11pTeIcqlCRU+3eYa1v7BeNvXIKNhZoK5orXWrtuH3cy8jrSns/u70aYfJ6B2jA8CnWnDbuvpeQtEY61SQqlKUsSArNa8NAsXj41wr3Ar9gAG9330w7EMTqlutk8HZO35uHI0q5qinUhaQYufPPrVkb2L/N+ZCfu0fnh appveyor" APPVEYOR_SSH_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJDW/+oYNGOiPvwuwAL9tc/LQgg58aosIVpMYfepQZ20V+VZnHpZh8IRDA8Jo5xht19p2PksA+hFgqA0kpKtrSkuiWdE8rATQItfk4gf7yB0yGasJGGQZYazy9k/9XtmYkq2HHOOeEqdxvrICddJQ88MLCLT9lJENSUP/YS/yGcjZFXVxE11pTeIcqlCRU+3eYa1v7BeNvXIKNhZoK5orXWrtuH3cy8jrSns/u70aYfJ6B2jA8CnWnDbuvpeQtEY61SQqlKUsSArNa8NAsXj41wr3Ar9gAG9330w7EMTqlutk8HZO35uHI0q5qinUhaQYufPPrVkb2L/N+ZCfu0fnh appveyor"
cache: cache:
- /Users/appveyor/Library/Caches/pip/ -> requirements.txt - /Users/appveyor/Library/Caches/pip/ -> requirements.txt
init: init:
# - sh: curl -sflL 'https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-ssh.sh' | bash -e - # - 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: install:
- sh: git fetch --prune --unshallow # to fix the version dump issues
- sh: bash scripts/osx-setup.sh - sh: bash scripts/osx-setup.sh
build_script: build_script:
- sh: bash scripts/osx-translations.sh - sh: bash scripts/osx-translations.sh

View File

@@ -2,7 +2,7 @@
## Requirements ## Requirements
- Python 3.7 - Python 3.11
- Git CLI installed - Git CLI installed
- Python, pip and git are all available as command-line commands (add to the path if needed) - Python, pip and git are all available as command-line commands (add to the path if needed)

View File

@@ -9,6 +9,7 @@ import hashlib
from eos.const import FittingSlot from eos.const import FittingSlot
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from collections import namedtuple
pyfalog = Logger(__name__) pyfalog = Logger(__name__)
@@ -44,9 +45,16 @@ experimentalFeatures = None
version = None version = None
language = None language = None
API_CLIENT_ID = '095d8cd841ac40b581330919b49fe746' ApiServer = namedtuple('ApiBase', ['name', 'sso', 'esi', 'client_id', 'callback', 'supports_auto_login'])
supported_servers = {
"Tranquility": ApiServer("Tranquility", "login.eveonline.com", "esi.evetech.net", '095d8cd841ac40b581330919b49fe746', 'https://pyfa-org.github.io/Pyfa/callback', True),
# No point having SISI: https://developers.eveonline.com/blog/article/removing-datasource-singularity
# "Singularity": ApiServer("Singularity", "sisilogin.testeveonline.com", "esi.evetech.net", 'b9c3cc79448f449ab17f3aebd018842e', 'https://pyfa-org.github.io/Pyfa/callback'),
"Serenity": ApiServer("Serenity", "login.evepc.163.com", "ali-esi.evepc.163.com", 'bc90aa496a404724a93f41b4f4e97761', 'https://ali-esi.evepc.163.com/ui/oauth2-redirect.html', False)
}
SSO_LOGOFF_SERENITY='https://login.evepc.163.com/account/logoff'
ESI_CACHE = 'esi_cache' ESI_CACHE = 'esi_cache'
SSO_CALLBACK = 'https://pyfa-org.github.io/Pyfa/callback'
LOGLEVEL_MAP = { LOGLEVEL_MAP = {
"critical": CRITICAL, "critical": CRITICAL,
@@ -58,13 +66,22 @@ LOGLEVEL_MAP = {
CATALOG = 'lang' CATALOG = 'lang'
slotColourMapDark = {
FittingSlot.LOW: wx.Colour(44, 36, 19), # yellow = low slots 24/13
FittingSlot.MED: wx.Colour(28, 39, 51), # blue = mid slots 8.1/9.5
FittingSlot.HIGH: wx.Colour(53, 31, 34), # red = high slots 6.5/11.5
FittingSlot.RIG: '',
FittingSlot.SUBSYSTEM: ''}
errColorDark = wx.Colour(70, 20, 20)
slotColourMap = { slotColourMap = {
FittingSlot.LOW: wx.Colour(250, 235, 204), # yellow = low slots FittingSlot.LOW: wx.Colour(250, 235, 204), # yellow = low slots
FittingSlot.MED: wx.Colour(188, 215, 241), # blue = mid slots FittingSlot.MED: wx.Colour(188, 215, 241), # blue = mid slots
FittingSlot.HIGH: wx.Colour(235, 204, 209), # red = high slots FittingSlot.HIGH: wx.Colour(235, 204, 209), # red = high slots
FittingSlot.RIG: '', FittingSlot.RIG: '',
FittingSlot.SUBSYSTEM: '' FittingSlot.SUBSYSTEM: ''}
} errColor = wx.Colour(204, 51, 51)
def getClientSecret(): def getClientSecret():
return clientHash return clientHash

View File

@@ -794,8 +794,8 @@ def update_db():
_hardcodeAttribs(cybeleTypeID, attrMap) _hardcodeAttribs(cybeleTypeID, attrMap)
_hardcodeEffects(cybeleTypeID, effectMap) _hardcodeEffects(cybeleTypeID, effectMap)
hardcodeShapash() # hardcodeShapash()
hardcodeCybele() # hardcodeCybele()
eos.db.gamedata_session.commit() eos.db.gamedata_session.commit()
eos.db.gamedata_engine.execute('VACUUM') eos.db.gamedata_engine.execute('VACUUM')

View File

@@ -0,0 +1,74 @@
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
# Unknown
- libpcre2-32-0 # https://github.com/pyfa-org/Pyfa/issues/2572
- libnotify4 # https://github.com/pyfa-org/Pyfa/issues/2598
- libwayland-client0 # https://github.com/pyfa-org/Pyfa/issues/2600
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,3 +0,0 @@
#! /bin/bash
${APPDIR}/usr/bin/python3.7 -s "${APPDIR}/opt/pyfa/pyfa.py" "$@"

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,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( app = BUNDLE(
exe, exe,
name='pyfa.app', name='pyfa.app',
version=os.getenv('PYFA_VERSION'),
icon=icon, icon=icon,
bundle_identifier=None, bundle_identifier=None,
info_plist={ info_plist={
@@ -88,5 +89,7 @@ app = BUNDLE(
'CFBundleName': 'pyfa', 'CFBundleName': 'pyfa',
'CFBundleDisplayName': 'pyfa', 'CFBundleDisplayName': 'pyfa',
'CFBundleIdentifier': 'org.pyfaorg.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} DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName} DefaultGroupName={#MyAppName}
AllowNoIcons=yes AllowNoIcons=yes
LicenseFile={#MyAppDir}\LICENSE LicenseFile={#MyAppDir}\app\LICENSE
OutputDir={#MyOutputDir} OutputDir={#MyOutputDir}
OutputBaseFilename={#MyOutputFile} OutputBaseFilename={#MyOutputFile}
SetupIconFile={#MyAppDir}\pyfa.ico SetupIconFile={#MyAppDir}\app\pyfa.ico
SolidCompression=yes SolidCompression=yes
[Languages] [Languages]

View File

@@ -10,10 +10,15 @@
</trustInfo> </trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application> <application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application> </application>
</compatibility> </compatibility>

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ Migration 1
loaded as they no longer exist in the database. We therefore replace these loaded as they no longer exist in the database. We therefore replace these
modules with their new replacements modules with their new replacements
Based on http://community.eveonline.com/news/patch-notes/patch-notes-for-oceanus/ Based on https://www.eveonline.com/news/view/patch-notes-for-oceanus
and output of itemDiff.py and output of itemDiff.py
""" """

View File

@@ -2,7 +2,7 @@
Migration 25 Migration 25
- Converts T3C fitting configurations based on the spreadsheet noted here: - Converts T3C fitting configurations based on the spreadsheet noted here:
https://community.eveonline.com/news/patch-notes/patch-notes-for-july-2017-release https://www.eveonline.com/news/view/patch-notes-for-july-2017-release
(csv copies can be found on the pyfa repo in case the official documents are deleted) (csv copies can be found on the pyfa repo in case the official documents are deleted)
@@ -4228,8 +4228,8 @@ def upgrade(saveddata_engine):
# We don't have a conversion for this. I don't think this will ever happen, but who knows # We don't have a conversion for this. I don't think this will ever happen, but who knows
continue continue
# It doesn't actully matter which old module is replaced with which new module, so we don't have to worry # It doesn't actually matter which old module is replaced with which new module, so we don't have to worry
# about module position or anything like that. Just doe a straight up record UPDATE # about module position or anything like that. Just do a straight up record UPDATE
for i, old in enumerate(oldModules[:4]): for i, old in enumerate(oldModules[:4]):
saveddata_engine.execute("UPDATE modules SET itemID = ? WHERE ID = ?", (newModules[i], old[0])) saveddata_engine.execute("UPDATE modules SET itemID = ? WHERE ID = ?", (newModules[i], old[0]))

View File

@@ -6,7 +6,7 @@ Migration 4
from database), which causes pyfa to crash. We therefore replace these from database), which causes pyfa to crash. We therefore replace these
modules with their new replacements modules with their new replacements
Based on http://community.eveonline.com/news/patch-notes/patch-notes-for-proteus/ Based on https://www.eveonline.com/news/view/patch-notes-for-proteus
and output of itemDiff.py and output of itemDiff.py
""" """

View File

@@ -0,0 +1,36 @@
"""
Migration 46
- add support for server selection for SSO characters
"""
import sqlalchemy
tmpTable = """
CREATE TABLE ssoCharacterTemp (
ID INTEGER NOT NULL,
client VARCHAR NOT NULL,
characterID INTEGER NOT NULL,
characterName VARCHAR NOT NULL,
refreshToken VARCHAR NOT NULL,
accessToken VARCHAR NOT NULL,
accessTokenExpires DATETIME NOT NULL,
created DATETIME,
modified DATETIME,
server VARCHAR,
PRIMARY KEY (ID),
CONSTRAINT "uix_client_server_characterID" UNIQUE (client, server, characterID),
CONSTRAINT "uix_client_server_characterName" UNIQUE (client, server, characterName)
)
"""
def upgrade(saveddata_engine):
try:
saveddata_engine.execute("SELECT server FROM ssoCharacter LIMIT 1")
except sqlalchemy.exc.DatabaseError:
saveddata_engine.execute(tmpTable)
saveddata_engine.execute(
"INSERT INTO ssoCharacterTemp (ID, client, characterID, characterName, refreshToken, accessToken, accessTokenExpires, created, modified, server) "
"SELECT ID, client, characterID, characterName, refreshToken, accessToken, accessTokenExpires, created, modified, 'Tranquility' "
"FROM ssoCharacter")
saveddata_engine.execute("DROP TABLE ssoCharacter")
saveddata_engine.execute("ALTER TABLE ssoCharacterTemp RENAME TO ssoCharacter")

View File

@@ -40,18 +40,18 @@ characters_table = Table("characters", saveddata_meta,
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)) Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now))
sso_table = Table("ssoCharacter", saveddata_meta, sso_table = Table("ssoCharacter", saveddata_meta,
Column("ID", Integer, primary_key=True), Column("ID", Integer, primary_key=True),
Column("client", String, nullable=False), Column("client", String, nullable=False),
Column("characterID", Integer, nullable=False), Column("characterID", Integer, nullable=False),
Column("characterName", String, nullable=False), Column("characterName", String, nullable=False),
Column("refreshToken", String, nullable=False), Column("server", String, nullable=False),
Column("accessToken", String, nullable=False), Column("refreshToken", String, nullable=False),
Column("accessTokenExpires", DateTime, nullable=False), Column("accessToken", String, nullable=False),
Column("created", DateTime, nullable=True, default=datetime.datetime.now), Column("accessTokenExpires", DateTime, nullable=False),
Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now), Column("created", DateTime, nullable=True, default=datetime.datetime.now),
UniqueConstraint('client', 'characterID', name='uix_client_characterID'), Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now),
UniqueConstraint('client', 'characterName', name='uix_client_characterName') UniqueConstraint('client', 'server', 'characterID', name='uix_client_server_characterID'),
) UniqueConstraint('client', 'server', 'characterName', name='uix_client_server_characterName'))
sso_character_map_table = Table("ssoCharacterMap", saveddata_meta, sso_character_map_table = Table("ssoCharacterMap", saveddata_meta,
Column("characterID", ForeignKey("characters.ID"), primary_key=True), Column("characterID", ForeignKey("characters.ID"), primary_key=True),

View File

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

View File

@@ -493,9 +493,12 @@ def getSsoCharacters(clientHash, eager=None):
@cachedQuery(SsoCharacter, 1, "lookfor", "clientHash") @cachedQuery(SsoCharacter, 1, "lookfor", "clientHash")
def getSsoCharacter(lookfor, clientHash, eager=None): def getSsoCharacter(lookfor, clientHash, server=None, eager=None):
filter = SsoCharacter.client == clientHash filter = SsoCharacter.client == clientHash
if server is not None:
filter = and_(filter, SsoCharacter.server == server)
if isinstance(lookfor, int): if isinstance(lookfor, int):
filter = and_(filter, SsoCharacter.ID == lookfor) filter = and_(filter, SsoCharacter.ID == lookfor)
elif isinstance(lookfor, str): elif isinstance(lookfor, str):

View File

@@ -966,8 +966,8 @@ class Effect272(BaseEffect):
Used by: Used by:
Implants named like: Inherent Implants 'Noble' Repair Systems RS (6 of 6) Implants named like: Inherent Implants 'Noble' Repair Systems RS (6 of 6)
Implants named like: Repairer Booster (4 of 4)
Modules named like: Nanobot Accelerator (8 of 8) Modules named like: Nanobot Accelerator (8 of 8)
Implant: AIR Repairer Booster III
Implant: Numon Family Heirloom Implant: Numon Family Heirloom
Skill: Repair Systems Skill: Repair Systems
""" """
@@ -1060,6 +1060,7 @@ class Effect290(BaseEffect):
Used by: Used by:
Implants named like: Frentix Booster (4 of 4) Implants named like: Frentix Booster (4 of 4)
Implants named like: Halcyon B Booster (5 of 5) Implants named like: Halcyon B Booster (5 of 5)
Implants named like: SoCT Turret Booster (3 of 3)
Implants named like: Zainou 'Deadeye' Sharpshooter ST (6 of 6) Implants named like: Zainou 'Deadeye' Sharpshooter ST (6 of 6)
Skill: Sharpshooter Skill: Sharpshooter
""" """
@@ -1169,6 +1170,7 @@ class Effect394(BaseEffect):
Implant: AIR Overclocker Booster II Implant: AIR Overclocker Booster II
Implant: Quafe Zero Classic Implant: Quafe Zero Classic
Implant: Serenity YC122.9 Season Booster - Max Velocity Implant: Serenity YC122.9 Season Booster - Max Velocity
Implant: Starsi Blast! Classic
Implant: Wisdom of Gheinok Implant: Wisdom of Gheinok
Skill: Navigation Skill: Navigation
""" """
@@ -1190,17 +1192,18 @@ class Effect395(BaseEffect):
Used by: Used by:
Modules from group: Rig Anchor (4 of 4) Modules from group: Rig Anchor (4 of 4)
Implants named like: Agility Booster (4 of 4)
Implants named like: Eifyr and Co. 'Rogue' Evasive Maneuvering EM (6 of 6) Implants named like: Eifyr and Co. 'Rogue' Evasive Maneuvering EM (6 of 6)
Implants named like: Grand Prix Booster (4 of 6) Implants named like: Grand Prix Booster (4 of 6)
Implants named like: Halcyon G Booster (5 of 5) Implants named like: Halcyon G Booster (5 of 5)
Implants named like: Halcyon Y Booster (5 of 5) Implants named like: Halcyon Y Booster (5 of 5)
Implants named like: grade Nomad (10 of 12) Implants named like: grade Nomad (10 of 12)
Modules named like: Low Friction Nozzle Joints (8 of 8) Modules named like: Low Friction Nozzle Joints (8 of 8)
Implant: AIR Agility Booster II
Implant: AIR Overclocker Booster III Implant: AIR Overclocker Booster III
Implant: Genolution Core Augmentation CA-4 Implant: Genolution Core Augmentation CA-4
Implant: Quafe Zero Green Apple Implant: Quafe Zero Green Apple
Implant: Serenity YC122.9 Season Booster - Agility Implant: Serenity YC122.9 Season Booster - Agility
Implant: Starsi Blast! Classic
Skill: Evasive Maneuvering Skill: Evasive Maneuvering
Skill: Spaceship Command Skill: Spaceship Command
""" """
@@ -1335,6 +1338,7 @@ class Effect485(BaseEffect):
Implant: Basic Capsuleer Engineering Augmentation Chip Implant: Basic Capsuleer Engineering Augmentation Chip
Implant: Genolution Core Augmentation CA-2 Implant: Genolution Core Augmentation CA-2
Implant: Quafe Zero Green Apple Implant: Quafe Zero Green Apple
Implant: Starsi Blast! Orange
Skill: Capacitor Systems Operation Skill: Capacitor Systems Operation
""" """
@@ -1511,6 +1515,7 @@ class Effect512(BaseEffect):
Ship: Atron Ship: Atron
Ship: Federation Navy Comet Ship: Federation Navy Comet
Ship: Pacifier Ship: Pacifier
Ship: Shapash
Ship: Taranis Ship: Taranis
""" """
@@ -1712,6 +1717,7 @@ class Effect562(BaseEffect):
Variations of ship: Vexor (3 of 4) Variations of ship: Vexor (3 of 4)
Ship: Adrestia Ship: Adrestia
Ship: Arazu Ship: Arazu
Ship: Cybele
Ship: Deimos Ship: Deimos
Ship: Enforcer Ship: Enforcer
Ship: Exequror Navy Issue Ship: Exequror Navy Issue
@@ -1769,6 +1775,7 @@ class Effect584(BaseEffect):
Implants named like: 'Pyrolancea' Dose (7 of 7) Implants named like: 'Pyrolancea' Dose (7 of 7)
Implants named like: Eifyr and Co. 'Gunslinger' Surgical Strike SS (6 of 6) Implants named like: Eifyr and Co. 'Gunslinger' Surgical Strike SS (6 of 6)
Implants named like: Halcyon Y Booster (5 of 5) Implants named like: Halcyon Y Booster (5 of 5)
Implants named like: SoCT Turret Booster (3 of 3)
Implant: AIR Pyrolancea Booster II Implant: AIR Pyrolancea Booster II
Implant: Standard Cerebral Accelerator Implant: Standard Cerebral Accelerator
""" """
@@ -2151,9 +2158,11 @@ class Effect699(BaseEffect):
Used by: Used by:
Implants named like: Halcyon B Booster (5 of 5) Implants named like: Halcyon B Booster (5 of 5)
Implants named like: Halcyon R Booster (5 of 5) Implants named like: Halcyon R Booster (5 of 5)
Implants named like: SoCT Scan Booster (3 of 3)
Implants named like: Zainou 'Gypsy' Signature Analysis SA (6 of 6) Implants named like: Zainou 'Gypsy' Signature Analysis SA (6 of 6)
Modules named like: Targeting System Subcontroller (8 of 8) Modules named like: Targeting System Subcontroller (8 of 8)
Implant: Quafe Zero Classic Implant: Quafe Zero Classic
Implant: Starsi Blast! Orange
Skill: Signature Analysis Skill: Signature Analysis
""" """
@@ -2500,6 +2509,7 @@ class Effect856(BaseEffect):
Implants named like: Grand Prix Booster (5 of 6) Implants named like: Grand Prix Booster (5 of 6)
Implants named like: Halcyon B Booster (5 of 5) Implants named like: Halcyon B Booster (5 of 5)
Implants named like: Serenity Limited 'Overclocker' Dose (3 of 3) Implants named like: Serenity Limited 'Overclocker' Dose (3 of 3)
Implants named like: SoCT Agility Booster (3 of 3)
Implants named like: grade Ascendancy (10 of 12) Implants named like: grade Ascendancy (10 of 12)
Modules named like: Hyperspatial Velocity Optimizer (8 of 8) Modules named like: Hyperspatial Velocity Optimizer (8 of 8)
Implant: Serenity YC122.9 Season Booster - Warp Speed Implant: Serenity YC122.9 Season Booster - Warp Speed
@@ -3267,24 +3277,6 @@ class Effect1024(BaseEffect):
skill='Caldari Cruiser', **kwargs) skill='Caldari Cruiser', **kwargs)
class Effect1025(BaseEffect):
"""
shipMissileLightVelocityBonusCC2
Used by:
Ship: Caracal
Ship: Osprey Navy Issue
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill('Light Missiles'),
'maxVelocity', ship.getModifiedItemAttr('shipBonusCC2'),
skill='Caldari Cruiser', **kwargs)
class Effect1030(BaseEffect): class Effect1030(BaseEffect):
""" """
remoteArmorSystemsCapNeedBonusPostPercentCapacitorNeedLocationShipModulesRequiringRemoteArmorSystems remoteArmorSystemsCapNeedBonusPostPercentCapacitorNeedLocationShipModulesRequiringRemoteArmorSystems
@@ -4906,8 +4898,8 @@ class Effect1635(BaseEffect):
capitalRepairSystemsSkillDurationBonus capitalRepairSystemsSkillDurationBonus
Used by: Used by:
Implants named like: Repairer Booster (4 of 4)
Modules named like: Nanobot Accelerator (8 of 8) Modules named like: Nanobot Accelerator (8 of 8)
Implant: AIR Repairer Booster III
Skill: Capital Repair Systems Skill: Capital Repair Systems
""" """
@@ -5630,8 +5622,8 @@ class Effect1882(BaseEffect):
miningYieldMultiplyPercent miningYieldMultiplyPercent
Used by: Used by:
Modules from group: Mining Upgrade (7 of 12)
Variations of module: Mining Laser Upgrade I (5 of 5) Variations of module: Mining Laser Upgrade I (5 of 5)
Module: Frostline 'Omnivore' Harvester Upgrade
""" """
type = 'passive' type = 'passive'
@@ -6802,8 +6794,8 @@ class Effect2255(BaseEffect):
tractorBeamCan tractorBeamCan
Used by: Used by:
Deployables from group: Mobile Tractor Unit (4 of 4) Deployables from group: Mobile Tractor Unit (5 of 5)
Modules from group: Tractor Beam (4 of 4) Modules from group: Tractor Beam (6 of 6)
""" """
type = 'active' type = 'active'
@@ -6815,7 +6807,6 @@ class Effect2296(BaseEffect):
Used by: Used by:
Implants named like: Halcyon Y Booster (5 of 5) Implants named like: Halcyon Y Booster (5 of 5)
Implants named like: Tetrimon Resistance Booster (4 of 4)
""" """
type = 'passive' type = 'passive'
@@ -7013,7 +7004,7 @@ class Effect2432(BaseEffect):
Implants named like: Halcyon Y Booster (5 of 5) Implants named like: Halcyon Y Booster (5 of 5)
Implants named like: Inherent Implants 'Squire' Capacitor Management EM (6 of 6) Implants named like: Inherent Implants 'Squire' Capacitor Management EM (6 of 6)
Implants named like: Mindflood Booster (4 of 4) Implants named like: Mindflood Booster (4 of 4)
Implants named like: Tetrimon Capacitor Booster (4 of 4) Implants named like: SoCT Capacitor Booster (3 of 3)
Modules named like: Semiconductor Memory Cell (8 of 8) Modules named like: Semiconductor Memory Cell (8 of 8)
Implant: Antipharmakon Aeolis Implant: Antipharmakon Aeolis
Implant: Basic Capsuleer Engineering Augmentation Chip Implant: Basic Capsuleer Engineering Augmentation Chip
@@ -7034,8 +7025,8 @@ class Effect2444(BaseEffect):
minerCpuUsageMultiplyPercent2 minerCpuUsageMultiplyPercent2
Used by: Used by:
Modules from group: Mining Upgrade (7 of 12)
Variations of module: Mining Laser Upgrade I (5 of 5) Variations of module: Mining Laser Upgrade I (5 of 5)
Module: Frostline 'Omnivore' Harvester Upgrade
""" """
type = 'passive' type = 'passive'
@@ -7638,7 +7629,6 @@ class Effect2696(BaseEffect):
maxRangeBonusEffectLasers maxRangeBonusEffectLasers
Used by: Used by:
Implants named like: Tetrimon Precision Booster (4 of 4)
Modules named like: Energy Locus Coordinator (8 of 8) Modules named like: Energy Locus Coordinator (8 of 8)
""" """
@@ -8389,7 +8379,6 @@ class Effect2803(BaseEffect):
energyWeaponDamageMultiplyPassive energyWeaponDamageMultiplyPassive
Used by: Used by:
Implants named like: Harvest Damage Booster (4 of 4)
Modules named like: Energy Collision Accelerator (8 of 8) Modules named like: Energy Collision Accelerator (8 of 8)
Implant: Wisdom of Gheinok Implant: Wisdom of Gheinok
""" """
@@ -10094,23 +10083,6 @@ class Effect3356(BaseEffect):
skill='Heavy Interdiction Cruisers', **kwargs) skill='Heavy Interdiction Cruisers', **kwargs)
class Effect3357(BaseEffect):
"""
eliteBonusHeavyInterdictorLightMissileVelocityBonus
Used by:
Ship: Onyx
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill('Light Missiles'),
'maxVelocity', ship.getModifiedItemAttr('eliteBonusHeavyInterdictors1'),
skill='Heavy Interdiction Cruisers', **kwargs)
class Effect3366(BaseEffect): class Effect3366(BaseEffect):
""" """
shipRemoteSensorDampenerCapNeedGF shipRemoteSensorDampenerCapNeedGF
@@ -13521,6 +13493,7 @@ class Effect4162(BaseEffect):
Implants named like: Poteque 'Prospector' Astrometric Rangefinding AR (3 of 3) Implants named like: Poteque 'Prospector' Astrometric Rangefinding AR (3 of 3)
Implants named like: Poteque 'Prospector' Sharpeye (2 of 2) Implants named like: Poteque 'Prospector' Sharpeye (2 of 2)
Implants named like: Serenity Limited 'Sharpeye' Dose (3 of 3) Implants named like: Serenity Limited 'Sharpeye' Dose (3 of 3)
Implants named like: SoCT Scan Booster (3 of 3)
Implants named like: grade Virtue (10 of 12) Implants named like: grade Virtue (10 of 12)
Modules named like: Gravity Capacitor Upgrade (8 of 8) Modules named like: Gravity Capacitor Upgrade (8 of 8)
Implant: AIR Astro-Rangefinding II Booster Implant: AIR Astro-Rangefinding II Booster
@@ -14883,6 +14856,7 @@ class Effect4473(BaseEffect):
Used by: Used by:
Ship: Adrestia Ship: Adrestia
Ship: Cybele
Ship: Mimir Ship: Mimir
""" """
@@ -15443,6 +15417,7 @@ class Effect4620(BaseEffect):
Used by: Used by:
Ship: Garmur Ship: Garmur
Ship: Shapash
Ship: Utu Ship: Utu
""" """
@@ -15461,6 +15436,7 @@ class Effect4621(BaseEffect):
Used by: Used by:
Ship: Cambion Ship: Cambion
Ship: Etana Ship: Etana
Ship: Shapash
Ship: Utu Ship: Utu
""" """
@@ -15542,6 +15518,7 @@ class Effect4626(BaseEffect):
Used by: Used by:
Ship: Adrestia Ship: Adrestia
Ship: Cybele
Ship: Orthrus Ship: Orthrus
""" """
@@ -16660,8 +16637,8 @@ class Effect4967(BaseEffect):
shieldBoosterDurationBonusShieldSkills shieldBoosterDurationBonusShieldSkills
Used by: Used by:
Implants named like: Repairer Booster (4 of 4)
Modules named like: Core Defense Operational Solidifier (8 of 8) Modules named like: Core Defense Operational Solidifier (8 of 8)
Implant: AIR Repairer Booster III
""" """
type = 'passive' type = 'passive'
@@ -17355,9 +17332,10 @@ class Effect5069(BaseEffect):
@staticmethod @staticmethod
def handler(fit, module, context, projectionRange, **kwargs): def handler(fit, module, context, projectionRange, **kwargs):
fit.modules.filteredChargeBoost(lambda mod: mod.charge.requiresSkill('Mercoxit Processing'), fit.modules.filteredChargeBoost(
'specializationAsteroidYieldMultiplier', lambda mod: mod.charge.requiresSkill('Mercoxit Ore Processing'),
module.getModifiedItemAttr('miningAmountBonus'), **kwargs) 'specializationAsteroidYieldMultiplier',
module.getModifiedItemAttr('miningAmountBonus'), **kwargs)
class Effect5079(BaseEffect): class Effect5079(BaseEffect):
@@ -18040,7 +18018,6 @@ class Effect5189(BaseEffect):
trackingSpeedBonusEffectLasers trackingSpeedBonusEffectLasers
Used by: Used by:
Implants named like: Tetrimon Precision Booster (4 of 4)
Modules named like: Energy Metastasis Adjuster (8 of 8) Modules named like: Energy Metastasis Adjuster (8 of 8)
""" """
@@ -20208,6 +20185,7 @@ class Effect5437(BaseEffect):
Used by: Used by:
Implants named like: Halcyon Y Booster (5 of 5) Implants named like: Halcyon Y Booster (5 of 5)
Implants named like: SoCT Relic Coherence Booster (3 of 3)
Modules named like: Emission Scope Sharpener (8 of 8) Modules named like: Emission Scope Sharpener (8 of 8)
Implant: Poteque 'Prospector' Archaeology AC-905 Implant: Poteque 'Prospector' Archaeology AC-905
Implant: Poteque 'Prospector' Environmental Analysis EY-1005 Implant: Poteque 'Prospector' Environmental Analysis EY-1005
@@ -24749,7 +24727,7 @@ class Effect6185(BaseEffect):
srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'), srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'),
distance=projectionRange) distance=projectionRange)
duration = module.getModifiedItemAttr('duration') / 1000.0 duration = module.getModifiedItemAttr('duration') / 1000.0
fit.extraAttributes.increase('hullRepair', bonus / duration, **kwargs) fit._hullRr.append((bonus, duration))
class Effect6186(BaseEffect): class Effect6186(BaseEffect):
@@ -24774,7 +24752,7 @@ class Effect6186(BaseEffect):
srcFalloffRange=container.getModifiedItemAttr('falloffEffectiveness'), srcFalloffRange=container.getModifiedItemAttr('falloffEffectiveness'),
distance=projectionRange) distance=projectionRange)
duration = container.getModifiedItemAttr('duration') / 1000.0 duration = container.getModifiedItemAttr('duration') / 1000.0
fit.extraAttributes.increase('shieldRepair', bonus / duration, **kwargs) fit._shieldRr.append((bonus, duration))
class Effect6187(BaseEffect): class Effect6187(BaseEffect):
@@ -24828,10 +24806,9 @@ class Effect6188(BaseEffect):
srcFalloffRange=container.getModifiedItemAttr('falloffEffectiveness'), srcFalloffRange=container.getModifiedItemAttr('falloffEffectiveness'),
distance=projectionRange) distance=projectionRange)
duration = container.getModifiedItemAttr('duration') / 1000.0 duration = container.getModifiedItemAttr('duration') / 1000.0
rps = bonus / duration fit._armorRr.append((bonus, duration))
fit.extraAttributes.increase('armorRepair', rps, **kwargs) fit._armorRrPreSpool.append((bonus, duration))
fit.extraAttributes.increase('armorRepairPreSpool', rps, **kwargs) fit._armorRrFullSpool.append((bonus, duration))
fit.extraAttributes.increase('armorRepairFullSpool', rps, **kwargs)
class Effect6195(BaseEffect): class Effect6195(BaseEffect):
@@ -29788,10 +29765,9 @@ class Effect6651(BaseEffect):
srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'), srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'),
distance=projectionRange) distance=projectionRange)
speed = module.getModifiedItemAttr('duration') / 1000.0 speed = module.getModifiedItemAttr('duration') / 1000.0
rps = amount / speed fit._armorRr.append((amount, speed))
fit.extraAttributes.increase('armorRepair', rps, **kwargs) fit._armorRrPreSpool.append((amount, speed))
fit.extraAttributes.increase('armorRepairPreSpool', rps, **kwargs) fit._armorRrFullSpool.append((amount, speed))
fit.extraAttributes.increase('armorRepairFullSpool', rps, **kwargs)
class Effect6652(BaseEffect): class Effect6652(BaseEffect):
@@ -29817,7 +29793,7 @@ class Effect6652(BaseEffect):
srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'), srcFalloffRange=module.getModifiedItemAttr('falloffEffectiveness'),
distance=projectionRange) distance=projectionRange)
speed = module.getModifiedItemAttr('duration') / 1000.0 speed = module.getModifiedItemAttr('duration') / 1000.0
fit.extraAttributes.increase('shieldRepair', amount / speed, **kwargs) fit._shieldRr.append((amount, speed))
class Effect6653(BaseEffect): class Effect6653(BaseEffect):
@@ -30446,10 +30422,9 @@ class Effect6687(BaseEffect):
return return
bonus = container.getModifiedItemAttr('armorDamageAmount') bonus = container.getModifiedItemAttr('armorDamageAmount')
duration = container.getModifiedItemAttr('duration') / 1000.0 duration = container.getModifiedItemAttr('duration') / 1000.0
rps = bonus / duration fit._armorRr.append((bonus, duration))
fit.extraAttributes.increase('armorRepair', rps, **kwargs) fit._armorRrPreSpool.append((bonus, duration))
fit.extraAttributes.increase('armorRepairPreSpool', rps, **kwargs) fit._armorRrFullSpool.append((bonus, duration))
fit.extraAttributes.increase('armorRepairFullSpool', rps, **kwargs)
class Effect6688(BaseEffect): class Effect6688(BaseEffect):
@@ -30472,7 +30447,7 @@ class Effect6688(BaseEffect):
return return
bonus = container.getModifiedItemAttr('shieldBonus') bonus = container.getModifiedItemAttr('shieldBonus')
duration = container.getModifiedItemAttr('duration') / 1000.0 duration = container.getModifiedItemAttr('duration') / 1000.0
fit.extraAttributes.increase('shieldRepair', bonus / duration, **kwargs) fit._shieldRr.append((bonus, duration))
class Effect6689(BaseEffect): class Effect6689(BaseEffect):
@@ -30496,7 +30471,7 @@ class Effect6689(BaseEffect):
return return
bonus = module.getModifiedItemAttr('structureDamageAmount') bonus = module.getModifiedItemAttr('structureDamageAmount')
duration = module.getModifiedItemAttr('duration') / 1000.0 duration = module.getModifiedItemAttr('duration') / 1000.0
fit.extraAttributes.increase('hullRepair', bonus / duration, **kwargs) fit._hullRr.append((bonus, duration))
class Effect6690(BaseEffect): class Effect6690(BaseEffect):
@@ -31607,7 +31582,7 @@ class Effect6783(BaseEffect):
Used by: Used by:
Ships from group: Carrier (4 of 4) Ships from group: Carrier (4 of 4)
Ships from group: Combat Battlecruiser (20 of 20) Ships from group: Combat Battlecruiser (20 of 20)
Ships from group: Command Ship (4 of 8) Ships from group: Command Ship (8 of 8)
Ships from group: Force Auxiliary (6 of 6) Ships from group: Force Auxiliary (6 of 6)
Ships from group: Supercarrier (6 of 6) Ships from group: Supercarrier (6 of 6)
Ships from group: Titan (8 of 8) Ships from group: Titan (8 of 8)
@@ -35047,12 +35022,12 @@ class Effect7166(BaseEffect):
repSpoolPerCycle = container.getModifiedItemAttr('repairMultiplierBonusPerCycle') repSpoolPerCycle = container.getModifiedItemAttr('repairMultiplierBonusPerCycle')
defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage'] defaultSpoolValue = eos.config.settings['globalDefaultSpoolupPercentage']
spoolType, spoolAmount = resolveSpoolOptions(SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False), container) spoolType, spoolAmount = resolveSpoolOptions(SpoolOptions(SpoolType.SPOOL_SCALE, defaultSpoolValue, False), container)
rps = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, spoolType, spoolAmount)[0]) / cycleTime amount = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, spoolType, spoolAmount)[0])
rpsPreSpool = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SPOOL_SCALE, 0)[0]) / cycleTime amountPreSpool = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SPOOL_SCALE, 0)[0])
rpsFullSpool = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SPOOL_SCALE, 1)[0]) / cycleTime amountFullSpool = repAmountBase * (1 + calculateSpoolup(repSpoolMax, repSpoolPerCycle, cycleTime, SpoolType.SPOOL_SCALE, 1)[0])
fit.extraAttributes.increase('armorRepair', rps, **kwargs) fit._armorRr.append((amount, cycleTime))
fit.extraAttributes.increase('armorRepairPreSpool', rpsPreSpool, **kwargs) fit._armorRrPreSpool.append((amountPreSpool, cycleTime))
fit.extraAttributes.increase('armorRepairFullSpool', rpsFullSpool, **kwargs) fit._armorRrFullSpool.append((amountFullSpool, cycleTime))
class Effect7167(BaseEffect): class Effect7167(BaseEffect):
@@ -36009,6 +35984,7 @@ class Effect8039(BaseEffect):
upwellSkillaoeVelocityaoeCloudSizeBonus upwellSkillaoeVelocityaoeCloudSizeBonus
Used by: Used by:
Implants named like: Halcyon G Booster (5 of 5)
Skill: Vorton Arc Guidance Skill: Vorton Arc Guidance
""" """
@@ -36258,12 +36234,30 @@ class Effect8065(BaseEffect):
'maxRange', skill.getModifiedItemAttr('rangeSkillBonus') * skill.level, **kwargs) 'maxRange', skill.getModifiedItemAttr('rangeSkillBonus') * skill.level, **kwargs)
class Effect8064(BaseEffect):
"""
vortonProjectorOptimalRangeBonus
Used by:
Implants named like: Halcyon B Booster (5 of 5)
"""
type = 'passive'
@staticmethod
def handler(fit, booster, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Vorton Projector Operation'), 'maxRange',
booster.getModifiedItemAttr('rangeSkillBonus'), **kwargs)
class Effect8066(BaseEffect): class Effect8066(BaseEffect):
""" """
vortonProjectorDamageBonus vortonProjectorDamageBonus
Used by: Used by:
Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4) Implants named like: Agency 'Pyrolancea' DB Dose (4 of 4)
Implants named like: Halcyon Y Booster (5 of 5)
Implant: AIR Pyrolancea Booster II Implant: AIR Pyrolancea Booster II
""" """
@@ -37665,64 +37659,12 @@ class Effect8264(BaseEffect):
skill='Industrial Command Ships', **kwargs) skill='Industrial Command Ships', **kwargs)
class Effect8267(BaseEffect):
"""
weaponDisruptorResistanceBonusPassive
Used by:
Implants named like: Harvest Anti Disruptor Booster (4 of 4)
"""
type = 'passive'
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
fit.ship.boostItemAttr(
'weaponDisruptionResistance',
container.getModifiedItemAttr('weaponDisruptionResistanceBonus'), **kwargs)
class Effect8268(BaseEffect):
"""
nosferatuDurationBonusPassive
Used by:
Implants named like: Harvest Nosferatu Booster (4 of 4)
"""
type = 'passive'
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.group.name == 'Energy Nosferatu', 'duration',
module.getModifiedItemAttr('durationBonus'), **kwargs)
class Effect8269(BaseEffect):
"""
stasisWebifierMaxRangeAddPassive
Used by:
Implants named like: Harvest Webifier Booster (4 of 4)
"""
type = 'passive'
@staticmethod
def handler(fit, module, context, projectionRange, **kwargs):
fit.modules.filteredItemIncrease(
lambda mod: mod.item.group.name == 'Stasis Web', 'maxRange',
module.getModifiedItemAttr('stasisWebRangeAdd'), **kwargs)
class Effect8270(BaseEffect): class Effect8270(BaseEffect):
""" """
capacitorWarfareResistanceBonusPassive capacitorWarfareResistanceBonusPassive
Used by: Used by:
Implants named like: Halcyon Y Booster (5 of 5) Implants named like: Halcyon Y Booster (5 of 5)
Implants named like: Tetrimon Anti Drain Booster (4 of 4)
""" """
type = 'passive' type = 'passive'
@@ -38073,6 +38015,40 @@ class Effect8323(BaseEffect):
skill='Gallente Hauler', **kwargs) skill='Gallente Hauler', **kwargs)
class Effect8327(BaseEffect):
"""
relicAnalyzerRangeBonusPassive
Used by:
Implants named like: SoCT Relic Range Booster (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Archaeology'), 'maxRange',
container.getModifiedItemAttr('rangeSkillBonus'), **kwargs)
class Effect8328(BaseEffect):
"""
relicVirusStrengthBonusPassive
Used by:
Implants named like: SoCT Relic Strength Booster (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, container, context, projectionRange, **kwargs):
fit.modules.filteredItemIncrease(
lambda mod: mod.item.requiresSkill('Archaeology'), 'virusStrength',
container.getModifiedItemAttr('virusStrengthBonus'), **kwargs)
class Effect8360(BaseEffect): class Effect8360(BaseEffect):
""" """
shipBonusMissileReloadTimeGC2 shipBonusMissileReloadTimeGC2
@@ -38291,6 +38267,23 @@ class Effect8479(BaseEffect):
container.getModifiedItemAttr('falloffBonus'), **kwargs) container.getModifiedItemAttr('falloffBonus'), **kwargs)
class Effect8594(BaseEffect):
"""
modifyArmorDamageResistanceBonusPostPercentPassive
Used by:
Implants named like: SoCT Armor Booster (3 of 3)
"""
type = 'passive'
@staticmethod
def handler(fit, booster, context, projectionRange, **kwargs):
for type in ('Em', 'Explosive', 'Kinetic', 'Thermal'):
fit.ship.boostItemAttr(f'armor{type}DamageResonance',
booster.getModifiedItemAttr('armorDamageResistanceBonus'), **kwargs)
class Effect11055(BaseEffect): class Effect11055(BaseEffect):
""" """
shipBonusBattlecruiserHeavyMissileAoeVelocityMBC1 shipBonusBattlecruiserHeavyMissileAoeVelocityMBC1
@@ -40101,6 +40094,23 @@ class Effect11764(BaseEffect):
'speed', ship.getModifiedItemAttr('shipBonusRole7'), **kwargs) 'speed', ship.getModifiedItemAttr('shipBonusRole7'), **kwargs)
class Effect11767(BaseEffect):
"""
shipBonusHybridTrackingATC3
Used by:
Ship: Cybele
"""
type = 'passive'
@staticmethod
def handler(fit, src, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Medium Hybrid Turret'), 'trackingSpeed',
src.getModifiedItemAttr('eliteBonusHeavyGunship1'), skill='Heavy Assault Cruisers', **kwargs)
class Effect11919(BaseEffect): class Effect11919(BaseEffect):
""" """
shipBonusDestroyerMD1Falloff shipBonusDestroyerMD1Falloff
@@ -40326,11 +40336,12 @@ class Effect11953(BaseEffect):
stackingPenalties=True, penaltyGroup='postMul', **kwargs) stackingPenalties=True, penaltyGroup='postMul', **kwargs)
class Effect100100(BaseEffect): class Effect11992(BaseEffect):
""" """
pyfaCustomShapashAfArAmount shipBonusArmorPlateMassAT
Used by: Used by:
Ship: Cybele
Ship: Shapash Ship: Shapash
""" """
@@ -40339,13 +40350,47 @@ class Effect100100(BaseEffect):
@staticmethod @staticmethod
def handler(fit, ship, context, projectionRange, **kwargs): def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost( fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Repair Systems'), lambda mod: mod.item.group.name == 'Armor Plate', 'massAddition',
'armorDamageAmount', 10, skill='Assault Frigates', **kwargs) ship.getModifiedItemAttr('shipBonusATF3'), **kwargs)
class Effect100101(BaseEffect): class Effect11993(BaseEffect):
""" """
pyfaCustomShapashAfShtTrackingOptimal shipBonusRepairSystemsBonusATC3
Used by:
Ship: Cybele
"""
type = 'passive'
@staticmethod
def handler(fit, src, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Repair Systems'), 'armorDamageAmount',
src.getModifiedItemAttr('shipBonusGC3'), skill='Gallente Cruiser', **kwargs)
class Effect11994(BaseEffect):
"""
shipBonusHybridFalloffATC3
Used by:
Ship: Cybele
"""
type = 'passive'
@staticmethod
def handler(fit, src, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Medium Hybrid Turret'), 'falloff',
src.getModifiedItemAttr('eliteBonusHeavyGunship2'), skill='Heavy Assault Cruisers', **kwargs)
class Effect11995(BaseEffect):
"""
shipBonusHeatAfterburnerATGF
Used by: Used by:
Ship: Shapash Ship: Shapash
@@ -40354,18 +40399,15 @@ class Effect100101(BaseEffect):
type = 'passive' type = 'passive'
@staticmethod @staticmethod
def handler(fit, ship, context, projectionRange, **kwargs): def handler(fit, src, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost( fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Small Hybrid Turret'), lambda mod: mod.item.requiresSkill('Afterburner'), 'overloadSpeedFactorBonus',
'maxRange', 10, skill='Assault Frigates', **kwargs) src.getModifiedItemAttr('shipBonusGF2'), skill='Gallente Frigate', **kwargs)
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Small Hybrid Turret'),
'trackingSpeed', 10, skill='Assault Frigates', **kwargs)
class Effect100102(BaseEffect): class Effect11996(BaseEffect):
""" """
pyfaCustomShapashGfShtDamage shipBonusMWDHeatATGF
Used by: Used by:
Ship: Shapash Ship: Shapash
@@ -40374,15 +40416,15 @@ class Effect100102(BaseEffect):
type = 'passive' type = 'passive'
@staticmethod @staticmethod
def handler(fit, ship, context, projectionRange, **kwargs): def handler(fit, src, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost( fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Small Hybrid Turret'), lambda mod: mod.item.requiresSkill('High Speed Maneuvering'), 'overloadSpeedFactorBonus',
'damageMultiplier', 10, skill='Gallente Frigate', **kwargs) src.getModifiedItemAttr('shipBonusGF2'), skill='Gallente Frigate', **kwargs)
class Effect100103(BaseEffect): class Effect11997(BaseEffect):
""" """
pyfaCustomShapashGfPointRange shipBonusArmorRepATGF
Used by: Used by:
Ship: Shapash Ship: Shapash
@@ -40391,15 +40433,15 @@ class Effect100103(BaseEffect):
type = 'passive' type = 'passive'
@staticmethod @staticmethod
def handler(fit, ship, context, projectionRange, **kwargs): def handler(fit, src, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost( fit.modules.filteredItemBoost(
lambda mod: mod.item.group.name == 'Warp Scrambler', lambda mod: mod.item.requiresSkill('Repair Systems'), 'armorDamageAmount',
'maxRange', 10, skill='Gallente Frigate', **kwargs) src.getModifiedItemAttr('eliteBonusGunship1'), skill='Assault Frigates', **kwargs)
class Effect100104(BaseEffect): class Effect11998(BaseEffect):
""" """
pyfaCustomShapashGfPropOverheat shipBonusSmallHybridMaxRangeATF3
Used by: Used by:
Ship: Shapash Ship: Shapash
@@ -40408,15 +40450,15 @@ class Effect100104(BaseEffect):
type = 'passive' type = 'passive'
@staticmethod @staticmethod
def handler(fit, ship, context, projectionRange, **kwargs): def handler(fit, src, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost( fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Afterburner') or mod.item.requiresSkill('High Speed Maneuvering'), lambda mod: mod.item.requiresSkill('Small Hybrid Turret'), 'maxRange',
'overloadSpeedFactorBonus', 10, skill='Gallente Frigate', **kwargs) src.getModifiedItemAttr('eliteBonusGunship2'), skill='Assault Frigates', **kwargs)
class Effect100105(BaseEffect): class Effect11999(BaseEffect):
""" """
pyfaCustomShapashRolePlateMass shipBonusSmallHybridTrackingSpeedATF3
Used by: Used by:
Ship: Shapash Ship: Shapash
@@ -40425,31 +40467,35 @@ class Effect100105(BaseEffect):
type = 'passive' type = 'passive'
@staticmethod @staticmethod
def handler(fit, ship, context, projectionRange, **kwargs): def handler(fit, src, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Armor Plate', 'massAddition', -100, **kwargs) fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Small Hybrid Turret'), 'trackingSpeed',
src.getModifiedItemAttr('eliteBonusGunship2'), skill='Assault Frigates', **kwargs)
class Effect100106(BaseEffect): class Effect12003(BaseEffect):
""" """
pyfaCustomShapashRoleHeat vortonTurretSpeeBonusPostPercentSpeedLocationShipModulesRequiringVortonProjectorOperation
Used by: Used by:
Ship: Shapash Implants named like: Halcyon R Booster (5 of 5)
""" """
type = 'passive' type = 'passive'
@staticmethod @staticmethod
def handler(fit, ship, context, projectionRange, **kwargs): def handler(fit, booster, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: True, 'heatDamage', -50, **kwargs) fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Vorton Projector Operation'), 'speed',
booster.getModifiedItemAttr('turretSpeeBonus'), **kwargs)
class Effect100200(BaseEffect): class Effect12038(BaseEffect):
""" """
pyfaCustomCybeleHacMhtFalloff shipBonusSPTFalloffMF3
Used by: Used by:
Ship: Cybele Ship: Republic Fleet Firetail
""" """
type = 'passive' type = 'passive'
@@ -40457,103 +40503,5 @@ class Effect100200(BaseEffect):
@staticmethod @staticmethod
def handler(fit, ship, context, projectionRange, **kwargs): def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost( fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Medium Hybrid Turret'), lambda mod: mod.item.requiresSkill('Small Projectile Turret'), 'falloff',
'falloff', 10, skill='Heavy Assault Cruisers', **kwargs) ship.getModifiedItemAttr('shipBonus3MF'), skill='Minmatar Frigate', **kwargs)
class Effect100201(BaseEffect):
"""
pyfaCustomCybeleHacMhtTracking
Used by:
Ship: Cybele
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Medium Hybrid Turret'),
'trackingSpeed', 7.5, skill='Heavy Assault Cruisers', **kwargs)
class Effect100202(BaseEffect):
"""
pyfaCustomCybeleGcMhtDamage
Used by:
Ship: Cybele
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Medium Hybrid Turret'),
'damageMultiplier', 20, skill='Gallente Cruiser', **kwargs)
class Effect100203(BaseEffect):
"""
pyfaCustomCybeleGcArAmount
Used by:
Ship: Cybele
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.requiresSkill('Repair Systems'),
'armorDamageAmount', 10, skill='Gallente Cruiser', **kwargs)
class Effect100204(BaseEffect):
"""
pyfaCustomCybeleGcPointRange
Used by:
Ship: Cybele
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(
lambda mod: mod.item.group.name == 'Warp Scrambler',
'maxRange', 25, skill='Gallente Cruiser', **kwargs)
class Effect100205(BaseEffect):
"""
pyfaCustomCybeleRoleVelocity
Used by:
Ship: Cybele
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.ship.boostItemAttr('maxVelocity', 30, **kwargs)
class Effect100206(BaseEffect):
"""
pyfaCustomCybeleRolePlateMass
Used by:
Ship: Cybele
"""
type = 'passive'
@staticmethod
def handler(fit, ship, context, projectionRange, **kwargs):
fit.modules.filteredItemBoost(lambda mod: mod.item.group.name == 'Armor Plate', 'massAddition', -100, **kwargs)

View File

@@ -159,6 +159,12 @@ class Fit:
self.gangBoosts = None self.gangBoosts = None
self.__ecmProjectedList = [] self.__ecmProjectedList = []
self.commandBonuses = {} 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): def clearFactorReloadDependentData(self):
# Here we clear all data known to rely on cycle parameters # 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: if stuff is not None and stuff != self:
stuff.clear() 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, # 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 # then this will run and clear the projected ships and flag the next
# iteration to skip this part to prevent recursion. # iteration to skip this part to prevent recursion.
@@ -621,7 +633,7 @@ class Fit:
"duration", value) "duration", value)
if warfareBuffID == 12: # Shield Burst: Shield Extension: Shield HP 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 if warfareBuffID == 13: # Armor Burst: Armor Energizing: Armor Resistance
for damageType in ("Em", "Thermal", "Explosive", "Kinetic"): for damageType in ("Em", "Thermal", "Explosive", "Kinetic"):
@@ -640,7 +652,7 @@ class Fit:
"duration", value) "duration", value)
if warfareBuffID == 15: # Armor Burst: Armor Reinforcement: Armor HP 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 if warfareBuffID == 16: # Information Burst: Sensor Optimization: Scan Resolution
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True) self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True)
@@ -734,7 +746,7 @@ class Fit:
self.ship.boostItemAttr(attr, value, stackingPenalties=True) self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 42: # Erebus Effect Generator : Armor HP bonus 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 if warfareBuffID == 43: # Erebus Effect Generator : Explosive resistance bonus
for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "explosiveDamageResonance"): for attr in ("armorExplosiveDamageResonance", "shieldExplosiveDamageResonance", "explosiveDamageResonance"):
@@ -756,7 +768,7 @@ class Fit:
self.ship.boostItemAttr(attr, value, stackingPenalties=True) self.ship.boostItemAttr(attr, value, stackingPenalties=True)
if warfareBuffID == 48: # Leviathan Effect Generator : Shield HP bonus 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 if warfareBuffID == 49: # Leviathan Effect Generator : EM resistance bonus
for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "emDamageResonance"): for attr in ("armorEmDamageResonance", "shieldEmDamageResonance", "emDamageResonance"):
@@ -870,6 +882,14 @@ class Fit:
if warfareBuffID == 100: # Weather_caustic_toxin_scan_resolution_bonus if warfareBuffID == 100: # Weather_caustic_toxin_scan_resolution_bonus
self.ship.boostItemAttr("scanResolution", value, stackingPenalties=True) 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] del self.commandBonuses[warfareBuffID]
def __resetDependentCalcs(self): def __resetDependentCalcs(self):
@@ -1478,11 +1498,11 @@ class Fit:
def tank(self): def tank(self):
reps = { reps = {
"passiveShield": self.calculateShieldRecharge(), "passiveShield": self.calculateShieldRecharge(),
"shieldRepair": self.extraAttributes["shieldRepair"], "shieldRepair": self.extraAttributes["shieldRepair"] + self._getAppliedShieldRr(),
"armorRepair": self.extraAttributes["armorRepair"], "armorRepair": self.extraAttributes["armorRepair"] + self._getAppliedArmorRr(),
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"], "armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"] + self._getAppliedArmorPreSpoolRr(),
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"], "armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"] + self._getAppliedArmorFullSpoolRr(),
"hullRepair": self.extraAttributes["hullRepair"] "hullRepair": self.extraAttributes["hullRepair"] + self._getAppliedHullRr()
} }
return reps return reps
@@ -1519,11 +1539,11 @@ class Fit:
if self.__sustainableTank is None: if self.__sustainableTank is None:
sustainable = { sustainable = {
"passiveShield": self.calculateShieldRecharge(), "passiveShield": self.calculateShieldRecharge(),
"shieldRepair": self.extraAttributes["shieldRepair"], "shieldRepair": self.extraAttributes["shieldRepair"] + self._getAppliedShieldRr(),
"armorRepair": self.extraAttributes["armorRepair"], "armorRepair": self.extraAttributes["armorRepair"] + self._getAppliedArmorRr(),
"armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"], "armorRepairPreSpool": self.extraAttributes["armorRepairPreSpool"] + self._getAppliedArmorPreSpoolRr(),
"armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"], "armorRepairFullSpool": self.extraAttributes["armorRepairFullSpool"] + self._getAppliedArmorFullSpoolRr(),
"hullRepair": self.extraAttributes["hullRepair"] "hullRepair": self.extraAttributes["hullRepair"] + self._getAppliedHullRr()
} }
if not self.capStable or self.factorReload: if not self.capStable or self.factorReload:
# Map a local repairer type to the attribute it uses # Map a local repairer type to the attribute it uses
@@ -1760,6 +1780,38 @@ class Fit:
mults.setdefault(stackingGroup, []).append((1 + strength / 100, None)) mults.setdefault(stackingGroup, []).append((1 + strength / 100, None))
return calculateMultiplier(mults) 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): def __deepcopy__(self, memo=None):
fitCopy = Fit() fitCopy = Fit()
# Character and owner are not copied # Character and owner are not copied

View File

@@ -25,10 +25,11 @@ import time
class SsoCharacter: class SsoCharacter:
def __init__(self, charID, name, client, accessToken=None, refreshToken=None): def __init__(self, charID, name, client, server, accessToken=None, refreshToken=None):
self.characterID = charID self.characterID = charID
self.characterName = name self.characterName = name
self.client = client self.client = client
self.server = server
self.accessToken = accessToken self.accessToken = accessToken
self.refreshToken = refreshToken self.refreshToken = refreshToken
self.accessTokenExpires = None self.accessTokenExpires = None
@@ -37,6 +38,9 @@ class SsoCharacter:
def init(self): def init(self):
pass pass
@property
def characterDisplay(self):
return "{} [{}]".format(self.characterName, self.server)
def is_token_expired(self): def is_token_expired(self):
if self.accessTokenExpires is None: if self.accessTokenExpires is None:
return True return True

View File

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

View File

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

View File

@@ -212,7 +212,7 @@ class AttributeGauge(wx.Window):
for x in range(1, 20): for x in range(1, 20):
dc.SetBrush(wx.Brush(wx.LIGHT_GREY)) dc.SetBrush(wx.Brush(wx.LIGHT_GREY))
dc.SetPen(wx.Pen(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.SetBrush(wx.Brush(colour))
dc.SetPen(wx.Pen(colour)) dc.SetPen(wx.Pen(colour))
@@ -222,19 +222,19 @@ class AttributeGauge(wx.Window):
if value >= 0: if value >= 0:
padding = (half if is_even else math.ceil(half - 1)) + 1 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: else:
padding = half - w + 1 if is_even else math.ceil(half) - (w - 1) 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): if self.leading_edge and (self.edge_on_neutral or value != 0):
dc.SetPen(wx.Pen(wx.WHITE)) dc.SetPen(wx.Pen(wx.WHITE))
dc.SetBrush(wx.Brush(wx.WHITE)) dc.SetBrush(wx.Brush(wx.WHITE))
if value > 0: 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: 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): def OnTimer(self, event):
old_value = self._old_percentage old_value = self._old_percentage

View File

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

View File

@@ -65,7 +65,6 @@ class DroneStackSplit(wx.Dialog):
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
self.input.SetValue(str(value)) self.input.SetValue(str(value))
self.input.SelectAll()
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15) bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
@@ -75,12 +74,13 @@ class DroneStackSplit(wx.Dialog):
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND) bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10) bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
self.input.SetFocus()
self.input.Bind(wx.EVT_CHAR, self.onChar) self.input.Bind(wx.EVT_CHAR, self.onChar)
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter) self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
self.SetSizer(bSizer1) self.SetSizer(bSizer1)
self.CenterOnParent()
self.Fit() self.Fit()
self.CenterOnParent()
self.input.SetFocus()
self.input.SelectAll()
def processEnter(self, evt): def processEnter(self, evt):
self.EndModal(wx.ID_OK) self.EndModal(wx.ID_OK)

View File

@@ -124,6 +124,8 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
data.groups[_t('Sansha Incursion')] = self.getEffectBeacons( data.groups[_t('Sansha Incursion')] = self.getEffectBeacons(
_t('ContextMenu|ProjectedEffectManipulation|Sansha Incursion')) _t('ContextMenu|ProjectedEffectManipulation|Sansha Incursion'))
data.groups[_t('Triglavian Invasion')] = self.getInvasionBeacons() data.groups[_t('Triglavian Invasion')] = self.getInvasionBeacons()
# data.groups[_t('Pirate Insurgency')] = self.getEffectBeacons(
# _t('ContextMenu|ProjectedEffectManipulation|Insurgency'))
return data return data
def getEffectBeacons(self, *groups, extra_garbage=()): def getEffectBeacons(self, *groups, extra_garbage=()):
@@ -238,5 +240,12 @@ class AddEnvironmentEffect(ContextMenuUnconditional):
data.items.append(Entry(item.ID, item.name, item.name)) data.items.append(Entry(item.ID, item.name, item.name))
return data 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() AddEnvironmentEffect.register()

View File

@@ -59,7 +59,6 @@ class NameDialog(wx.Dialog):
else: else:
value = str(value) value = str(value)
self.input.SetValue(value) self.input.SetValue(value)
self.input.SelectAll()
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15) bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
@@ -69,11 +68,12 @@ class NameDialog(wx.Dialog):
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND) bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10) bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
self.input.SetFocus()
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter) self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
self.SetSizer(bSizer1) self.SetSizer(bSizer1)
self.CenterOnParent()
self.Fit() self.Fit()
self.CenterOnParent()
self.input.SetFocus()
self.input.SelectAll()
def processEnter(self, evt): def processEnter(self, evt):
self.EndModal(wx.ID_OK) self.EndModal(wx.ID_OK)

View File

@@ -108,7 +108,6 @@ class AmountChanger(wx.Dialog):
self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER) self.input = wx.TextCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_ENTER)
self.input.SetValue(str(value)) self.input.SetValue(str(value))
self.input.SelectAll()
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15) bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
@@ -118,12 +117,13 @@ class AmountChanger(wx.Dialog):
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND) bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10) bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
self.input.SetFocus()
self.input.Bind(wx.EVT_CHAR, self.onChar) self.input.Bind(wx.EVT_CHAR, self.onChar)
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter) self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
self.SetSizer(bSizer1) self.SetSizer(bSizer1)
self.CenterOnParent()
self.Fit() self.Fit()
self.CenterOnParent()
self.input.SetFocus()
self.input.SelectAll()
def processEnter(self, evt): def processEnter(self, evt):
self.EndModal(wx.ID_OK) self.EndModal(wx.ID_OK)

View File

@@ -94,7 +94,6 @@ class RangeChanger(wx.Dialog):
value = int(value) value = int(value)
value = str(value) value = str(value)
self.input.SetValue(value) self.input.SetValue(value)
self.input.SelectAll()
bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15) bSizer1.Add(self.input, 0, wx.LEFT | wx.RIGHT | wx.EXPAND, 15)
@@ -104,12 +103,13 @@ class RangeChanger(wx.Dialog):
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND) bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND)
bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10) bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10)
self.input.SetFocus()
self.input.Bind(wx.EVT_CHAR, self.onChar) self.input.Bind(wx.EVT_CHAR, self.onChar)
self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter) self.input.Bind(wx.EVT_TEXT_ENTER, self.processEnter)
self.SetSizer(bSizer1) self.SetSizer(bSizer1)
self.CenterOnParent()
self.Fit() self.Fit()
self.CenterOnParent()
self.input.SetFocus()
self.input.SelectAll()
def processEnter(self, evt): def processEnter(self, evt):
self.EndModal(wx.ID_OK) self.EndModal(wx.ID_OK)

View File

@@ -4,13 +4,18 @@ import wx
# noinspection PyPackageRequirements # noinspection PyPackageRequirements
import wx.lib.mixins.listctrl as listmix import wx.lib.mixins.listctrl as listmix
from gui.utils.dark import isDark
class AutoListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter): class AutoListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter):
def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):
wx.ListCtrl.__init__(self, parent, ID, pos, size, style) wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
listmix.ListCtrlAutoWidthMixin.__init__(self) listmix.ListCtrlAutoWidthMixin.__init__(self)
listmix.ListRowHighlighter.__init__(self) listmix.ListRowHighlighter.__init__(self)
if 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): class AutoListCtrlNoHighlight(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.ListRowHighlighter):
def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): def __init__(self, parent, ID, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0):

View File

@@ -2,12 +2,13 @@ import wx
from logbook import Logger from logbook import Logger
import gui.builtinMarketBrowser.pfSearchBox as SBox import gui.builtinMarketBrowser.pfSearchBox as SBox
from config import slotColourMap from config import slotColourMap, slotColourMapDark
from eos.saveddata.module import Module from eos.saveddata.module import Module
from gui.builtinMarketBrowser.events import ItemSelected, RECENTLY_USED_MODULES from gui.builtinMarketBrowser.events import ItemSelected, RECENTLY_USED_MODULES
from gui.contextMenu import ContextMenu from gui.contextMenu import ContextMenu
from gui.display import Display from gui.display import Display
from gui.utils.staticHelpers import DragDropHelper from gui.utils.staticHelpers import DragDropHelper
from gui.utils.dark import isDark
from service.fit import Fit from service.fit import Fit
from service.market import Market from service.market import Market
@@ -243,6 +244,7 @@ class ItemView(Display):
def columnBackground(self, colItem, item): def columnBackground(self, colItem, item):
if self.sFit.serviceFittingOptions["colorFitBySlot"]: if self.sFit.serviceFittingOptions["colorFitBySlot"]:
return slotColourMap.get(Module.calculateSlot(item)) or self.GetBackgroundColour() colorMap = slotColourMapDark if isDark() else slotColourMap
return colorMap.get(Module.calculateSlot(item)) or self.GetBackgroundColour()
else: else:
return self.GetBackgroundColour() return self.GetBackgroundColour()

View File

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

View File

@@ -1,9 +1,11 @@
# noinspection PyPackageRequirements # noinspection PyPackageRequirements
import wx import wx
import config
import gui.mainFrame import gui.mainFrame
from gui.bitmap_loader import BitmapLoader from gui.bitmap_loader import BitmapLoader
from gui.preferenceView import PreferenceView from gui.preferenceView import PreferenceView
from service.esi import Esi
from service.settings import EsiSettings from service.settings import EsiSettings
# noinspection PyPackageRequirements # noinspection PyPackageRequirements
@@ -41,38 +43,68 @@ class PFEsiPref(PreferenceView):
"due to 'Signature has expired' error"))) "due to 'Signature has expired' error")))
mainSizer.Add(self.enforceJwtExpiration, 0, wx.ALL | wx.EXPAND, 5) mainSizer.Add(self.enforceJwtExpiration, 0, wx.ALL | wx.EXPAND, 5)
self.ssoServer = wx.CheckBox(panel, wx.ID_ANY, _t("Auto-login (starts local server)"), wx.DefaultPosition,
wx.DefaultSize,
0)
self.ssoServer.SetToolTip(wx.ToolTip(_t("This allows the EVE SSO to callback to your local pyfa instance and complete the authentication process without manual intervention.")))
mainSizer.Add(self.ssoServer, 0, wx.ALL | wx.EXPAND, 5)
rbSizer = wx.BoxSizer(wx.HORIZONTAL) rbSizer = wx.BoxSizer(wx.HORIZONTAL)
self.rbMode = wx.RadioBox(panel, -1, _t("Login Authentication Method"), wx.DefaultPosition, wx.DefaultSize,
[_t('Local Server'), _t('Manual')], 1, wx.RA_SPECIFY_COLS)
self.rbMode.SetItemToolTip(0, _t("This option starts a local webserver that EVE SSO Server will call back to"
" with information about the character login."))
self.rbMode.SetItemToolTip(1, _t("This option prompts users to copy and paste information to allow for"
" character login. Use this if having issues with the local server."))
self.rbMode.SetSelection(self.settings.get('loginMode')) self.enforceJwtExpiration.SetValue(self.settings.get("enforceJwtExpiration") or True)
self.enforceJwtExpiration.SetValue(self.settings.get("enforceJwtExpiration" or True)) self.ssoServer.SetValue(True if self.settings.get("loginMode") == 0 else False)
rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5) mainSizer.Add(rbSizer, 0, wx.ALL | wx.EXPAND, 0)
self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) esiSizer = wx.BoxSizer(wx.HORIZONTAL)
self.esiServer = wx.StaticText(panel, wx.ID_ANY, _t("Default SSO Server:"), wx.DefaultPosition, wx.DefaultSize, 0)
self.esiServer.Wrap(-1)
esiSizer.Add(self.esiServer, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.esiServer.SetToolTip(wx.ToolTip(_t('The source you choose will be used on connection.')))
self.chESIserver = wx.Choice(panel, choices=list(self.settings.keys()))
self.chESIserver.SetStringSelection(self.settings.get("server"))
esiSizer.Add(self.chESIserver, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10)
mainSizer.Add(esiSizer, 0, wx.TOP | wx.RIGHT, 10)
self.chESIserver.Bind(wx.EVT_CHOICE, self.OnServerChange)
self.enforceJwtExpiration.Bind(wx.EVT_CHECKBOX, self.OnEnforceChange) self.enforceJwtExpiration.Bind(wx.EVT_CHECKBOX, self.OnEnforceChange)
mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) self.ssoServer.Bind(wx.EVT_CHECKBOX, self.OnModeChange)
panel.SetSizer(mainSizer) panel.SetSizer(mainSizer)
panel.Layout() panel.Layout()
def OnTimeoutChange(self, event): def OnTimeoutChange(self, event):
self.settings.set('timeout', event.GetEventObject().GetValue()) self.settings.set('timeout', event.GetEventObject().GetValue())
event.Skip()
def OnModeChange(self, event): def OnModeChange(self, event):
self.settings.set('loginMode', event.GetInt()) self.settings.set('loginMode', 0 if self.ssoServer.GetValue() else 1)
event.Skip()
def OnEnforceChange(self, event): def OnEnforceChange(self, event):
self.settings.set('enforceJwtExpiration', self.enforceJwtExpiration.GetValue()) self.settings.set('enforceJwtExpiration', self.enforceJwtExpiration.GetValue())
event.Skip() event.Skip()
def OnServerChange(self, event):
# pass
source = self.chESIserver.GetString(self.chESIserver.GetSelection())
esiService = Esi.getInstance()
# init servers
esiService.init(config.supported_servers[source])
self.settings.set("server", source)
event.Skip()
def getImage(self): def getImage(self):
return BitmapLoader.getBitmap("eve", "gui") return BitmapLoader.getBitmap("eve", "gui")
PFEsiPref.register() PFEsiPref.register()

View File

@@ -40,7 +40,7 @@ class PFGeneralPref(PreferenceView):
langSizer = wx.BoxSizer(wx.HORIZONTAL) 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) pyfaLangsEnabled = bool(self.langChoices)
if pyfaLangsEnabled: if pyfaLangsEnabled:
@@ -64,7 +64,7 @@ class PFGeneralPref(PreferenceView):
langBox.Add(hl.HyperLinkCtrl(panel, -1, langBox.Add(hl.HyperLinkCtrl(panel, -1,
_t("Interested in helping with translations?"), _t("Interested in helping with translations?"),
URL="https://github.com/pyfa-org/Pyfa/blob/master/locale/README.md" 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: 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 = 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) self.stLangLabel.Wrap(-1)
@@ -93,7 +93,7 @@ class PFGeneralPref(PreferenceView):
langBox.Add(wx.StaticText(panel, wx.ID_ANY, langBox.Add(wx.StaticText(panel, wx.ID_ANY,
_t("Auto will use the same language pyfa uses if available, otherwise English"), _t("Auto will use the same language pyfa uses if available, otherwise English"),
wx.DefaultPosition, 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, self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, _t("Use global character"), wx.DefaultPosition, wx.DefaultSize,
0) 0)

View File

@@ -104,14 +104,14 @@ class CategoryItem(SFBrowserItem):
textColor = colorUtils.GetSuitable(windowColor, 1) textColor = colorUtils.GetSuitable(windowColor, 1)
mdc.SetTextForeground(textColor) mdc.SetTextForeground(textColor)
mdc.DrawBitmap(self.dropShadowBitmap, self.shipBmpx + 1, self.shipBmpy + 1) mdc.DrawBitmap(self.dropShadowBitmap, round(self.shipBmpx + 1), round(self.shipBmpy + 1))
mdc.DrawBitmap(self.shipBmp, self.shipBmpx, self.shipBmpy, 0) mdc.DrawBitmap(self.shipBmp, round(self.shipBmpx), round(self.shipBmpy), 0)
mdc.SetFont(self.fontBig) mdc.SetFont(self.fontBig)
categoryName, fittings = self.fittingInfo 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 self.dragging:
if not self.dragged: if not self.dragged:
if self.dragMotionTrigger < 0: 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(): if not self.HasCapture():
self.CaptureMouse() self.CaptureMouse()
self.dragWindow = PFBitmapFrame(self, pos, self.dragTLFBmp) self.dragWindow = PFBitmapFrame(self, pos, self.dragTLFBmp)
self.dragWindow.Show() self.dragWindow.Show()
self.dragged = True self.dragged = True
self.dragMotionTrigger = self.dragMotionTrail self.dragMotionTrigger = self.dragMotionTrail
@@ -493,9 +502,9 @@ class FitItem(SFItem.SFBrowserItem):
else: else:
shipEffBk = self.shipEffBk 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) mdc.SetFont(self.fontNormal)
@@ -504,26 +513,21 @@ class FitItem(SFItem.SFBrowserItem):
pfdate = drawUtils.GetPartialText(mdc, fitLocalDate, pfdate = drawUtils.GetPartialText(mdc, fitLocalDate,
self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) 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.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) mdc.SetFont(self.fontBig)
psname = drawUtils.GetPartialText(mdc, self.fitName, psname = drawUtils.GetPartialText(mdc, self.fitName,
self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) 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(): if self.tcFitName.IsShown():
self.AdjustControlSizePos(self.tcFitName, self.textStartx, self.toolbarx - self.editWidth - self.padding) 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): def AdjustControlSizePos(self, editCtl, start, end):
fnEditSize = editCtl.GetSize() fnEditSize = editCtl.GetSize()

View File

@@ -231,7 +231,7 @@ class NavigationPanel(SFItem.SFBrowserItem):
self.toolbar.SetPosition((self.toolbarx, self.toolbary)) self.toolbar.SetPosition((self.toolbarx, self.toolbary))
mdc.SetFont(self.fontSmall) 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.SetPen(wx.Pen(sepColor, 1))
mdc.DrawLine(0, rect.height - 1, rect.width, rect.height - 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 # todo: evaluate wx.DragImage, might make this class obsolete, however might also lose our customizations
# (like the sexy fade-in animation) # (like the sexy fade-in animation)
rect = self.GetRect() 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) # todo: convert to context manager after updating to wxPython >v4.0.1 (4.0.1 has a bug, see #1421)
# See #1418 for discussion # See #1418 for discussion
mdc = wx.BufferedPaintDC(self) mdc = wx.BufferedPaintDC(self)
@@ -63,4 +63,4 @@ class PFBitmapFrame(wx.Frame):
mdc.DrawBitmap(self.bitmap, 0, 0) mdc.DrawBitmap(self.bitmap, 0, 0)
mdc.SetPen(wx.Pen("#000000", width=1)) mdc.SetPen(wx.Pen("#000000", width=1))
mdc.SetBrush(wx.TRANSPARENT_BRUSH) 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.GetScrollPos(wx.VERTICAL)
posy -= self.itemsHeight posy -= self.itemsHeight
self.Scroll(0, posy) self.Scroll(0, round(posy))
event.Skip() event.Skip()
@@ -65,7 +65,7 @@ class PFListPane(wx.ScrolledWindow):
posy = self.GetScrollPos(wx.VERTICAL) posy = self.GetScrollPos(wx.VERTICAL)
posy += self.itemsHeight posy += self.itemsHeight
self.Scroll(0, posy) self.Scroll(0, round(posy))
event.Skip() event.Skip()
@@ -109,7 +109,7 @@ class PFListPane(wx.ScrolledWindow):
# if we need to adjust # if we need to adjust
if new_vs_x != -1 or new_vs_y != -1: 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): def AddWidget(self, widget):
widget.Reparent(self) widget.Reparent(self)

View File

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

View File

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

View File

@@ -247,12 +247,12 @@ class ShipItem(SFItem.SFBrowserItem):
else: else:
shipEffBk = self.shipEffBk 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.raceDropShadowBmp, round(self.raceBmpx + 1), round(self.raceBmpy + 1))
mdc.DrawBitmap(self.raceBmp, self.raceBmpx, self.raceBmpy) mdc.DrawBitmap(self.raceBmp, round(self.raceBmpx), round(self.raceBmpy))
shipName, shipTrait, fittings = self.shipFittingInfo shipName, shipTrait, fittings = self.shipFittingInfo
@@ -264,17 +264,17 @@ class ShipItem(SFItem.SFBrowserItem):
fformat = "%d fits" fformat = "%d fits"
mdc.SetFont(self.fontNormal) 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.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) mdc.SetFont(self.fontBig)
psname = drawUtils.GetPartialText(mdc, shipName, psname = drawUtils.GetPartialText(mdc, shipName,
self.toolbarx - self.textStartx - self.padding * 2 - self.thoverw) 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(): if self.tcFitName.IsShown():
self.AdjustControlSizePos(self.tcFitName, self.textStartx, self.toolbarx - self.editWidth - self.padding) 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 = PyGauge(contentPanel, font, 100)
lbl.SetMinSize((48, 16)) lbl.SetMinSize((48, 16))
lbl.SetBackgroundColour(wx.Colour(bc[0], bc[1], bc[2])) lbl.SetBackgroundColour(wx.Colour(round(bc[0]), round(bc[1]), round(bc[2])))
lbl.SetBarColour(wx.Colour(fc[0], fc[1], fc[2])) lbl.SetBarColour(wx.Colour(round(fc[0]), round(fc[1]), round(fc[2])))
lbl.SetBarGradient() lbl.SetBarGradient()
lbl.SetFractionDigits(1) lbl.SetFractionDigits(1)

View File

@@ -167,7 +167,7 @@ class Miscellanea(ViewColumn):
text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3)) text = "{0}/s".format(formatAmount(capPerSec, 3, 0, 3))
tooltip = "Energy neutralization per second" tooltip = "Energy neutralization per second"
return text, tooltip return text, tooltip
elif itemGroup == "Salvager": elif itemGroup in ("Salvager", "Salvage Drone"):
chance = stuff.getModifiedItemAttr("accessDifficultyBonus") chance = stuff.getModifiedItemAttr("accessDifficultyBonus")
if not chance: if not chance:
return "", None return "", None

View File

@@ -39,9 +39,10 @@ from gui.builtinViewColumns.state import State
from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED
from gui.contextMenu import ContextMenu from gui.contextMenu import ContextMenu
from gui.utils.staticHelpers import DragDropHelper from gui.utils.staticHelpers import DragDropHelper
from gui.utils.dark import isDark
from service.fit import Fit from service.fit import Fit
from service.market import Market from service.market import Market
from config import slotColourMap from config import slotColourMap, slotColourMapDark, errColor, errColorDark
from gui.fitCommands.helpers import getSimilarModPositions from gui.fitCommands.helpers import getSimilarModPositions
pyfalog = Logger(__name__) pyfalog = Logger(__name__)
@@ -729,7 +730,10 @@ class FittingView(d.Display):
event.Skip() event.Skip()
def slotColour(self, slot): def slotColour(self, slot):
return slotColourMap.get(slot) or self.GetBackgroundColour() if isDark():
return slotColourMapDark.get(slot) or self.GetBackgroundColour()
else:
return slotColourMap.get(slot) or self.GetBackgroundColour()
def refresh(self, stuff): def refresh(self, stuff):
""" """
@@ -774,7 +778,7 @@ class FittingView(d.Display):
if slotMap[mod.slot] or hasRestrictionOverriden: # Color too many modules as red if slotMap[mod.slot] or hasRestrictionOverriden: # Color too many modules as red
self.SetItemBackgroundColour(i, wx.Colour(204, 51, 51)) self.SetItemBackgroundColour(i, errColorDark if isDark() else errColor)
elif sFit.serviceFittingOptions["colorFitBySlot"]: # Color by slot it enabled elif sFit.serviceFittingOptions["colorFitBySlot"]: # Color by slot it enabled
self.SetItemBackgroundColour(i, self.slotColour(mod.slot)) self.SetItemBackgroundColour(i, self.slotColour(mod.slot))
@@ -895,7 +899,7 @@ class FittingView(d.Display):
opts.m_labelText = name opts.m_labelText = name
if imgId != -1: 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) width = render.DrawHeaderButton(self, tdc, (0, 0, 16, 16), sortArrow=wx.HDR_SORT_ICON_NONE, params=opts)
@@ -911,7 +915,7 @@ class FittingView(d.Display):
maxWidth += columnsWidths[i] maxWidth += columnsWidths[i]
mdc = wx.MemoryDC() 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) mdc.SelectObject(mbmp)
@@ -956,7 +960,7 @@ class FittingView(d.Display):
cx = padding cx = padding
if slotMap[st.slot]: 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): for i, col in enumerate(self.activeColumns):
if i > maxColumns: if i > maxColumns:

View File

@@ -371,7 +371,7 @@ class SkillTreeView(wx.Panel):
bSizerButtons.AddStretchSpacer() bSizerButtons.AddStretchSpacer()
importExport = ((_t("Import skills from clipboard"), wx.ART_FILE_OPEN, "import"), 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: for tooltip, art, attr in importExport:
bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON) bitmap = wx.ArtProvider.GetBitmap(art, wx.ART_BUTTON)
@@ -446,6 +446,7 @@ class SkillTreeView(wx.Panel):
text = fromClipboard().strip() text = fromClipboard().strip()
if text: if text:
sCharacter = Character.getInstance()
char = self.charEditor.entityEditor.getActiveEntity() char = self.charEditor.entityEditor.getActiveEntity()
try: try:
lines = text.splitlines() 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, level = s.rsplit(None, 1)[0], arabicOrRomanToInt(s.rsplit(None, 1)[1])
skill = char.getSkill(skill) skill = char.getSkill(skill)
if skill: if skill:
skill.setLevel(level, ignoreRestrict=True) sCharacter.changeLevel(char.ID, skill.item.ID, level)
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
raise raise
@@ -516,7 +517,10 @@ class SkillTreeView(wx.Panel):
def populateSkillTreeSkillSearch(self, event=None): def populateSkillTreeSkillSearch(self, event=None):
sChar = Character.getInstance() sChar = Character.getInstance()
char = self.charEditor.entityEditor.getActiveEntity() 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 root = self.root
tree = self.skillTreeListCtrl tree = self.skillTreeListCtrl
@@ -530,7 +534,7 @@ class SkillTreeView(wx.Panel):
iconId = self.skillBookDirtyImageId iconId = self.skillBookDirtyImageId
childId = tree.AppendItem(root, name, iconId, data=('skill', id)) 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): def populateSkillTree(self, event=None):
sChar = Character.getInstance() sChar = Character.getInstance()
@@ -588,7 +592,6 @@ class SkillTreeView(wx.Panel):
iconId = self.skillBookDirtyImageId iconId = self.skillBookDirtyImageId
childId = tree.AppendItem(root, name, iconId, data=('skill', id)) childId = tree.AppendItem(root, name, iconId, data=('skill', id))
tree.SetItemText(childId, 1, _t("Level {}").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 spawnMenu(self, event): def spawnMenu(self, event):
@@ -804,7 +807,12 @@ class APIView(wx.Panel):
self.SetSizer(pmainSizer) self.SetSizer(pmainSizer)
self.Layout() self.Layout()
self.ssoListChanged(None) try:
self.ssoListChanged(None)
except (KeyboardInterrupt, SystemExit):
raise
except:
pass
def ssoCharChanged(self, event): def ssoCharChanged(self, event):
sChar = Character.getInstance() sChar = Character.getInstance()
@@ -856,7 +864,7 @@ class APIView(wx.Panel):
noneID = self.charChoice.Append(_t("None"), None) noneID = self.charChoice.Append(_t("None"), None)
for char in ssoChars: for char in ssoChars:
currId = self.charChoice.Append(char.characterName, char.ID) currId = self.charChoice.Append(char.characterDisplay, char.ID)
if sso is not None and char.ID == sso.ID: if sso is not None and char.ID == sso.ID:
self.charChoice.SetSelection(currId) self.charChoice.SetSelection(currId)

View File

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

View File

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

View File

@@ -96,7 +96,7 @@ class EveFittings(AuxiliaryFrame):
self.charChoice.Clear() self.charChoice.Clear()
for char in chars: for char in chars:
self.charChoice.Append(char.characterName, char.ID) self.charChoice.Append(char.characterDisplay, char.ID)
if len(chars) > 0: if len(chars) > 0:
self.charChoice.SetSelection(0) self.charChoice.SetSelection(0)
@@ -227,21 +227,6 @@ class EveFittings(AuxiliaryFrame):
self.fitView.update([]) self.fitView.update([])
class ESIServerExceptionHandler:
def __init__(self, parentWindow, ex):
pyfalog.error(ex)
with wx.MessageDialog(
parentWindow,
_t("There was an issue starting up the localized server, try setting "
"Login Authentication Method to Manual by going to Preferences -> EVE SS0 -> "
"Login Authentication Method. If this doesn't fix the problem please file an "
"issue on Github."),
_t("Add Character Error"),
wx.OK | wx.ICON_ERROR
) as dlg:
dlg.ShowModal()
class ESIExceptionHandler: class ESIExceptionHandler:
# todo: make this a generate excetpion handler for all calls # todo: make this a generate excetpion handler for all calls
def __init__(self, ex): def __init__(self, ex):
@@ -283,7 +268,7 @@ class ExportToEve(AuxiliaryFrame):
def __init__(self, parent): def __init__(self, parent):
super().__init__( super().__init__(
parent, id=wx.ID_ANY, title=_t("Export fit to EVE"), pos=wx.DefaultPosition, 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 self.mainFrame = parent
@@ -305,6 +290,16 @@ class ExportToEve(AuxiliaryFrame):
self.exportChargesCb.Bind(wx.EVT_CHECKBOX, self.OnChargeExportChange) self.exportChargesCb.Bind(wx.EVT_CHECKBOX, self.OnChargeExportChange)
mainSizer.Add(self.exportChargesCb, 0, 0, 5) 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.exportBtn.Bind(wx.EVT_BUTTON, self.exportFitting)
self.statusbar = wx.StatusBar(self) self.statusbar = wx.StatusBar(self)
@@ -324,13 +319,21 @@ class ExportToEve(AuxiliaryFrame):
EsiSettings.getInstance().set('exportCharges', self.exportChargesCb.GetValue()) EsiSettings.getInstance().set('exportCharges', self.exportChargesCb.GetValue())
event.Skip() 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): def updateCharList(self):
sEsi = Esi.getInstance() sEsi = Esi.getInstance()
chars = sEsi.getSsoCharacters() chars = sEsi.getSsoCharacters()
self.charChoice.Clear() self.charChoice.Clear()
for char in chars: for char in chars:
self.charChoice.Append(char.characterName, char.ID) self.charChoice.Append(char.characterDisplay, char.ID)
if len(chars) > 0: if len(chars) > 0:
self.charChoice.SetSelection(0) self.charChoice.SetSelection(0)
@@ -360,8 +363,10 @@ class ExportToEve(AuxiliaryFrame):
sFit = Fit.getInstance() sFit = Fit.getInstance()
exportCharges = self.exportChargesCb.GetValue() exportCharges = self.exportChargesCb.GetValue()
exportImplants = self.exportImplantsCb.GetValue()
exportBoosters = self.exportBoostersCb.GetValue()
try: try:
data = sPort.exportESI(sFit.getFit(fitID), exportCharges) data = sPort.exportESI(sFit.getFit(fitID), exportCharges, exportImplants, exportBoosters)
except ESIExportException as e: except ESIExportException as e:
msg = str(e) msg = str(e)
if not msg: if not msg:
@@ -414,6 +419,7 @@ class SsoCharacterMgmt(AuxiliaryFrame):
self.lcCharacters.InsertColumn(0, heading=_t('Character')) self.lcCharacters.InsertColumn(0, heading=_t('Character'))
self.lcCharacters.InsertColumn(1, heading=_t('Character ID')) self.lcCharacters.InsertColumn(1, heading=_t('Character ID'))
self.lcCharacters.InsertColumn(2, heading=_t('Server'))
self.popCharList() self.popCharList()
@@ -476,9 +482,11 @@ class SsoCharacterMgmt(AuxiliaryFrame):
self.lcCharacters.InsertItem(index, char.characterName) self.lcCharacters.InsertItem(index, char.characterName)
self.lcCharacters.SetItem(index, 1, str(char.characterID)) self.lcCharacters.SetItem(index, 1, str(char.characterID))
self.lcCharacters.SetItemData(index, char.ID) self.lcCharacters.SetItemData(index, char.ID)
self.lcCharacters.SetItem(index, 2, char.server or "<unknown>")
self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE) self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE)
self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE) self.lcCharacters.SetColumnWidth(1, wx.LIST_AUTOSIZE)
self.lcCharacters.SetColumnWidth(2, wx.LIST_AUTOSIZE)
def addChar(self, event): def addChar(self, event):
try: try:
@@ -486,8 +494,6 @@ class SsoCharacterMgmt(AuxiliaryFrame):
sEsi.login() sEsi.login()
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
raise raise
except Exception as ex:
ESIServerExceptionHandler(self, ex)
def delChar(self, event): def delChar(self, event):
item = self.lcCharacters.GetFirstSelected() item = self.lcCharacters.GetFirstSelected()

View File

@@ -61,10 +61,11 @@ from gui.statsPane import StatsPane
from gui.targetProfileEditor import TargetProfileEditor from gui.targetProfileEditor import TargetProfileEditor
from gui.updateDialog import UpdateDialog from gui.updateDialog import UpdateDialog
from gui.utils.clipboard import fromClipboard from gui.utils.clipboard import fromClipboard
from gui.utils.progressHelper import ProgressHelper
from service.character import Character from service.character import Character
from service.esi import Esi from service.esi import Esi
from service.fit import Fit from service.fit import Fit
from service.port import IPortUser, Port from service.port import Port
from service.price import Price from service.price import Price
from service.settings import HTMLExportSettings, SettingsProvider from service.settings import HTMLExportSettings, SettingsProvider
from service.update import Update from service.update import Update
@@ -130,7 +131,6 @@ class OpenFitsThread(threading.Thread):
self.running = False self.running = False
# todo: include IPortUser again
class MainFrame(wx.Frame): class MainFrame(wx.Frame):
__instance = None __instance = None
@@ -845,14 +845,15 @@ class MainFrame(wx.Frame):
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE
) as dlg: ) as dlg:
if dlg.ShowModal() == wx.ID_OK: if dlg.ShowModal() == wx.ID_OK:
self.progressDialog = wx.ProgressDialog( # set some arbitrary spacing to create width in window
_t("Importing fits"), progress = ProgressHelper(message=" " * 100, callback=self._openAfterImport)
" " * 100, # set some arbitrary spacing to create width in window call = (Port.importFitsThreaded, [dlg.GetPaths(), progress], {})
parent=self, self.handleProgress(
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL title=_t("Importing fits"),
) style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE,
Port.importFitsThreaded(dlg.GetPaths(), self) call=call,
self.progressDialog.ShowModal() progress=progress,
errMsgLbl=_t("Import Error"))
def backupToXml(self, event): def backupToXml(self, event):
""" Back up all fits to EVE XML file """ """ Back up all fits to EVE XML file """
@@ -863,32 +864,30 @@ class MainFrame(wx.Frame):
_t("Save Backup As..."), _t("Save Backup As..."),
wildcard=_t("EVE XML fitting file") + " (*.xml)|*.xml", wildcard=_t("EVE XML fitting file") + " (*.xml)|*.xml",
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
defaultFile=defaultFile, defaultFile=defaultFile) as fileDlg:
) as dlg: if fileDlg.ShowModal() == wx.ID_OK:
if dlg.ShowModal() == wx.ID_OK: filePath = fileDlg.GetPath()
filePath = dlg.GetPath()
if '.' not in os.path.basename(filePath): if '.' not in os.path.basename(filePath):
filePath += ".xml" filePath += ".xml"
sFit = Fit.getInstance() fitAmount = Fit.getInstance().countAllFits()
max_ = sFit.countAllFits() progress = ProgressHelper(
message=_t("Backing up {} fits to: {}").format(fitAmount, filePath),
self.progressDialog = wx.ProgressDialog( maximum=fitAmount + 1)
_t("Backup fits"), call = (Port.backupFits, [filePath, progress], {})
_t("Backing up {} fits to: {}").format(max_, filePath), self.handleProgress(
maximum=max_, title=_t("Backup fits"),
parent=self, style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL | wx.PD_AUTO_HIDE,
style=wx.PD_CAN_ABORT | wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_APP_MODAL call=call,
) progress=progress,
Port.backupFits(filePath, self) errMsgLbl=_t("Export Error"))
self.progressDialog.ShowModal()
def exportHtml(self, event): def exportHtml(self, event):
from gui.utils.exportHtml import exportHtml from gui.utils.exportHtml import exportHtml
sFit = Fit.getInstance() sFit = Fit.getInstance()
settings = HTMLExportSettings.getInstance() settings = HTMLExportSettings.getInstance()
max_ = sFit.countAllFits()
path = settings.getPath() path = settings.getPath()
if not os.path.isdir(os.path.dirname(path)): if not os.path.isdir(os.path.dirname(path)):
@@ -903,82 +902,44 @@ class MainFrame(wx.Frame):
) as dlg: ) as dlg:
if dlg.ShowModal() == wx.ID_OK: if dlg.ShowModal() == wx.ID_OK:
return return
progress = ProgressHelper(
message=_t("Generating HTML file at: {}").format(path),
maximum=sFit.countAllFits() + 1)
call = (exportHtml.getInstance().refreshFittingHtml, [True, progress], {})
self.handleProgress(
title=_t("Backup fits"),
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME,
call=call,
progress=progress)
self.progressDialog = wx.ProgressDialog( def handleProgress(self, title, style, call, progress, errMsgLbl=None):
_t("Backup fits"), extraArgs = {}
_t("Generating HTML file at: {}").format(path), if progress.maximum is not None:
maximum=max_, parent=self, extraArgs['maximum'] = progress.maximum
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME) with wx.ProgressDialog(
parent=self,
exportHtml.getInstance().refreshFittingHtml(True, self.backupCallback) title=title,
self.progressDialog.ShowModal() message=progress.message,
style=style,
def backupCallback(self, info): **extraArgs
if info == -1: ) as dlg:
self.closeProgressDialog() func, args, kwargs = call
else: func(*args, **kwargs)
self.progressDialog.Update(info) while progress.working:
wx.MilliSleep(250)
def on_port_process_start(self): wx.Yield()
# flag for progress dialog. (progress.dlgWorking, skip) = dlg.Update(progress.current, progress.message)
self.__progress_flag = True if progress.error and errMsgLbl:
def on_port_processing(self, action, data=None):
# 2017/03/29 NOTE: implementation like interface
wx.CallAfter(
self._on_port_processing, action, data
)
return self.__progress_flag
def _on_port_processing(self, action, data):
"""
While importing fits from file, the logic calls back to this function to
update progress bar to show activity. XML files can contain multiple
ships with multiple fits, whereas EFT cfg files contain many fits of
a single ship. When iterating through the files, we update the message
when we start a new file, and then Pulse the progress bar with every fit
that is processed.
action : a flag that lets us know how to deal with :data
None: Pulse the progress bar
1: Replace message with data
other: Close dialog and handle based on :action (-1 open fits, -2 display error)
"""
_message = None
if action & IPortUser.ID_ERROR:
self.closeProgressDialog()
_message = _t("Import Error") if action & IPortUser.PROCESS_IMPORT else _t("Export Error")
with wx.MessageDialog( with wx.MessageDialog(
self, self,
_t("The following error was generated") + _t("The following error was generated") +
f"\n\n{data}\n\n" + f"\n\n{progress.error}\n\n" +
_t("Be aware that already processed fits were not saved"), _t("Be aware that already processed fits were not saved"),
_message, wx.OK | wx.ICON_ERROR errMsgLbl, wx.OK | wx.ICON_ERROR
) as dlg: ) as dlg:
dlg.ShowModal() dlg.ShowModal()
return elif progress.callback:
progress.callback(*progress.cbArgs)
# data is str
if action & IPortUser.PROCESS_IMPORT:
if action & IPortUser.ID_PULSE:
_message = ()
# update message
elif action & IPortUser.ID_UPDATE: # and data != self.progressDialog.message:
_message = data
if _message is not None:
self.__progress_flag, _unuse = self.progressDialog.Pulse(_message)
else:
self.closeProgressDialog()
if action & IPortUser.ID_DONE:
self._openAfterImport(data)
# data is tuple(int, str)
elif action & IPortUser.PROCESS_EXPORT:
if action & IPortUser.ID_DONE:
self.closeProgressDialog()
else:
self.__progress_flag, _unuse = self.progressDialog.Update(data[0], data[1])
def _openAfterImport(self, fits): def _openAfterImport(self, fits):
if len(fits) > 0: if len(fits) > 0:
@@ -988,6 +949,8 @@ class MainFrame(wx.Frame):
wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True)) wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=fit.shipID, back=True))
else: else:
fits.sort(key=lambda _fit: (_fit.ship.item.name, _fit.name)) fits.sort(key=lambda _fit: (_fit.ship.item.name, _fit.name))
# Show 100 fits max
fits = fits[:100]
results = [] results = []
for fit in fits: for fit in fits:
results.append(( results.append((
@@ -999,15 +962,6 @@ class MainFrame(wx.Frame):
)) ))
wx.PostEvent(self.shipBrowser, ImportSelected(fits=results, back=True)) wx.PostEvent(self.shipBrowser, ImportSelected(fits=results, back=True))
def closeProgressDialog(self):
# Windows apparently handles ProgressDialogs differently. We can
# simply Destroy it here, but for other platforms we must Close it
if 'wxMSW' in wx.PlatformInfo:
self.progressDialog.Destroy()
else:
self.progressDialog.EndModal(wx.ID_OK)
self.progressDialog.Close()
def importCharacter(self, event): def importCharacter(self, event):
""" Imports character XML file from EVE API """ """ Imports character XML file from EVE API """
with wx.FileDialog( with wx.FileDialog(

View File

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

View File

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

View File

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

View File

@@ -2,30 +2,48 @@ import wx
import gui.mainFrame import gui.mainFrame
import webbrowser import webbrowser
import gui.globalEvents as GE import gui.globalEvents as GE
import config
import time
from service.settings import EsiSettings
_t = wx.GetTranslation _t = wx.GetTranslation
class SsoLogin(wx.Dialog): class SsoLogin(wx.Dialog):
def __init__(self): def __init__(self, server: config.ApiServer, start_local_server=True):
mainFrame = gui.mainFrame.MainFrame.getInstance() self.mainFrame = gui.mainFrame.MainFrame.getInstance()
from service.esi import Esi
super().__init__( super().__init__(
mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE, self.mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), style=wx.DEFAULT_DIALOG_STYLE,
size=wx.Size(450, 240) if "wxGTK" in wx.PlatformInfo else wx.Size(400, 240)) size=wx.Size(450, 240) if "wxGTK" in wx.PlatformInfo else wx.Size(400, 240))
bSizer1 = wx.BoxSizer(wx.VERTICAL) bSizer1 = wx.BoxSizer(wx.VERTICAL)
text = wx.StaticText(self, wx.ID_ANY, _t("Copy and paste the block of text provided by pyfa.io")) if start_local_server:
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10) text = wx.StaticText(self, wx.ID_ANY, _t("Waiting for character login through EVE Single Sign-On."))
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
bSizer1.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.EXPAND, 15)
text = wx.StaticText(self, wx.ID_ANY, _t("If auto-login fails, copy and paste the token provided by pyfa.io"))
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
elif server.name == "Serenity":
text = wx.StaticText(self, wx.ID_ANY, _t("Please copy and paste the url when your authorization is completed"))
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
else:
text = wx.StaticText(self, wx.ID_ANY, _t("Please copy and paste the token provided by pyfa.io"))
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10)
self.ssoInfoCtrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, (-1, -1), style=wx.TE_MULTILINE) self.ssoInfoCtrl = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, (-1, -1), style=wx.TE_MULTILINE)
self.ssoInfoCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) self.ssoInfoCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL))
self.ssoInfoCtrl.Layout() self.ssoInfoCtrl.Layout()
self.ssoInfoCtrl.Bind(wx.EVT_TEXT, self.OnTextEnter)
bSizer1.Add(self.ssoInfoCtrl, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 10) bSizer1.Add(self.ssoInfoCtrl, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 10)
self.Esisettings = EsiSettings.getInstance()
bSizer3 = wx.BoxSizer(wx.VERTICAL) bSizer3 = wx.BoxSizer(wx.VERTICAL)
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10) bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10)
@@ -34,51 +52,43 @@ class SsoLogin(wx.Dialog):
self.SetSizer(bSizer1) self.SetSizer(bSizer1)
self.Center() self.Center()
from service.esi import Esi
self.sEsi = Esi.getInstance() self.sEsi = Esi.getInstance()
uri = self.sEsi.get_login_uri(None)
webbrowser.open(uri)
class SsoLoginServer(wx.Dialog):
def __init__(self, port):
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
super().__init__(self.mainFrame, id=wx.ID_ANY, title=_t("SSO Login"), size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE)
from service.esi import Esi
self.sEsi = Esi.getInstance()
serverAddr = self.sEsi.startServer(port)
serverAddr = self.sEsi.startServer(0) if start_local_server else None
uri = self.sEsi.get_login_uri(serverAddr) uri = self.sEsi.get_login_uri(serverAddr)
bSizer1 = wx.BoxSizer(wx.VERTICAL) if server.name == "Serenity":
self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin) webbrowser.open(config.SSO_LOGOFF_SERENITY)
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) time.sleep(1)
text = wx.StaticText(self, wx.ID_ANY, _t("Waiting for character login through EVE Single Sign-On.")) self.okBtn = self.FindWindow(wx.ID_OK)
bSizer1.Add(text, 0, wx.ALL | wx.EXPAND, 10) self.okBtn.Enable(False)
# Ensure we clean up once they hit the "OK" button
bSizer3 = wx.BoxSizer(wx.VERTICAL) self.okBtn.Bind(wx.EVT_BUTTON, self.OnDestroy)
bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10)
bSizer3.Add(self.CreateStdDialogButtonSizer(wx.CANCEL), 0, wx.EXPAND)
bSizer1.Add(bSizer3, 0, wx.BOTTOM | wx.RIGHT | wx.LEFT | wx.EXPAND, 10)
self.SetSizer(bSizer1)
self.Fit()
self.Center()
webbrowser.open(uri) webbrowser.open(uri)
self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.OnLogin)
# Ensure we clean up if ESC is pressed
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
def OnTextEnter(self, event):
t = event.String.strip()
if t == "":
self.okBtn.Enable(False)
else:
self.okBtn.Enable(True)
event.Skip()
def OnLogin(self, event): def OnLogin(self, event):
self.EndModal(wx.ID_OK) # This would normally happen if it was logged in via server auto-login. In this case, the modal is done, we effectively want to cancel out
self.EndModal(wx.ID_CANCEL)
event.Skip() event.Skip()
def OnDestroy(self, event): def OnDestroy(self, event):
# Clean up by unbinding some events and stopping the server
self.mainFrame.Unbind(GE.EVT_SSO_LOGIN, handler=self.OnLogin) self.mainFrame.Unbind(GE.EVT_SSO_LOGIN, handler=self.OnLogin)
if self:
self.Unbind(wx.EVT_WINDOW_DESTROY, handler=self.OnDestroy)
self.sEsi.stopServer() self.sEsi.stopServer()
event.Skip() event.Skip()

View File

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

View File

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

View File

@@ -12,7 +12,7 @@ def Brighten(color, factor):
b += (255 - b) * factor b += (255 - b) * factor
g += (255 - g) * 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): def Darken(color, factor):
@@ -30,7 +30,7 @@ def Darken(color, factor):
b = min(max(b, 0), 255) b = min(max(b, 0), 255)
g = min(max(g, 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): def _getBrightness(color):
@@ -70,4 +70,4 @@ def CalculateTransition(s_color, e_color, delta):
tG = sG + (eG - sG) * delta tG = sG + (eG - sG) * delta
tB = sB + (eB - sB) * 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))

12
gui/utils/dark.py Normal file
View File

@@ -0,0 +1,12 @@
import wx
def isDark():
if 'wxMSW' in wx.PlatformInfo:
return False
try:
return wx.SystemSettings.GetAppearance().IsDark()
except (KeyboardInterrupt, SystemExit):
raise
except:
return False

View File

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

View File

@@ -26,20 +26,20 @@ class exportHtml:
def __init__(self): def __init__(self):
self.thread = exportHtmlThread() self.thread = exportHtmlThread()
def refreshFittingHtml(self, force=False, callback=False): def refreshFittingHtml(self, force=False, progress=None):
settings = HTMLExportSettings.getInstance() settings = HTMLExportSettings.getInstance()
if force or settings.getEnabled(): if force or settings.getEnabled():
self.thread.stop() self.thread.stop()
self.thread = exportHtmlThread(callback) self.thread = exportHtmlThread(progress)
self.thread.start() self.thread.start()
class exportHtmlThread(threading.Thread): class exportHtmlThread(threading.Thread):
def __init__(self, callback=False): def __init__(self, progress=False):
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.name = "HTMLExport" self.name = "HTMLExport"
self.callback = callback self.progress = progress
self.stopRunning = False self.stopRunning = False
def stop(self): def stop(self):
@@ -72,11 +72,13 @@ class exportHtmlThread(threading.Thread):
pass pass
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
raise raise
except Exception as ex: except Exception as e:
pass if self.progress:
self.progress.error = f'{e}'
if self.callback: finally:
wx.CallAfter(self.callback, -1) if self.progress:
self.progress.current += 1
self.progress.workerWorking = False
def generateFullHTML(self, sMkt, sFit, dnaUrl): def generateFullHTML(self, sMkt, sFit, dnaUrl):
""" Generate the complete HTML with styling and javascript """ """ Generate the complete HTML with styling and javascript """
@@ -171,13 +173,13 @@ class exportHtmlThread(threading.Thread):
</head> </head>
<body> <body>
<div id="canvas" data-role="page"> <div id="canvas" data-role="page">
<div style="text-align: center;"><strong>Last updated:</strong> %s <small>(<span class="timer"></span>)</small></div>
<div data-role="header"> <div data-role="header">
<h1>Pyfa fits</h1> <h1>Pyfa fits by Group</h1>
</div> </div>
<div data-role="content"> <div data-role="content">
<div style="text-align: center;"><strong>Last updated:</strong> %s <small>(<span class="timer"></span>)</small></div>
""" % (time.time(), dnaUrl, localDate) """ % (time.time(), dnaUrl, localDate)
HTML += ' <ul data-role="listview" class="ui-listview-outer" data-inset="true" data-filter="true">\n' HTML += ' <ul data-role="listview" class="ui-listview-outer" data-inset="true" data-filter="true">\n'
categoryList = list(sMkt.getShipRoot()) categoryList = list(sMkt.getShipRoot())
categoryList.sort(key=lambda _ship: _ship.name) categoryList.sort(key=lambda _ship: _ship.name)
@@ -214,7 +216,9 @@ class exportHtmlThread(threading.Thread):
eftFit = Port.exportEft(getFit(fit[0]), options={ eftFit = Port.exportEft(getFit(fit[0]), options={
PortEftOptions.IMPLANTS: True, PortEftOptions.IMPLANTS: True,
PortEftOptions.MUTATIONS: True, PortEftOptions.MUTATIONS: True,
PortEftOptions.LOADED_CHARGES: True}) PortEftOptions.LOADED_CHARGES: True,
PortEftOptions.BOOSTERS: True,
PortEftOptions.CARGO: True})
HTMLfit = ( HTMLfit = (
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" ' ' <li data-role="collapsible" data-iconpos="right" data-shadow="false" '
@@ -234,8 +238,8 @@ class exportHtmlThread(threading.Thread):
pyfalog.warning("Failed to export line") pyfalog.warning("Failed to export line")
continue continue
finally: finally:
if self.callback: if self.progress:
wx.CallAfter(self.callback, count) self.progress.current = count
count += 1 count += 1
HTMLgroup += HTMLship + (' </ul>\n' HTMLgroup += HTMLship + (' </ul>\n'
' </li>\n') ' </li>\n')
@@ -244,7 +248,7 @@ class exportHtmlThread(threading.Thread):
# Market group header # Market group header
HTML += ( HTML += (
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" data-corners="false">\n' ' <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' + ' <ul data-role="listview" data-shadow="false" data-inset="true" data-corners="false">\n' +
HTMLgroup + HTMLgroup +
' </ul>\n' ' </ul>\n'
@@ -254,6 +258,68 @@ class exportHtmlThread(threading.Thread):
HTML += """ HTML += """
</ul> </ul>
</div> </div>
<div data-role="header">
<h1>Pyfa fits by Name</h1>
</div>
<div data-role="content">
"""
HTML += ' <ul data-role="listview" class="ui-listview-outer" data-inset="true" data-filter="true">\n'
categoryList = list(sMkt.getShipRoot())
categoryList.sort(key=lambda _ship: _ship.name)
count = 0
for group in categoryList:
# init market group string to give ships something to attach to
HTMLgroup = ''
ships = list(sMkt.getShipList(group.ID))
ships.sort(key=lambda _ship: _ship.name)
# Keep track of how many ships per group
groupFits = 0
for ship in ships:
fits = sFit.getFitsWithShip(ship.ID)
if len(fits) > 0:
groupFits += len(fits)
for fit in fits:
if self.stopRunning:
return
try:
eftFit = Port.exportEft(getFit(fit[0]), options={
PortEftOptions.IMPLANTS: True,
PortEftOptions.MUTATIONS: True,
PortEftOptions.LOADED_CHARGES: True,
PortEftOptions.BOOSTERS: True,
PortEftOptions.CARGO: True})
HTMLfit = (
' <li data-role="collapsible" data-iconpos="right" data-shadow="false" '
'data-corners="false">\n'
' <h2>' + ship.name + " - " + fit[1] + '</h2>\n'
' <ul data-role="listview" data-shadow="false" data-inset="true" '
'data-corners="false">\n'
)
HTMLfit += ' <li><pre>' + eftFit + '\n </pre></li>\n'
HTMLfit += ' </ul>\n </li>\n'
HTML += HTMLfit
except (KeyboardInterrupt, SystemExit):
raise
except:
pyfalog.warning("Failed to export line")
continue
finally:
if self.progress:
self.progress.current = count
count += 1
HTML += """
</ul>
</div>
</div> </div>
</body> </body>
</html>""" </html>"""
@@ -291,7 +357,7 @@ class exportHtmlThread(threading.Thread):
pyfalog.error("Failed to export line") pyfalog.error("Failed to export line")
continue continue
finally: finally:
if self.callback: if self.progress:
wx.CallAfter(self.callback, count) self.progress.current = count
count += 1 count += 1
return HTML return HTML

View File

@@ -58,6 +58,9 @@ class InputValidator(metaclass=ABCMeta):
class FloatBox(wx.TextCtrl): class FloatBox(wx.TextCtrl):
def __init__(self, parent, value, id=wx.ID_ANY, style=0, validator=None, **kwargs): def __init__(self, parent, value, id=wx.ID_ANY, style=0, validator=None, **kwargs):
# Workaround for #2591
if 'wxMac' in wx.PlatformInfo and 'size' not in kwargs:
kwargs['size'] = wx.Size(97, 26)
super().__init__(parent=parent, id=id, style=style, **kwargs) super().__init__(parent=parent, id=id, style=style, **kwargs)
self.Bind(wx.EVT_TEXT, self.OnText) self.Bind(wx.EVT_TEXT, self.OnText)
self._storedValue = '' self._storedValue = ''
@@ -107,6 +110,9 @@ class FloatBox(wx.TextCtrl):
class FloatRangeBox(wx.TextCtrl): class FloatRangeBox(wx.TextCtrl):
def __init__(self, parent, value, id=wx.ID_ANY, style=0, **kwargs): def __init__(self, parent, value, id=wx.ID_ANY, style=0, **kwargs):
# Workaround for #2591
if 'wxMac' in wx.PlatformInfo and 'size' not in kwargs:
kwargs['size'] = wx.Size(97, 26)
super().__init__(parent=parent, id=id, style=style, **kwargs) super().__init__(parent=parent, id=id, style=style, **kwargs)
self.Bind(wx.EVT_TEXT, self.OnText) self.Bind(wx.EVT_TEXT, self.OnText)
self._storedValue = '' self._storedValue = ''

View File

@@ -0,0 +1,19 @@
class ProgressHelper:
def __init__(self, message, maximum=None, callback=None):
self.message = message
self.current = 0
self.maximum = maximum
self.workerWorking = True
self.dlgWorking = True
self.error = None
self.callback = callback
self.cbArgs = []
@property
def working(self):
return self.workerWorking and self.dlgWorking and not self.error
@property
def userCancelled(self):
return not self.dlgWorking

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 724 B

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 887 B

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 863 B

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 795 B

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 787 B

After

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 908 B

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 893 B

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 B

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 760 B

After

Width:  |  Height:  |  Size: 783 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 861 B

After

Width:  |  Height:  |  Size: 847 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

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