diff --git a/.gitignore b/.gitignore index dcee5d692..c5efd554c 100644 --- a/.gitignore +++ b/.gitignore @@ -118,4 +118,7 @@ ENV/ .idea eos.iml gitversion +.version +/.version *.swp + diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000..26dd19431 --- /dev/null +++ b/.mailmap @@ -0,0 +1,14 @@ +cncfanatics cncfanatics +blitzmann +blitzmann blitzmann +blitzmann +blitzmann blitzman +blitzmann Ryan Holmes +blitzmann +Corollax Corollax +Corollax Corollax +Mr. Nukealizer Mr. Nukealizer +DarkPhoenix +Sakari Orisi +Will Wykeham Will Wykeham +OISumeko OISumeko \ No newline at end of file diff --git a/README.md b/README.md index fd87aed48..2e09ca7f0 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,8 @@ The following is a list of pyfa packages available for certain distributions. Pl ### Dependencies If you wish to help with development or simply need to run pyfa through a Python interpreter, the following software is required: -* Python 2.7 -* `wxPython` 2.8/3.0 -* `sqlalchemy` >= 1.0.5 -* `dateutil` -* `matplotlib` (for some Linux distributions you may need to install separate wxPython bindings such as `python-matplotlib-wx`) -* `requests` -* `logbook` >= 1.0.0 +* Python 3.6 +* Requirements as listed in `requirements.txt` ## Bug Reporting The preferred method of reporting bugs is through the project's [GitHub Issues interface](https://github.com/pyfa-org/Pyfa/issues). Alternatively, posting a report in the [pyfa thread](http://forums.eveonline.com/default.aspx?g=posts&t=247609) on the official EVE Online forums is acceptable. Guidelines for bug reporting can be found on [this wiki page](https://github.com/DarkFenX/Pyfa/wiki/Bug-Reporting). diff --git a/_development/helpers.py b/_development/helpers.py index 4ae17bc5e..8af8f9733 100644 --- a/_development/helpers.py +++ b/_development/helpers.py @@ -28,7 +28,7 @@ def DBInMemory_test(): gamedataCache = True saveddataCache = True gamedata_version = "" - gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(unicode(__file__))), "..", "eve.db")) + gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(str(__file__))), "..", "eve.db")) saveddata_connectionstring = 'sqlite:///:memory:' class ReadOnlyException(Exception): @@ -100,8 +100,8 @@ def DBInMemory(): import eos.db # Output debug info to help us troubleshoot Travis - print(eos.db.saveddata_engine) - print(eos.db.gamedata_engine) + print((eos.db.saveddata_engine)) + print((eos.db.gamedata_engine)) helper = { 'config': eos.config, diff --git a/_development/helpers_fits.py b/_development/helpers_fits.py index 61db7a3ff..a0506827d 100644 --- a/_development/helpers_fits.py +++ b/_development/helpers_fits.py @@ -41,7 +41,7 @@ def CurseFit(DB, Gamedata, Saveddata): mod.state = Saveddata['State'].ONLINE # Add 5 neuts - for _ in xrange(5): + for _ in range(5): fit.modules.append(mod) return fit @@ -60,7 +60,7 @@ def HeronFit(DB, Gamedata, Saveddata): mod.state = Saveddata['State'].ONLINE # Add 5 neuts - for _ in xrange(4): + for _ in range(4): fit.modules.append(mod) return fit \ No newline at end of file diff --git a/_development/helpers_locale.py b/_development/helpers_locale.py index 4d4d29f4e..27983278d 100644 --- a/_development/helpers_locale.py +++ b/_development/helpers_locale.py @@ -94,8 +94,8 @@ def GetUnicodePath(root, file=None, codec=None): path = os.path.join(path, file) if codec: - path = unicode(path, codec) + path = str(path, codec) else: - path = unicode(path) + path = str(path) return path diff --git a/config.py b/config.py index e87c9ec1b..07eb94e37 100644 --- a/config.py +++ b/config.py @@ -1,7 +1,11 @@ import os import sys -from logbook import Logger +from logbook import CRITICAL, DEBUG, ERROR, FingersCrossedHandler, INFO, Logger, NestedSetup, NullHandler, \ + StreamHandler, TimedRotatingFileHandler, WARNING +import hashlib + +from cryptography.fernet import Fernet pyfalog = Logger(__name__) @@ -19,17 +23,39 @@ debug = False saveInRoot = False # Version data -version = "1.36.0" -tag = "Stable" + +version = "2.0.0b5" +tag = "git" expansionName = "YC120.3" expansionVersion = "1.8" evemonMinVersion = "4081" +minItemSearchLength = 3 + pyfaPath = None savePath = None saveDB = None gameDB = None logPath = None +loggingLevel = None +logging_setup = None +cipher = None +clientHash = None + +ESI_AUTH_PROXY = "https://www.pyfa.io" # "http://localhost:5015" +ESI_CACHE = 'esi_cache' + +LOGLEVEL_MAP = { + "critical": CRITICAL, + "error": ERROR, + "warning": WARNING, + "info": INFO, + "debug": DEBUG, +} + + +def getClientSecret(): + return clientHash def isFrozen(): @@ -45,23 +71,37 @@ def __createDirs(path): def getPyfaRoot(): - base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else sys.argv[0] + if hasattr(sys, '_MEIPASS'): + return sys._MEIPASS + base = getattr(sys.modules['__main__'], "__file__", sys.executable) if isFrozen() else __file__ root = os.path.dirname(os.path.realpath(os.path.abspath(base))) - root = unicode(root, sys.getfilesystemencoding()) + root = root return root +def getVersion(): + if os.path.isfile(os.path.join(pyfaPath, '.version')): + with open(os.path.join(pyfaPath, '.version')) as f: + gitVersion = f.readline() + return gitVersion + # if no version file exists, then user is running from source or not an official build + return version + " (git)" + + def getDefaultSave(): - return unicode(os.path.expanduser(os.path.join("~", ".pyfa")), sys.getfilesystemencoding()) + return os.path.expanduser(os.path.join("~", ".pyfa")) -def defPaths(customSavePath): +def defPaths(customSavePath=None): global debug global pyfaPath global savePath global saveDB global gameDB global saveInRoot + global logPath + global cipher + global clientHash pyfalog.debug("Configuring Pyfa") @@ -86,12 +126,22 @@ def defPaths(customSavePath): __createDirs(savePath) - if isFrozen(): - os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8') - os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem").encode('utf8') + secret_file = os.path.join(savePath, ".secret") + if not os.path.exists(secret_file): + with open(secret_file, "wb") as _file: + _file.write(Fernet.generate_key()) + + with open(secret_file, 'rb') as fp: + key = fp.read() + clientHash = hashlib.sha3_256(key).hexdigest() + cipher = Fernet(key) + + # if isFrozen(): + # os.environ["REQUESTS_CA_BUNDLE"] = os.path.join(pyfaPath, "cacert.pem") + # os.environ["SSL_CERT_FILE"] = os.path.join(pyfaPath, "cacert.pem") # The database where we store all the fits etc - saveDB = os.path.join(savePath, "saveddata.db") + saveDB = os.path.join(savePath, "saveddata-py3-dev.db") # The database where the static EVE data from the datadump is kept. # This is not the standard sqlite datadump but a modified version created by eos @@ -100,6 +150,13 @@ def defPaths(customSavePath): if not gameDB: gameDB = os.path.join(pyfaPath, "eve.db") + if debug: + logFile = "pyfa_debug.log" + else: + logFile = "pyfa.log" + + logPath = os.path.join(savePath, logFile) + # DON'T MODIFY ANYTHING BELOW import eos.config @@ -109,6 +166,100 @@ def defPaths(customSavePath): eos.config.saveddata_connectionstring = "sqlite:///" + saveDB + "?check_same_thread=False" eos.config.gamedata_connectionstring = "sqlite:///" + gameDB + "?check_same_thread=False" + print(eos.config.saveddata_connectionstring) + print(eos.config.gamedata_connectionstring) + # initialize the settings from service.settings import EOSSettings eos.config.settings = EOSSettings.getInstance().EOSSettings # this is kind of confusing, but whatever + + +def defLogging(): + global debug + global logPath + global loggingLevel + global logging_setup + + try: + if debug: + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + StreamHandler( + sys.stdout, + bubble=False, + level=loggingLevel + ), + TimedRotatingFileHandler( + logPath, + level=0, + backup_count=3, + bubble=True, + date_format='%Y-%m-%d', + ), + ]) + else: + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + FingersCrossedHandler( + TimedRotatingFileHandler( + logPath, + level=0, + backup_count=3, + bubble=False, + date_format='%Y-%m-%d', + ), + action_level=ERROR, + buffer_size=1000, + # pull_information=True, + # reset=False, + ) + ]) + except: + print("Critical error attempting to setup logging. Falling back to console only.") + logging_setup = NestedSetup([ + # make sure we never bubble up to the stderr handler + # if we run out of setup handling + NullHandler(), + StreamHandler( + sys.stdout, + bubble=False + ) + ]) + + with logging_setup.threadbound(): + + # Output all stdout (print) messages as warnings + try: + sys.stdout = LoggerWriter(pyfalog.warning) + except: + pyfalog.critical("Cannot redirect. Continuing without writing stdout to log.") + + # Output all stderr (stacktrace) messages as critical + try: + sys.stderr = LoggerWriter(pyfalog.critical) + except: + pyfalog.critical("Cannot redirect. Continuing without writing stderr to log.") + + +class LoggerWriter(object): + def __init__(self, level): + # self.level is really like using log.debug(message) + # at least in my case + self.level = level + + def write(self, message): + # if statement reduces the amount of newlines that are + # printed to the logger + if message.strip() != '': + self.level(message.replace("\n", "")) + + def flush(self): + # create a flush method so things can be flushed when + # the system wants to. Not sure if simply 'printing' + # sys.stderr is the correct way to do it, but it seemed + # to work properly for me. + self.level(sys.stderr) diff --git a/dist_assets/linux/pyfa-linux.spec b/dist_assets/linux/pyfa-linux.spec new file mode 100644 index 000000000..bfc24a2f2 --- /dev/null +++ b/dist_assets/linux/pyfa-linux.spec @@ -0,0 +1,70 @@ +# -*- mode: python -*- + +import os +from itertools import chain +import subprocess + +label = subprocess.check_output([ + "git", "describe", "--tags"]).strip() + +with open('gitversion', 'w+') as f: + f.write(label.decode()) + +block_cipher = None + + +added_files = [ + ( 'imgs/gui/*.png', 'imgs/gui' ), + ( 'imgs/gui/*.gif', 'imgs/gui' ), + ( 'imgs/icons/*.png', 'imgs/icons' ), + ( 'imgs/renders/*.png', 'imgs/renders' ), + ( 'dist_assets/win/pyfa.ico', '.' ), + ( 'dist_assets/cacert.pem', '.' ), + ( 'eve.db', '.' ), + ( 'README.md', '.' ), + ( 'LICENSE', '.' ), + ( 'gitversion', '.' ), + ] + +import_these = [] + +# Walk directories that do dynamic importing +paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') +for root, folders, files in chain.from_iterable(os.walk(path) for path in paths): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + import_these.append(mod_name) + + +a = Analysis(['pyfa.py'], + pathex=[], + binaries=[], + datas=added_files, + hiddenimports=import_these, + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name='pyfa', + debug=False, + strip=False, + upx=True, + console=True ) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + name='pyfa') diff --git a/dist_assets/mac/pyfa.spec b/dist_assets/mac/pyfa.spec new file mode 100644 index 000000000..a3214b3d3 --- /dev/null +++ b/dist_assets/mac/pyfa.spec @@ -0,0 +1,74 @@ +# -*- mode: python -*- + +import os +from itertools import chain +import subprocess +import requests.certs + +label = subprocess.check_output([ + "git", "describe", "--tags"]).strip() + +with open('.version', 'w+') as f: + f.write(label.decode()) + +block_cipher = None + +added_files = [ + ('../../imgs/gui/*.png', 'imgs/gui'), + ('../../imgs/gui/*.gif', 'imgs/gui'), + ('../../imgs/icons/*.png', 'imgs/icons'), + ('../../imgs/renders/*.png', 'imgs/renders'), + ('../../dist_assets/win/pyfa.ico', '.'), + ('../../service/jargon/*.yaml', 'service/jargon'), + (requests.certs.where(), '.'), # is this needed anymore? + ('../../eve.db', '.'), + ('../../README.md', '.'), + ('../../LICENSE', '.'), + ('../../.version', '.'), + ] + + +import_these = [] + +# Walk directories that do dynamic importing +paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') +for root, folders, files in chain.from_iterable(os.walk(path) for path in paths): + for file_ in files: + if file_.endswith(".py") and not file_.startswith("_"): + mod_name = "{}.{}".format( + root.replace("/", "."), + file_.split(".py")[0], + ) + import_these.append(mod_name) + +a = Analysis([r'../../pyfa.py'], + pathex=[], + binaries=[], + datas=added_files, + hiddenimports=import_these, + hookspath=['dist_assets/pyinstaller_hooks'], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + name='pyfa', + debug=False, + strip=False, + upx=True, + runtime_tmpdir=None, + console=False , + icon='dist_assets/mac/pyfa.icns', + ) + +app = BUNDLE(exe, + name='pyfa.app', + icon=None, + bundle_identifier=None) \ No newline at end of file diff --git a/dist_assets/pyinstaller_hooks/hook-matplotlib.backends.py b/dist_assets/pyinstaller_hooks/hook-matplotlib.backends.py new file mode 100644 index 000000000..d73ba1ec3 --- /dev/null +++ b/dist_assets/pyinstaller_hooks/hook-matplotlib.backends.py @@ -0,0 +1,78 @@ +# This apes hook-matplotlib.backends.py, but REMOVES backends, all but +# the ones in the list below. +# Courtesy of https://github.com/bpteague/cytoflow/blob/70f9291/packaging/hook-matplotlib.backends.py + +KEEP = ["WXAgg", "WX", "agg"] + +from PyInstaller.compat import is_darwin +from PyInstaller.utils.hooks import ( + eval_statement, exec_statement, logger) + + +def get_matplotlib_backend_module_names(): + """ + List the names of all matplotlib backend modules importable under the + current Python installation. + Returns + ---------- + list + List of the fully-qualified names of all such modules. + """ + # Statement safely importing a single backend module. + import_statement = """ +import os, sys +# Preserve stdout. +sys_stdout = sys.stdout +try: + # Redirect output printed by this importation to "/dev/null", preventing + # such output from being erroneously interpreted as an error. + with open(os.devnull, 'w') as dev_null: + sys.stdout = dev_null + __import__('%s') +# If this is an ImportError, print this exception's message without a traceback. +# ImportError messages are human-readable and require no additional context. +except ImportError as exc: + sys.stdout = sys_stdout + print(exc) +# Else, print this exception preceded by a traceback. traceback.print_exc() +# prints to stderr rather than stdout and must not be called here! +except Exception: + sys.stdout = sys_stdout + import traceback + print(traceback.format_exc()) +""" + + # List of the human-readable names of all available backends. + backend_names = eval_statement( + 'import matplotlib; print(matplotlib.rcsetup.all_backends)') + + # List of the fully-qualified names of all importable backend modules. + module_names = [] + + # If the current system is not OS X and the "CocoaAgg" backend is available, + # remove this backend from consideration. Attempting to import this backend + # on non-OS X systems halts the current subprocess without printing output + # or raising exceptions, preventing its reliable detection. + if not is_darwin and 'CocoaAgg' in backend_names: + backend_names.remove('CocoaAgg') + + # For safety, attempt to import each backend in a unique subprocess. + for backend_name in backend_names: + if backend_name in KEEP: + continue + + module_name = 'matplotlib.backends.backend_%s' % backend_name.lower() + stdout = exec_statement(import_statement % module_name) + + # If no output was printed, this backend is importable. + if not stdout: + module_names.append(module_name) + logger.info(' Matplotlib backend "%s": removed' % backend_name) + + return module_names + +# Freeze all importable backends, as PyInstaller is unable to determine exactly +# which backends are required by the current program. +e=get_matplotlib_backend_module_names() +print(e) +excludedimports = e \ No newline at end of file diff --git a/dist_assets/win/pyfa.spec b/dist_assets/win/pyfa.spec index 0f61a860d..70eb4673d 100644 --- a/dist_assets/win/pyfa.spec +++ b/dist_assets/win/pyfa.spec @@ -1,35 +1,37 @@ # -*- mode: python -*- -# Note: This script is provided AS-IS for those that may be interested. -# pyfa does not currently support pyInstaller (or any other build process) 100% at the moment - -# Command line to build: -# (Run from directory where pyfa.py and pyfa.spec lives.) -# c:\Python27\scripts\pyinstaller.exe --clean --noconfirm --windowed --upx-dir=.\scripts\upx.exe pyfa.spec - -# Don't forget to change the path to where your pyfa.py and pyfa.spec lives -# pathex=['C:\\Users\\Ebag333\\Documents\\GitHub\\Ebag333\\Pyfa'], - import os +from itertools import chain +import subprocess +import requests.certs + +label = subprocess.check_output([ + "git", "describe", "--tags"]).strip() + +with open('.version', 'w+') as f: + f.write(label.decode()) block_cipher = None added_files = [ - ( 'imgs/gui/*.png', 'imgs/gui' ), - ( 'imgs/gui/*.gif', 'imgs/gui' ), - ( 'imgs/icons/*.png', 'imgs/icons' ), - ( 'imgs/renders/*.png', 'imgs/renders' ), - ( 'dist_assets/win/pyfa.ico', '.' ), - ( 'dist_assets/cacert.pem', '.' ), - ( 'eve.db', '.' ), - ( 'README.md', '.' ), - ( 'LICENSE', '.' ), + ('../../imgs/gui/*.png', 'imgs/gui'), + ('../../imgs/gui/*.gif', 'imgs/gui'), + ('../../imgs/icons/*.png', 'imgs/icons'), + ('../../imgs/renders/*.png', 'imgs/renders'), + ('../../service/jargon/*.yaml', 'service/jargon'), + ('../../dist_assets/win/pyfa.ico', '.'), + (requests.certs.where(), '.'), # is this needed anymore? + ('../../eve.db', '.'), + ('../../README.md', '.'), + ('../../LICENSE', '.'), + ('../../.version', '.'), ] import_these = [] -# Walk eos.effects and add all effects so we can import them properly -for root, folders, files in os.walk("eos/effects"): +# Walk directories that do dynamic importing +paths = ('eos/effects', 'eos/db/migrations', 'service/conversions') +for root, folders, files in chain.from_iterable(os.walk(path) for path in paths): for file_ in files: if file_.endswith(".py") and not file_.startswith("_"): mod_name = "{}.{}".format( @@ -38,25 +40,24 @@ for root, folders, files in os.walk("eos/effects"): ) import_these.append(mod_name) -a = Analysis( - ['pyfa.py'], - pathex=['C:\\projects\\pyfa\\'], +a = Analysis(['../../pyfa.py'], + pathex=[ + # Need this, see https://github.com/pyinstaller/pyinstaller/issues/1566 + # To get this, download and install windows 10 SDK + # If not building on Windows 10, this might be optional + r'C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86'], binaries=[], datas=added_files, hiddenimports=import_these, - hookspath=[], + hookspath=['dist_assets/pyinstaller_hooks'], runtime_hooks=[], - excludes=[], + excludes=['Tkinter'], win_no_prefer_redirects=False, win_private_assemblies=False, - cipher=block_cipher, - ) + cipher=block_cipher) -pyz = PYZ( - a.pure, - a.zipped_data, - cipher=block_cipher, - ) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) exe = EXE(pyz, a.scripts, @@ -67,7 +68,6 @@ exe = EXE(pyz, upx=True, name='pyfa', icon='dist_assets/win/pyfa.ico', - onefile=False, ) coll = COLLECT( @@ -77,7 +77,6 @@ coll = COLLECT( a.datas, strip=False, upx=True, - onefile=False, name='pyfa', icon='dist_assets/win/pyfa.ico', - ) + ) \ No newline at end of file diff --git a/dist_assets/win/version_resource.py b/dist_assets/win/version_resource.py new file mode 100644 index 000000000..a5018edd7 --- /dev/null +++ b/dist_assets/win/version_resource.py @@ -0,0 +1,45 @@ +# UTF-8 +# +# For more details about fixed file info 'ffi' see: +# http://msdn.microsoft.com/en-us/library/ms646997.aspx +VSVersionInfo( + ffi=FixedFileInfo( + # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) + # Set not needed items to zero 0. + filevers=(1, 15, 1, 0), + prodvers=(1, 15, 1, 0), + # Contains a bitmask that specifies the valid bits 'flags'r + mask=0x3f, + # Contains a bitmask that specifies the Boolean attributes of the file. + flags=0x0, + # The operating system for which this file was designed. + # 0x4 - NT and there is no need to change it. + OS=0x40004, + # The general type of file. + # 0x1 - the file is an application. + fileType=0x1, + # The function of the file. + # 0x0 - the function is not defined for this fileType + subtype=0x0, + # Creation date and time stamp. + date=(0, 0) + ), + kids=[ + StringFileInfo( + [ + StringTable( + u'040904E4', + [StringStruct(u'LegalCopyright', u''), + StringStruct(u'InternalName', u'pyfa.exe'), + StringStruct(u'FileVersion', u'1.15.1.0'), + StringStruct(u'CompanyName', u''), + StringStruct(u'OriginalFilename', u'pyfa.exe'), + StringStruct(u'ProductVersion', u'1.15.1.0'), + StringStruct(u'FileDescription', u'Python fitting assistant'), + StringStruct(u'LegalTrademarks', u''), + StringStruct(u'Comments', u''), + StringStruct(u'ProductName', u'pyfa')]) + ]), + VarFileInfo([VarStruct(u'Translation', [1033, 1252])]) + ] +) diff --git a/eos/capSim.py b/eos/capSim.py index 3a2f965c3..8b16c3e0f 100644 --- a/eos/capSim.py +++ b/eos/capSim.py @@ -1,6 +1,7 @@ import heapq import time from math import sqrt, exp +from functools import reduce DAY = 24 * 60 * 60 * 1000 @@ -88,7 +89,7 @@ class CapSimulator(object): mods[(duration, capNeed, clipSize, disableStagger, reloadTime)] = 1 # Loop over grouped modules, configure staggering and push to the simulation state - for (duration, capNeed, clipSize, disableStagger, reloadTime), amount in mods.iteritems(): + for (duration, capNeed, clipSize, disableStagger, reloadTime), amount in mods.items(): if self.stagger and not disableStagger: if clipSize == 0: duration = int(duration / amount) @@ -193,7 +194,7 @@ class CapSimulator(object): # calculate EVE's stability value try: - avgDrain = reduce(float.__add__, map(lambda x: x[2] / x[1], self.state), 0.0) + avgDrain = reduce(float.__add__, [x[2] / x[1] for x in self.state], 0.0) self.cap_stable_eve = 0.25 * (1.0 + sqrt(-(2.0 * avgDrain * tau - capCapacity) / capCapacity)) ** 2 except ValueError: self.cap_stable_eve = 0.0 diff --git a/eos/config.py b/eos/config.py index ec64e2bd3..6bc31eef8 100644 --- a/eos/config.py +++ b/eos/config.py @@ -11,14 +11,14 @@ debug = False gamedataCache = True saveddataCache = True gamedata_version = "" -gamedata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "eve.db")), sys.getfilesystemencoding()) +gamedata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "eve.db")) pyfalog.debug("Gamedata connection string: {0}", gamedata_connectionstring) if istravis is True or hasattr(sys, '_called_from_test'): # Running in Travis. Run saveddata database in memory. saveddata_connectionstring = 'sqlite:///:memory:' else: - saveddata_connectionstring = 'sqlite:///' + unicode(realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata.db")), sys.getfilesystemencoding()) + saveddata_connectionstring = 'sqlite:///' + realpath(join(dirname(abspath(__file__)), "..", "saveddata", "saveddata-py3-db.db")) pyfalog.debug("Saveddata connection string: {0}", saveddata_connectionstring) @@ -28,4 +28,4 @@ settings = { } # Autodetect path, only change if the autodetection bugs out. -path = dirname(unicode(__file__, sys.getfilesystemencoding())) +path = dirname(__file__) diff --git a/eos/db/__init__.py b/eos/db/__init__.py index a29bb86c7..dd027841b 100644 --- a/eos/db/__init__.py +++ b/eos/db/__init__.py @@ -22,7 +22,7 @@ import threading from sqlalchemy import MetaData, create_engine from sqlalchemy.orm import sessionmaker -import migration +from . import migration from eos import config from logbook import Logger @@ -76,7 +76,7 @@ sd_lock = threading.RLock() # noinspection PyPep8 from eos.db.gamedata import alphaClones, attribute, category, effect, group, icon, item, marketGroup, metaData, metaGroup, queries, traits, unit # noinspection PyPep8 -from eos.db.saveddata import booster, cargo, character, crest, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \ +from eos.db.saveddata import booster, cargo, character, damagePattern, databaseRepair, drone, fighter, fit, implant, implantSet, loadDefaultDatabaseValues, \ miscData, module, override, price, queries, skill, targetResists, user # Import queries diff --git a/eos/db/gamedata/queries.py b/eos/db/gamedata/queries.py index 7a50d726c..737169897 100644 --- a/eos/db/gamedata/queries.py +++ b/eos/db/gamedata/queries.py @@ -81,7 +81,7 @@ def getItem(lookfor, eager=None): item = gamedata_session.query(Item).get(lookfor) else: item = gamedata_session.query(Item).options(*processEager(eager)).filter(Item.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): if lookfor in itemNameMap: id = itemNameMap[lookfor] if eager is None: @@ -154,7 +154,7 @@ def getGroup(lookfor, eager=None): group = gamedata_session.query(Group).get(lookfor) else: group = gamedata_session.query(Group).options(*processEager(eager)).filter(Group.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): if lookfor in groupNameMap: id = groupNameMap[lookfor] if eager is None: @@ -181,7 +181,7 @@ def getCategory(lookfor, eager=None): else: category = gamedata_session.query(Category).options(*processEager(eager)).filter( Category.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): if lookfor in categoryNameMap: id = categoryNameMap[lookfor] if eager is None: @@ -210,7 +210,7 @@ def getMetaGroup(lookfor, eager=None): else: metaGroup = gamedata_session.query(MetaGroup).options(*processEager(eager)).filter( MetaGroup.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): if lookfor in metaGroupNameMap: id = metaGroupNameMap[lookfor] if eager is None: @@ -245,7 +245,7 @@ def getMarketGroup(lookfor, eager=None): def getItemsByCategory(filter, where=None, eager=None): if isinstance(filter, int): filter = Category.ID == filter - elif isinstance(filter, basestring): + elif isinstance(filter, str): filter = Category.name == filter else: raise TypeError("Need integer or string as argument") @@ -257,7 +257,7 @@ def getItemsByCategory(filter, where=None, eager=None): @cachedQuery(3, "where", "nameLike", "join") def searchItems(nameLike, where=None, join=None, eager=None): - if not isinstance(nameLike, basestring): + if not isinstance(nameLike, str): raise TypeError("Need string as argument") if join is None: @@ -268,7 +268,7 @@ def searchItems(nameLike, where=None, join=None, eager=None): items = gamedata_session.query(Item).options(*processEager(eager)).join(*join) for token in nameLike.split(' '): - token_safe = u"%{0}%".format(sqlizeString(token)) + token_safe = "%{0}%".format(sqlizeString(token)) if where is not None: items = items.filter(and_(Item.name.like(token_safe, escape="\\"), where)) else: @@ -279,12 +279,12 @@ def searchItems(nameLike, where=None, join=None, eager=None): @cachedQuery(3, "where", "nameLike", "join") def searchSkills(nameLike, where=None, eager=None): - if not isinstance(nameLike, basestring): + if not isinstance(nameLike, str): raise TypeError("Need string as argument") items = gamedata_session.query(Item).options(*processEager(eager)).join(Item.group, Group.category) for token in nameLike.split(' '): - token_safe = u"%{0}%".format(sqlizeString(token)) + token_safe = "%{0}%".format(sqlizeString(token)) if where is not None: items = items.filter(and_(Item.name.like(token_safe, escape="\\"), Category.ID == 16, where)) else: @@ -322,7 +322,7 @@ def getVariations(itemids, groupIDs=None, where=None, eager=None): @cachedQuery(1, "attr") def getAttributeInfo(attr, eager=None): - if isinstance(attr, basestring): + if isinstance(attr, str): filter = AttributeInfo.name == attr elif isinstance(attr, int): filter = AttributeInfo.ID == attr @@ -337,7 +337,7 @@ def getAttributeInfo(attr, eager=None): @cachedQuery(1, "field") def getMetaData(field): - if isinstance(field, basestring): + if isinstance(field, str): data = gamedata_session.query(MetaData).get(field) else: raise TypeError("Need string as argument") @@ -367,7 +367,7 @@ def getRequiredFor(itemID, attrMapping): skillToLevelClauses = [] - for attrSkill, attrLevel in attrMapping.iteritems(): + for attrSkill, attrLevel in attrMapping.items(): skillToLevelClauses.append(and_(Attribute1.attributeID == attrSkill, Attribute2.attributeID == attrLevel)) queryOr = or_(*skillToLevelClauses) diff --git a/eos/db/migration.py b/eos/db/migration.py index 3d8921eb2..4832daaa9 100644 --- a/eos/db/migration.py +++ b/eos/db/migration.py @@ -3,7 +3,7 @@ import shutil import time import config -import migrations +from . import migrations pyfalog = Logger(__name__) @@ -34,7 +34,7 @@ def update(saveddata_engine): shutil.copyfile(config.saveDB, toFile) - for version in xrange(dbVersion, appVersion): + for version in range(dbVersion, appVersion): func = migrations.updates[version + 1] if func: pyfalog.info("Applying database update: {0}", version + 1) diff --git a/eos/db/migrations/__init__.py b/eos/db/migrations/__init__.py index 94b33409a..e9797c352 100644 --- a/eos/db/migrations/__init__.py +++ b/eos/db/migrations/__init__.py @@ -15,7 +15,27 @@ updates = {} appVersion = 0 prefix = __name__ + "." -for importer, modname, ispkg in pkgutil.iter_modules(__path__, prefix): + +# load modules to work based with and without pyinstaller +# from: https://github.com/webcomics/dosage/blob/master/dosagelib/loader.py +# see: https://github.com/pyinstaller/pyinstaller/issues/1905 + +# load modules using iter_modules() +# (should find all filters in normal build, but not pyinstaller) +module_names = [m[1] for m in pkgutil.iter_modules(__path__, prefix)] + +# special handling for PyInstaller +importers = map(pkgutil.get_importer, __path__) +toc = set() +for i in importers: + if hasattr(i, 'toc'): + toc |= i.toc + +for elm in toc: + if elm.startswith(prefix): + module_names.append(elm) + +for modname in module_names: # loop through python files, extracting update number and function, and # adding it to a list modname_tail = modname.rsplit('.', 1)[-1] diff --git a/eos/db/migrations/upgrade1.py b/eos/db/migrations/upgrade1.py index 22292dc4f..9059b28de 100644 --- a/eos/db/migrations/upgrade1.py +++ b/eos/db/migrations/upgrade1.py @@ -91,7 +91,7 @@ def upgrade(saveddata_engine): saveddata_engine.execute("ALTER TABLE fits ADD COLUMN targetResistsID INTEGER;") # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade11.py b/eos/db/migrations/upgrade11.py index 2811cf5be..248822484 100644 --- a/eos/db/migrations/upgrade11.py +++ b/eos/db/migrations/upgrade11.py @@ -108,7 +108,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade12.py b/eos/db/migrations/upgrade12.py index 59d6d08d9..6d8e56d3a 100644 --- a/eos/db/migrations/upgrade12.py +++ b/eos/db/migrations/upgrade12.py @@ -332,7 +332,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade18.py b/eos/db/migrations/upgrade18.py index 4a13b7d57..8594c950e 100644 --- a/eos/db/migrations/upgrade18.py +++ b/eos/db/migrations/upgrade18.py @@ -60,7 +60,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade22.py b/eos/db/migrations/upgrade22.py index fb821c2d1..568351a25 100644 --- a/eos/db/migrations/upgrade22.py +++ b/eos/db/migrations/upgrade22.py @@ -29,7 +29,7 @@ def upgrade(saveddata_engine): "targetResists": 2 } - for table in tables.keys(): + for table in list(tables.keys()): # midnight brain, there's probably a much more simple way to do this, but fuck it if tables[table] > 0: diff --git a/eos/db/migrations/upgrade25.py b/eos/db/migrations/upgrade25.py index bf334c37d..703048f37 100644 --- a/eos/db/migrations/upgrade25.py +++ b/eos/db/migrations/upgrade25.py @@ -4204,7 +4204,7 @@ conversion2 = { def upgrade(saveddata_engine): # First we want to get a list of fittings that are completely fitted out with subsystems - oldItems = [str(x) for x in conversion2.iterkeys()] + oldItems = [str(x) for x in conversion2.keys()] # I can't figure out a way to get IN operator to work when supplying a list using a parameterized query. So I'm # doing it the shitty way by formatting the SQL string. Don't do this kids! @@ -4239,7 +4239,7 @@ def upgrade(saveddata_engine): # if something fails, fuck it, we tried. It'll default to the generic conversion below continue - for oldItem, newItem in conversion2.iteritems(): + for oldItem, newItem in conversion2.items(): saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (newItem, oldItem)) saveddata_engine.execute('UPDATE "cargo" SET "itemID" = ? WHERE "itemID" = ?', diff --git a/eos/db/migrations/upgrade4.py b/eos/db/migrations/upgrade4.py index d1e46d10a..b94d965f2 100644 --- a/eos/db/migrations/upgrade4.py +++ b/eos/db/migrations/upgrade4.py @@ -133,7 +133,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade7.py b/eos/db/migrations/upgrade7.py index fbb74f910..21797e5c5 100644 --- a/eos/db/migrations/upgrade7.py +++ b/eos/db/migrations/upgrade7.py @@ -17,7 +17,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert ships - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "fits" SET "shipID" = ? WHERE "shipID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/migrations/upgrade8.py b/eos/db/migrations/upgrade8.py index 0051034ec..7c1327416 100644 --- a/eos/db/migrations/upgrade8.py +++ b/eos/db/migrations/upgrade8.py @@ -77,7 +77,7 @@ CONVERSIONS = { def upgrade(saveddata_engine): # Convert modules - for replacement_item, list in CONVERSIONS.iteritems(): + for replacement_item, list in CONVERSIONS.items(): for retired_item in list: saveddata_engine.execute('UPDATE "modules" SET "itemID" = ? WHERE "itemID" = ?', (replacement_item, retired_item)) diff --git a/eos/db/saveddata/__init__.py b/eos/db/saveddata/__init__.py index 7dbf2a45d..ba1ddad73 100644 --- a/eos/db/saveddata/__init__.py +++ b/eos/db/saveddata/__init__.py @@ -12,7 +12,6 @@ __all__ = [ "miscData", "targetResists", "override", - "crest", "implantSet", "loadDefaultDatabaseValues" ] diff --git a/eos/db/saveddata/character.py b/eos/db/saveddata/character.py index c87817541..739a34a98 100644 --- a/eos/db/saveddata/character.py +++ b/eos/db/saveddata/character.py @@ -17,24 +17,24 @@ # along with eos. If not, see . # =============================================================================== -from sqlalchemy import Table, Column, Integer, ForeignKey, String, DateTime, Float +from sqlalchemy import Table, Column, Integer, ForeignKey, String, DateTime, Float, UniqueConstraint from sqlalchemy.orm import relation, mapper import datetime from eos.db import saveddata_meta from eos.db.saveddata.implant import charImplants_table -from eos.effectHandlerHelpers import HandledImplantBoosterList +from eos.effectHandlerHelpers import HandledImplantBoosterList, HandledSsoCharacterList from eos.saveddata.implant import Implant from eos.saveddata.user import User from eos.saveddata.character import Character, Skill +from eos.saveddata.ssocharacter import SsoCharacter + + + characters_table = Table("characters", saveddata_meta, Column("ID", Integer, primary_key=True), Column("name", String, nullable=False), - Column("apiID", Integer), - Column("apiKey", String), - Column("defaultChar", Integer), - Column("chars", String, nullable=True), Column("defaultLevel", Integer, nullable=True), Column("alphaCloneID", Integer, nullable=True), Column("ownerID", ForeignKey("users.ID"), nullable=True), @@ -42,6 +42,28 @@ characters_table = Table("characters", saveddata_meta, Column("created", DateTime, nullable=True, default=datetime.datetime.now), Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now)) +sso_table = Table("ssoCharacter", saveddata_meta, + Column("ID", Integer, primary_key=True), + Column("client", String, nullable=False), + Column("characterID", Integer, nullable=False), + Column("characterName", String, nullable=False), + Column("refreshToken", String, nullable=False), + Column("accessToken", String, nullable=False), + Column("accessTokenExpires", DateTime, nullable=False), + Column("created", DateTime, nullable=True, default=datetime.datetime.now), + Column("modified", DateTime, nullable=True, onupdate=datetime.datetime.now), + UniqueConstraint('client', 'characterID', name='uix_client_characterID'), + UniqueConstraint('client', 'characterName', name='uix_client_characterName') + ) + +sso_character_map_table = Table("ssoCharacterMap", saveddata_meta, + Column("characterID", ForeignKey("characters.ID"), primary_key=True), + Column("ssoCharacterID", ForeignKey("ssoCharacter.ID"), primary_key=True), + ) + + +mapper(SsoCharacter, sso_table) + mapper(Character, characters_table, properties={ "_Character__alphaCloneID": characters_table.c.alphaCloneID, @@ -63,5 +85,10 @@ mapper(Character, characters_table, primaryjoin=charImplants_table.c.charID == characters_table.c.ID, secondaryjoin=charImplants_table.c.implantID == Implant.ID, secondary=charImplants_table), + "_Character__ssoCharacters" : relation( + SsoCharacter, + collection_class=HandledSsoCharacterList, + backref='characters', + secondary=sso_character_map_table) } ) diff --git a/eos/db/saveddata/crest.py b/eos/db/saveddata/crest.py deleted file mode 100644 index 28f77a983..000000000 --- a/eos/db/saveddata/crest.py +++ /dev/null @@ -1,34 +0,0 @@ -# =============================================================================== -# Copyright (C) 2010 Diego Duclos -# -# This file is part of eos. -# -# eos is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# eos is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with eos. If not, see . -# =============================================================================== - -from sqlalchemy import Table, Column, Integer, String, DateTime -from sqlalchemy.orm import mapper -import datetime - -from eos.db import saveddata_meta -from eos.saveddata.crestchar import CrestChar - -crest_table = Table("crest", saveddata_meta, - Column("ID", Integer, primary_key=True), - Column("name", String, nullable=False, unique=True), - Column("refresh_token", String, nullable=False), - # These records aren't updated. Instead, they are dropped and created, hence we don't have a modified field - Column("created", DateTime, nullable=True, default=datetime.datetime.now)) - -mapper(CrestChar, crest_table) diff --git a/eos/db/saveddata/queries.py b/eos/db/saveddata/queries.py index fd8ae93db..e582eef87 100644 --- a/eos/db/saveddata/queries.py +++ b/eos/db/saveddata/queries.py @@ -27,7 +27,7 @@ from eos.db.saveddata.fit import projectedFits_table from eos.db.util import processEager, processWhere from eos.saveddata.price import Price from eos.saveddata.user import User -from eos.saveddata.crestchar import CrestChar +from eos.saveddata.ssocharacter import SsoCharacter from eos.saveddata.damagePattern import DamagePattern from eos.saveddata.targetResists import TargetResists from eos.saveddata.character import Character @@ -109,9 +109,9 @@ if configVal is True: if type not in queryCache: return functionCache = queryCache[type] - for _, localCache in functionCache.iteritems(): + for _, localCache in functionCache.items(): toDelete = set() - for cacheKey, info in localCache.iteritems(): + for cacheKey, info in localCache.items(): IDs = info[1] if ID in IDs: toDelete.add(cacheKey) @@ -156,7 +156,7 @@ def getUser(lookfor, eager=None): eager = processEager(eager) with sd_lock: user = saveddata_session.query(User).options(*eager).filter(User.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: user = saveddata_session.query(User).options(*eager).filter(User.username == lookfor).first() @@ -175,7 +175,7 @@ def getCharacter(lookfor, eager=None): eager = processEager(eager) with sd_lock: character = saveddata_session.query(Character).options(*eager).filter(Character.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: character = saveddata_session.query(Character).options(*eager).filter( @@ -337,7 +337,7 @@ def clearPrices(): def getMiscData(field): - if isinstance(field, basestring): + if isinstance(field, str): with sd_lock: data = saveddata_session.query(MiscData).get(field) else: @@ -391,7 +391,7 @@ def getDamagePattern(lookfor, eager=None): with sd_lock: pattern = saveddata_session.query(DamagePattern).options(*eager).filter( DamagePattern.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: pattern = saveddata_session.query(DamagePattern).options(*eager).filter( @@ -412,7 +412,7 @@ def getTargetResists(lookfor, eager=None): with sd_lock: pattern = saveddata_session.query(TargetResists).options(*eager).filter( TargetResists.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: pattern = saveddata_session.query(TargetResists).options(*eager).filter( @@ -433,7 +433,7 @@ def getImplantSet(lookfor, eager=None): with sd_lock: pattern = saveddata_session.query(ImplantSet).options(*eager).filter( TargetResists.ID == lookfor).first() - elif isinstance(lookfor, basestring): + elif isinstance(lookfor, str): eager = processEager(eager) with sd_lock: pattern = saveddata_session.query(ImplantSet).options(*eager).filter(TargetResists.name == lookfor).first() @@ -443,10 +443,10 @@ def getImplantSet(lookfor, eager=None): def searchFits(nameLike, where=None, eager=None): - if not isinstance(nameLike, basestring): + if not isinstance(nameLike, str): raise TypeError("Need string as argument") # Prepare our string for request - nameLike = u"%{0}%".format(sqlizeString(nameLike)) + nameLike = "%{0}%".format(sqlizeString(nameLike)) # Add any extra components to the search to our where clause filter = processWhere(Fit.name.like(nameLike, escape="\\"), where) @@ -467,29 +467,28 @@ def getProjectedFits(fitID): raise TypeError("Need integer as argument") -def getCrestCharacters(eager=None): +def getSsoCharacters(clientHash, eager=None): eager = processEager(eager) with sd_lock: - characters = saveddata_session.query(CrestChar).options(*eager).all() + characters = saveddata_session.query(SsoCharacter).filter(SsoCharacter.client == clientHash).options(*eager).all() return characters -@cachedQuery(CrestChar, 1, "lookfor") -def getCrestCharacter(lookfor, eager=None): +@cachedQuery(SsoCharacter, 1, "lookfor", "clientHash") +def getSsoCharacter(lookfor, clientHash, eager=None): + filter = SsoCharacter.client == clientHash + if isinstance(lookfor, int): - if eager is None: - with sd_lock: - character = saveddata_session.query(CrestChar).get(lookfor) - else: - eager = processEager(eager) - with sd_lock: - character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.ID == lookfor).first() - elif isinstance(lookfor, basestring): - eager = processEager(eager) - with sd_lock: - character = saveddata_session.query(CrestChar).options(*eager).filter(CrestChar.name == lookfor).first() + filter = and_(filter, SsoCharacter.ID == lookfor) + elif isinstance(lookfor, str): + filter = and_(filter, SsoCharacter.characterName == lookfor) else: raise TypeError("Need integer or string as argument") + + eager = processEager(eager) + with sd_lock: + character = saveddata_session.query(SsoCharacter).options(*eager).filter(filter).first() + return character @@ -515,8 +514,8 @@ def removeInvalid(fits): invalids = [f for f in fits if f.isInvalid] if invalids: - map(fits.remove, invalids) - map(saveddata_session.delete, invalids) + list(map(fits.remove, invalids)) + list(map(saveddata_session.delete, invalids)) saveddata_session.commit() return fits @@ -544,7 +543,7 @@ def commit(): try: saveddata_session.commit() saveddata_session.flush() - except Exception: + except Exception as ex: saveddata_session.rollback() exc_info = sys.exc_info() - raise exc_info[0], exc_info[1], exc_info[2] + raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) diff --git a/eos/db/util.py b/eos/db/util.py index 03a09aceb..7fcf3504d 100644 --- a/eos/db/util.py +++ b/eos/db/util.py @@ -39,7 +39,7 @@ def processEager(eager): return tuple() else: l = [] - if isinstance(eager, basestring): + if isinstance(eager, str): eager = (eager,) for e in eager: @@ -50,7 +50,7 @@ def processEager(eager): def _replacements(eagerString): splitEager = eagerString.split(".") - for i in xrange(len(splitEager)): + for i in range(len(splitEager)): part = splitEager[i] replacement = replace.get(part) if replacement: diff --git a/eos/effectHandlerHelpers.py b/eos/effectHandlerHelpers.py index 0b0b5e795..f890ae0ed 100644 --- a/eos/effectHandlerHelpers.py +++ b/eos/effectHandlerHelpers.py @@ -115,7 +115,7 @@ class HandledList(list): class HandledModuleList(HandledList): def append(self, mod): emptyPosition = float("Inf") - for i in xrange(len(self)): + for i in range(len(self)): currMod = self[i] if currMod.isEmpty and not mod.isEmpty and currMod.slot == mod.slot: currPos = mod.position or i @@ -149,7 +149,7 @@ class HandledModuleList(HandledList): oldPos = mod.position mod.position = None - for i in xrange(oldPos, len(self)): + for i in range(oldPos, len(self)): self[i].position -= 1 def toDummy(self, index): @@ -205,6 +205,16 @@ class HandledImplantBoosterList(HandledList): HandledList.append(self, thing) +class HandledSsoCharacterList(list): + def append(self, character): + old = next((x for x in self if x.client == character.client), None) + if old is not None: + pyfalog.warning("Removing SSO Character with same hash: {}".format(repr(old))) + list.remove(self, old) + + list.append(self, character) + + class HandledProjectedModList(HandledList): def append(self, proj): if proj.isInvalid: diff --git a/eos/effects/capacitorcapacitymultiply.py b/eos/effects/capacitorcapacitymultiply.py index 4b277a8da..8b5df217d 100644 --- a/eos/effects/capacitorcapacitymultiply.py +++ b/eos/effects/capacitorcapacitymultiply.py @@ -10,4 +10,6 @@ type = "passive" def handler(fit, module, context): - fit.ship.multiplyItemAttr("capacitorCapacity", module.getModifiedItemAttr("capacitorCapacityMultiplier")) + # We default this to None as there are times when the source attribute doesn't exist (for example, Cap Power Relay). + # It will return 0 as it doesn't exist, which would nullify whatever the target attribute is + fit.ship.multiplyItemAttr("capacitorCapacity", module.getModifiedItemAttr("capacitorCapacityMultiplier", None)) diff --git a/eos/effects/chargebonuswarfarecharge.py b/eos/effects/chargebonuswarfarecharge.py index 600379c44..b0998fbb4 100644 --- a/eos/effects/chargebonuswarfarecharge.py +++ b/eos/effects/chargebonuswarfarecharge.py @@ -6,6 +6,6 @@ type = "active" def handler(fit, module, context): - for x in xrange(1, 4): + for x in range(1, 4): value = module.getModifiedChargeAttr("warfareBuff{}Multiplier".format(x)) module.multiplyItemAttr("warfareBuff{}Value".format(x), value) diff --git a/eos/effects/modifyboostereffectchancewithboosterchancebonuspostpercent.py b/eos/effects/modifyboostereffectchancewithboosterchancebonuspostpercent.py index 6f123d02c..604e3e14b 100644 --- a/eos/effects/modifyboostereffectchancewithboosterchancebonuspostpercent.py +++ b/eos/effects/modifyboostereffectchancewithboosterchancebonuspostpercent.py @@ -8,7 +8,7 @@ type = "passive" def handler(fit, container, context): level = container.level if "skill" in context else 1 - for i in xrange(5): + for i in range(5): attr = "boosterEffectChance{0}".format(i + 1) fit.boosters.filteredItemBoost(lambda booster: attr in booster.itemModifiedAttributes, attr, container.getModifiedItemAttr("boosterChanceBonus") * level) diff --git a/eos/effects/modulebonuswarfarelinkarmor.py b/eos/effects/modulebonuswarfarelinkarmor.py index 2c792da83..be5843988 100644 --- a/eos/effects/modulebonuswarfarelinkarmor.py +++ b/eos/effects/modulebonuswarfarelinkarmor.py @@ -16,7 +16,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkinfo.py b/eos/effects/modulebonuswarfarelinkinfo.py index 491d824ad..dd1e1111b 100644 --- a/eos/effects/modulebonuswarfarelinkinfo.py +++ b/eos/effects/modulebonuswarfarelinkinfo.py @@ -7,7 +7,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkmining.py b/eos/effects/modulebonuswarfarelinkmining.py index fa28fcf4d..55876bc34 100644 --- a/eos/effects/modulebonuswarfarelinkmining.py +++ b/eos/effects/modulebonuswarfarelinkmining.py @@ -7,7 +7,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkshield.py b/eos/effects/modulebonuswarfarelinkshield.py index f8d3130e0..45cd0ac6f 100644 --- a/eos/effects/modulebonuswarfarelinkshield.py +++ b/eos/effects/modulebonuswarfarelinkshield.py @@ -7,7 +7,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/modulebonuswarfarelinkskirmish.py b/eos/effects/modulebonuswarfarelinkskirmish.py index 84b0cea3e..fc400c791 100644 --- a/eos/effects/modulebonuswarfarelinkskirmish.py +++ b/eos/effects/modulebonuswarfarelinkskirmish.py @@ -7,7 +7,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedChargeAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedChargeAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/moduletitaneffectgenerator.py b/eos/effects/moduletitaneffectgenerator.py index 901a0e89e..741de5ba4 100644 --- a/eos/effects/moduletitaneffectgenerator.py +++ b/eos/effects/moduletitaneffectgenerator.py @@ -6,7 +6,7 @@ type = "active", "gang" def handler(fit, module, context, **kwargs): - for x in xrange(1, 5): + for x in range(1, 5): if module.getModifiedItemAttr("warfareBuff{}ID".format(x)): value = module.getModifiedItemAttr("warfareBuff{}Value".format(x)) id = module.getModifiedItemAttr("warfareBuff{}ID".format(x)) diff --git a/eos/effects/poweroutputmultiply.py b/eos/effects/poweroutputmultiply.py index 3a0e5c6a1..f4d5eb600 100644 --- a/eos/effects/poweroutputmultiply.py +++ b/eos/effects/poweroutputmultiply.py @@ -9,4 +9,6 @@ type = "passive" def handler(fit, module, context): - fit.ship.multiplyItemAttr("powerOutput", module.getModifiedItemAttr("powerOutputMultiplier")) + # We default this to None as there are times when the source attribute doesn't exist (for example, Cap Power Relay). + # It will return 0 as it doesn't exist, which would nullify whatever the target attribute is + fit.ship.multiplyItemAttr("powerOutput", module.getModifiedItemAttr("powerOutputMultiplier", None)) diff --git a/eos/effects/shieldcapacitymultiply.py b/eos/effects/shieldcapacitymultiply.py index 71c91f19e..3c9aeea6d 100644 --- a/eos/effects/shieldcapacitymultiply.py +++ b/eos/effects/shieldcapacitymultiply.py @@ -9,4 +9,6 @@ type = "passive" def handler(fit, module, context): - fit.ship.multiplyItemAttr("shieldCapacity", module.getModifiedItemAttr("shieldCapacityMultiplier")) + # We default this to None as there are times when the source attribute doesn't exist (for example, Cap Power Relay). + # It will return 0 as it doesn't exist, which would nullify whatever the target attribute is + fit.ship.multiplyItemAttr("shieldCapacity", module.getModifiedItemAttr("shieldCapacityMultiplier", None)) diff --git a/eos/effects/techtwocommandburstbonus.py b/eos/effects/techtwocommandburstbonus.py index 757513cef..1769da1af 100644 --- a/eos/effects/techtwocommandburstbonus.py +++ b/eos/effects/techtwocommandburstbonus.py @@ -4,5 +4,5 @@ runTime = "late" def handler(fit, module, context): - for x in xrange(1, 4): + for x in range(1, 4): module.boostChargeAttr("warfareBuff{}Multiplier".format(x), module.getModifiedItemAttr("commandBurstStrengthBonus")) diff --git a/eos/effects/warpdisruptsphere.py b/eos/effects/warpdisruptsphere.py index f8ce29992..22787465b 100644 --- a/eos/effects/warpdisruptsphere.py +++ b/eos/effects/warpdisruptsphere.py @@ -1,7 +1,3 @@ -# warpDisruptSphere -# -# Used by: -# Modules from group: Warp Disrupt Field Generator (7 of 7) # warpDisruptSphere # diff --git a/eos/effects/warpscrambleblockmwdwithnpceffect.py b/eos/effects/warpscrambleblockmwdwithnpceffect.py index f8241436d..89c97117e 100644 --- a/eos/effects/warpscrambleblockmwdwithnpceffect.py +++ b/eos/effects/warpscrambleblockmwdwithnpceffect.py @@ -16,7 +16,10 @@ def handler(fit, module, context): # this is such a dirty hack for mod in fit.modules: - if not mod.isEmpty and mod.item.requiresSkill("High Speed Maneuvering") and mod.state > State.ONLINE: + if not mod.isEmpty and mod.state > State.ONLINE and ( + mod.item.requiresSkill("Micro Jump Drive Operation") + or mod.item.requiresSkill("High Speed Maneuvering") + ): mod.state = State.ONLINE if not mod.isEmpty and mod.item.requiresSkill("Micro Jump Drive Operation") and mod.state > State.ONLINE: mod.state = State.ONLINE diff --git a/eos/events.py b/eos/events.py index de8bdfadb..dd4b07b79 100644 --- a/eos/events.py +++ b/eos/events.py @@ -58,7 +58,7 @@ def rel_listener(target, value, initiator): if not target or (isinstance(value, Module) and value.isEmpty): return - print "{} has had a relationship change :D".format(target) + print("{} has had a relationship change :D".format(target)) target.modified = datetime.datetime.now() diff --git a/eos/gamedata.py b/eos/gamedata.py index a960e47e2..af7d413a7 100644 --- a/eos/gamedata.py +++ b/eos/gamedata.py @@ -22,13 +22,10 @@ import re from sqlalchemy.orm import reconstructor import eos.db -from eqBase import EqBase +from .eqBase import EqBase from eos.saveddata.price import Price as types_Price +from collections import OrderedDict -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict from logbook import Logger @@ -160,8 +157,6 @@ class Effect(EqBase): if it doesn't, set dummy values and add a dummy handler """ - pyfalog.debug("Generate effect handler for {}".format(self.name)) - try: self.__effectModule = effectModule = __import__('eos.effects.' + self.handlerName, fromlist=True) self.__handler = getattr(effectModule, "handler", effectDummy) @@ -258,7 +253,7 @@ class Item(EqBase): return default def isType(self, type): - for effect in self.effects.itervalues(): + for effect in self.effects.values(): if effect.isType(type): return True @@ -299,7 +294,7 @@ class Item(EqBase): self.__requiredSkills = requiredSkills # Map containing attribute IDs we may need for required skills # { requiredSkillX : requiredSkillXLevel } - combinedAttrIDs = set(self.srqIDMap.iterkeys()).union(set(self.srqIDMap.itervalues())) + combinedAttrIDs = set(self.srqIDMap.keys()).union(set(self.srqIDMap.values())) # Map containing result of the request # { attributeID : attributeValue } skillAttrs = {} @@ -309,7 +304,7 @@ class Item(EqBase): attrVal = attrInfo[2] skillAttrs[attrID] = attrVal # Go through all attributeID pairs - for srqIDAtrr, srqLvlAttr in self.srqIDMap.iteritems(): + for srqIDAtrr, srqLvlAttr in self.srqIDMap.items(): # Check if we have both in returned result if srqIDAtrr in skillAttrs and srqLvlAttr in skillAttrs: skillID = int(skillAttrs[srqIDAtrr]) @@ -383,7 +378,7 @@ class Item(EqBase): race = None # Check primary and secondary required skills' races if race is None: - skillRaces = tuple(filter(lambda rid: rid, (s.raceID for s in tuple(self.requiredSkills.keys())))) + skillRaces = tuple([rid for rid in (s.raceID for s in tuple(self.requiredSkills.keys())) if rid]) if sum(skillRaces) in map: race = map[sum(skillRaces)] if race == "angelserp": @@ -405,7 +400,7 @@ class Item(EqBase): if self.__assistive is None: assistive = False # Go through all effects and find first assistive - for effect in self.effects.itervalues(): + for effect in self.effects.values(): if effect.isAssistance is True: # If we find one, stop and mark item as assistive assistive = True @@ -420,7 +415,7 @@ class Item(EqBase): if self.__offensive is None: offensive = False # Go through all effects and find first offensive - for effect in self.effects.itervalues(): + for effect in self.effects.values(): if effect.isOffensive is True: # If we find one, stop and mark item as offensive offensive = True @@ -429,8 +424,8 @@ class Item(EqBase): return self.__offensive def requiresSkill(self, skill, level=None): - for s, l in self.requiredSkills.iteritems(): - if isinstance(skill, basestring): + for s, l in self.requiredSkills.items(): + if isinstance(skill, str): if s.name == skill and (level is None or l == level): return True @@ -468,7 +463,7 @@ class Item(EqBase): return self.__price def __repr__(self): - return u"Item(ID={}, name={}) at {}".format( + return "Item(ID={}, name={}) at {}".format( self.ID, self.name, hex(id(self)) ) @@ -522,9 +517,9 @@ class Icon(EqBase): class MarketGroup(EqBase): def __repr__(self): - return u"MarketGroup(ID={}, name={}, parent={}) at {}".format( + return "MarketGroup(ID={}, name={}, parent={}) at {}".format( self.ID, self.name, getattr(self.parent, "name", None), self.name, hex(id(self)) - ).encode('utf8') + ) class MetaGroup(EqBase): diff --git a/eos/graph/__init__.py b/eos/graph/__init__.py index 44802171f..fe8e8d25d 100644 --- a/eos/graph/__init__.py +++ b/eos/graph/__init__.py @@ -25,7 +25,7 @@ class Graph(object): self.fit = fit self.data = {} if data is not None: - for name, d in data.iteritems(): + for name, d in data.items(): self.setData(Data(name, d)) self.function = function @@ -39,7 +39,7 @@ class Graph(object): def getIterator(self): pointNames = [] pointIterators = [] - for data in self.data.itervalues(): + for data in self.data.values(): pointNames.append(data.name) pointIterators.append(data) @@ -48,7 +48,7 @@ class Graph(object): def _iterator(self, pointNames, pointIterators): for pointValues in itertools.product(*pointIterators): point = {} - for i in xrange(len(pointValues)): + for i in range(len(pointValues)): point[pointNames[i]] = pointValues[i] yield point, self.function(point) @@ -61,12 +61,12 @@ class Data(object): self.data = self.parseString(dataString) def parseString(self, dataString): - if not isinstance(dataString, basestring): + if not isinstance(dataString, str): return Constant(dataString), dataList = [] for data in dataString.split(";"): - if isinstance(data, basestring) and "-" in data: + if isinstance(data, str) and "-" in data: # Dealing with a range dataList.append(Range(data, self.step)) else: @@ -85,7 +85,7 @@ class Data(object): class Constant(object): def __init__(self, const): - if isinstance(const, basestring): + if isinstance(const, str): self.value = None if const == "" else float(const) else: self.value = const diff --git a/eos/graph/fitDps.py b/eos/graph/fitDps.py index 12cfda8d1..6aead3583 100644 --- a/eos/graph/fitDps.py +++ b/eos/graph/fitDps.py @@ -63,10 +63,10 @@ class FitDpsGraph(Graph): ew['signatureRadius'].sort(key=abssort) ew['velocity'].sort(key=abssort) - for attr, values in ew.iteritems(): + for attr, values in ew.items(): val = data[attr] try: - for i in xrange(len(values)): + for i in range(len(values)): bonus = values[i] val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) data[attr] = val @@ -81,7 +81,7 @@ class FitDpsGraph(Graph): total += dps * self.calculateTurretMultiplier(mod, data) elif mod.hardpoint == Hardpoint.MISSILE: - if mod.state >= State.ACTIVE and mod.maxRange >= distance: + if mod.state >= State.ACTIVE and mod.maxRange is not None and mod.maxRange >= distance: total += dps * self.calculateMissileMultiplier(mod, data) if distance <= fit.extraAttributes["droneControlRange"]: diff --git a/eos/modifiedAttributeDict.py b/eos/modifiedAttributeDict.py index 4106b306b..b4c73c958 100644 --- a/eos/modifiedAttributeDict.py +++ b/eos/modifiedAttributeDict.py @@ -28,23 +28,17 @@ cappingAttrKeyCache = {} class ItemAttrShortcut(object): - def getModifiedItemAttr(self, key, default=None): + def getModifiedItemAttr(self, key, default=0): return_value = self.itemModifiedAttributes.get(key) - if return_value is None and default is not None: - return_value = default - - return return_value + return return_value or default class ChargeAttrShortcut(object): - def getModifiedChargeAttr(self, key, default=None): + def getModifiedChargeAttr(self, key, default=0): return_value = self.chargeModifiedAttributes.get(key) - if return_value is None and default is not None: - return_value = default - - return return_value + return return_value or default class ModifiedAttributeDict(collections.MutableMapping): @@ -165,9 +159,9 @@ class ModifiedAttributeDict(collections.MutableMapping): def __len__(self): keys = set() - keys.update(self.original.iterkeys()) - keys.update(self.__modified.iterkeys()) - keys.update(self.__intermediary.iterkeys()) + keys.update(iter(self.original.keys())) + keys.update(iter(self.__modified.keys())) + keys.update(iter(self.__intermediary.keys())) return len(keys) def __calculateValue(self, key): @@ -231,11 +225,11 @@ class ModifiedAttributeDict(collections.MutableMapping): val *= multiplier # Each group is penalized independently # Things in different groups will not be stack penalized between each other - for penalizedMultipliers in penalizedMultiplierGroups.itervalues(): + for penalizedMultipliers in penalizedMultiplierGroups.values(): # A quick explanation of how this works: # 1: Bonuses and penalties are calculated seperately, so we'll have to filter each of them - l1 = filter(lambda _val: _val > 1, penalizedMultipliers) - l2 = filter(lambda _val: _val < 1, penalizedMultipliers) + l1 = [_val for _val in penalizedMultipliers if _val > 1] + l2 = [_val for _val in penalizedMultipliers if _val < 1] # 2: The most significant bonuses take the smallest penalty, # This means we'll have to sort abssort = lambda _val: -abs(_val - 1) @@ -245,7 +239,7 @@ class ModifiedAttributeDict(collections.MutableMapping): # Any module after the first takes penalties according to: # 1 + (multiplier - 1) * math.exp(- math.pow(i, 2) / 7.1289) for l in (l1, l2): - for i in xrange(len(l)): + for i in range(len(l)): bonus = l[i] val *= 1 + (bonus - 1) * exp(- i ** 2 / 7.1289) val += postIncrease @@ -388,7 +382,7 @@ class ModifiedAttributeDict(collections.MutableMapping): """Force value to attribute and prohibit any changes to it""" self.__forced[attributeName] = value self.__placehold(attributeName) - self.__afflict(attributeName, u"\u2263", value) + self.__afflict(attributeName, "\u2263", value) @staticmethod def getResistance(fit, effect): diff --git a/eos/saveddata/booster.py b/eos/saveddata/booster.py index 413661e86..655a3d3f1 100644 --- a/eos/saveddata/booster.py +++ b/eos/saveddata/booster.py @@ -116,7 +116,7 @@ class Booster(HandledItem, ItemAttrShortcut): if not self.active: return - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ (effect.isType("passive") or effect.isType("boosterSideEffect")): if effect.isType("boosterSideEffect") and effect not in self.activeSideEffectEffects: diff --git a/eos/saveddata/boosterSideEffect.py b/eos/saveddata/boosterSideEffect.py index 3fd39ac4e..ad0814928 100644 --- a/eos/saveddata/boosterSideEffect.py +++ b/eos/saveddata/boosterSideEffect.py @@ -39,7 +39,7 @@ class BoosterSideEffect(object): self.__effect = None if self.effectID: - self.__effect = next((x for x in self.booster.item.effects.itervalues() if x.ID == self.effectID), None) + self.__effect = next((x for x in self.booster.item.effects.values() if x.ID == self.effectID), None) if self.__effect is None: pyfalog.error("Effect (id: {0}) does not exist", self.effectID) return diff --git a/eos/saveddata/cargo.py b/eos/saveddata/cargo.py index 7b6e71349..11c017370 100644 --- a/eos/saveddata/cargo.py +++ b/eos/saveddata/cargo.py @@ -77,8 +77,8 @@ class Cargo(HandledItem, ItemAttrShortcut): "amount": lambda _val: isinstance(_val, int) } - if key == "amount" and val > sys.maxint: - val = sys.maxint + if key == "amount" and val > sys.maxsize: + val = sys.maxsize if not map[key](val): raise ValueError(str(val) + " is not a valid value for " + key) diff --git a/eos/saveddata/character.py b/eos/saveddata/character.py index 3c0e629d1..05ff456fa 100644 --- a/eos/saveddata/character.py +++ b/eos/saveddata/character.py @@ -119,16 +119,9 @@ class Character(object): return all0 - def apiUpdateCharSheet(self, skills, secStatus=0): - for skillRow in skills: - try: - skill = self.getSkill(int(skillRow["typeID"])) - skill.setLevel(int(skillRow["level"]), persist=True, ignoreRestrict=True) - except: - # if setting a skill doesn't work, it's not the end of the world, just quietly pass - pass - - self.secStatus = secStatus + def clearSkills(self): + del self.__skills[:] + self.__skillIdMap.clear() @property def ro(self): @@ -162,7 +155,7 @@ class Character(object): name += " *" if self.alphaCloneID: - name += u' (\u03B1)' + name += ' (\u03B1)' return name @@ -170,6 +163,18 @@ class Character(object): def name(self, name): self.savedName = name + def setSsoCharacter(self, character, clientHash): + if character is not None: + self.__ssoCharacters.append(character) + else: + for x in self.__ssoCharacters: + if x.client == clientHash: + self.__ssoCharacters.remove(x) + + + def getSsoCharacter(self, clientHash): + return next((x for x in self.__ssoCharacters if x.client == clientHash), None) + @property def alphaCloneID(self): return self.__alphaCloneID @@ -199,7 +204,7 @@ class Character(object): del self.__skillIdMap[skill.itemID] def getSkill(self, item): - if isinstance(item, basestring): + if isinstance(item, str): item = self.getSkillNameMap()[item] elif isinstance(item, int): item = self.getSkillIDMap()[item] @@ -282,7 +287,7 @@ class Character(object): map = { "ID" : lambda _val: isinstance(_val, int), "name" : lambda _val: True, - "apiKey" : lambda _val: _val is None or (isinstance(_val, basestring) and len(_val) > 0), + "apiKey" : lambda _val: _val is None or (isinstance(_val, str) and len(_val) > 0), "ownerID": lambda _val: isinstance(_val, int) or _val is None } @@ -344,13 +349,13 @@ class Skill(HandledItem): elif self.character.name == "All 0": self.activeLevel = self.__level = 0 elif self.character.alphaClone: - return min(self.activeLevel, self.character.alphaClone.getSkillLevel(self)) or 0 + return min(self.activeLevel or 0, self.character.alphaClone.getSkillLevel(self) or 0) return self.activeLevel or 0 def setLevel(self, level, persist=False, ignoreRestrict=False): - if (level < 0 or level > 5) and level is not None: + if level is not None and (level < 0 or level > 5): raise ValueError(str(level) + " is not a valid value for level") if hasattr(self, "_Skill__ro") and self.__ro is True: @@ -362,9 +367,9 @@ class Skill(HandledItem): # which affects performance. Should have a checkSkillLevels() or something that is more efficient for bulk. if not ignoreRestrict and eos.config.settings['strictSkillLevels']: start = time.time() - for item, rlevel in self.item.requiredFor.iteritems(): + for item, rlevel in self.item.requiredFor.items(): if item.group.category.ID == 16: # Skill category - if level < rlevel: + if level is None or level < rlevel: skill = self.character.getSkill(item.ID) # print "Removing skill: {}, Dependant level: {}, Required level: {}".format(skill, level, rlevel) skill.setLevel(None, persist) @@ -392,7 +397,7 @@ class Skill(HandledItem): if key in self.item.attributes: return self.item.attributes[key].value else: - return None + return 0 def calculateModifiedAttributes(self, fit, runTime): if self.__suppressed: # or not self.learned - removed for GH issue 101 @@ -402,7 +407,7 @@ class Skill(HandledItem): if item is None: return - for effect in item.effects.itervalues(): + for effect in item.effects.values(): if effect.runTime == runTime and \ effect.isType("passive") and \ (not fit.isStructure or effect.isType("structure")) and \ diff --git a/eos/saveddata/drone.py b/eos/saveddata/drone.py index 2ce1b52ab..da4c512f2 100644 --- a/eos/saveddata/drone.py +++ b/eos/saveddata/drone.py @@ -73,7 +73,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): self.__itemModifiedAttributes.overrides = self.__item.overrides self.__chargeModifiedAttributes = ModifiedAttributeDict() - chargeID = self.getModifiedItemAttr("entityMissileTypeID") + # pheonix todo: check the attribute itself, not the modified. this will always return 0 now. + chargeID = self.getModifiedItemAttr("entityMissileTypeID", None) if chargeID is not None: charge = eos.db.getItem(int(chargeID)) self.__charge = charge @@ -102,7 +103,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def cycleTime(self): - return max(self.getModifiedItemAttr("duration"), 0) + return max(self.getModifiedItemAttr("duration", 0), 0) @property def dealsDamage(self): @@ -138,8 +139,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): cycleTime = self.getModifiedItemAttr(attr) volley = sum( - map(lambda d: (getter("%sDamage" % d) or 0) * (1 - getattr(targetResists, "%sAmount" % d, 0)), - self.DAMAGE_TYPES)) + [(getter("%sDamage" % d) or 0) * (1 - getattr(targetResists, "%sAmount" % d, 0)) for d in self.DAMAGE_TYPES]) volley *= self.amountActive volley *= self.getModifiedItemAttr("damageMultiplier") or 1 self.__volley = volley @@ -155,7 +155,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): getter = self.getModifiedItemAttr cycleTime = self.getModifiedItemAttr(attr) - volley = sum(map(lambda d: getter(d), self.MINING_ATTRIBUTES)) * self.amountActive + volley = sum([getter(d) for d in self.MINING_ATTRIBUTES]) * self.amountActive self.__miningyield = volley / (cycleTime / 1000.0) else: self.__miningyield = 0 @@ -168,7 +168,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): "energyDestabilizationRange", "empFieldRange", "ecmBurstRange", "maxRange") for attr in attrs: - maxRange = self.getModifiedItemAttr(attr) + maxRange = self.getModifiedItemAttr(attr, None) if maxRange is not None: return maxRange if self.charge is not None: @@ -184,7 +184,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def falloff(self): attrs = ("falloff", "falloffEffectiveness") for attr in attrs: - falloff = self.getModifiedItemAttr(attr) + falloff = self.getModifiedItemAttr(attr, None) if falloff is not None: return falloff @@ -236,7 +236,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): context = ("drone",) projected = False - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ effect.activeByDefault and \ ((projected is True and effect.isType("projected")) or @@ -251,7 +251,7 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): i += 1 if self.charge: - for effect in self.charge.effects.itervalues(): + for effect in self.charge.effects.values(): if effect.runTime == runTime and effect.activeByDefault: effect.handler(fit, self, ("droneCharge",)) @@ -263,8 +263,8 @@ class Drone(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def fits(self, fit): fitDroneGroupLimits = set() - for i in xrange(1, 3): - groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i) + for i in range(1, 3): + groneGrp = fit.ship.getModifiedItemAttr("allowedDroneGroup%d" % i, None) if groneGrp is not None: fitDroneGroupLimits.add(int(groneGrp)) if len(fitDroneGroupLimits) == 0: diff --git a/eos/saveddata/fighter.py b/eos/saveddata/fighter.py index d2b1b45b7..180838e31 100644 --- a/eos/saveddata/fighter.py +++ b/eos/saveddata/fighter.py @@ -98,7 +98,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def __getAbilities(self): """Returns list of FighterAbilities that are loaded with data""" - return [FighterAbility(effect) for effect in self.item.effects.values()] + return [FighterAbility(effect) for effect in list(self.item.effects.values())] def __calculateSlot(self, item): types = { @@ -110,7 +110,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): "StandupHeavy": Slot.FS_HEAVY } - for t, slot in types.iteritems(): + for t, slot in types.items(): if self.getModifiedItemAttr("fighterSquadronIs{}".format(t)): return slot @@ -202,12 +202,12 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): "energyDestabilizationRange", "empFieldRange", "ecmBurstRange", "maxRange") for attr in attrs: - maxRange = self.getModifiedItemAttr(attr) + maxRange = self.getModifiedItemAttr(attr, None) if maxRange is not None: return maxRange if self.charge is not None: - delay = self.getModifiedChargeAttr("explosionDelay") - speed = self.getModifiedChargeAttr("maxVelocity") + delay = self.getModifiedChargeAttr("explosionDelay", None) + speed = self.getModifiedChargeAttr("maxVelocity", None) if delay is not None and speed is not None: return delay / 1000.0 * speed @@ -218,7 +218,7 @@ class Fighter(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def falloff(self): attrs = ("falloff", "falloffEffectiveness") for attr in attrs: - falloff = self.getModifiedItemAttr(attr) + falloff = self.getModifiedItemAttr(attr, None) if falloff is not None: return falloff diff --git a/eos/saveddata/fighterAbility.py b/eos/saveddata/fighterAbility.py index b6b099a5c..8c32be23d 100644 --- a/eos/saveddata/fighterAbility.py +++ b/eos/saveddata/fighterAbility.py @@ -57,7 +57,7 @@ class FighterAbility(object): self.__effect = None if self.effectID: - self.__effect = next((x for x in self.fighter.item.effects.itervalues() if x.ID == self.effectID), None) + self.__effect = next((x for x in self.fighter.item.effects.values() if x.ID == self.effectID), None) if self.__effect is None: pyfalog.error("Effect (id: {0}) does not exist", self.effectID) return @@ -127,8 +127,8 @@ class FighterAbility(object): if self.attrPrefix == "fighterAbilityLaunchBomb": # bomb calcs - volley = sum(map(lambda attr: (self.fighter.getModifiedChargeAttr("%sDamage" % attr) or 0) * ( - 1 - getattr(targetResists, "%sAmount" % attr, 0)), self.DAMAGE_TYPES)) + volley = sum([(self.fighter.getModifiedChargeAttr("%sDamage" % attr) or 0) * ( + 1 - getattr(targetResists, "%sAmount" % attr, 0)) for attr in self.DAMAGE_TYPES]) else: volley = sum(map(lambda d2, d: (self.fighter.getModifiedItemAttr( diff --git a/eos/saveddata/fit.py b/eos/saveddata/fit.py index 1abace648..cb5bedcf9 100644 --- a/eos/saveddata/fit.py +++ b/eos/saveddata/fit.py @@ -258,11 +258,11 @@ class Fit(object): def projectedFits(self): # only in extreme edge cases will the fit be invalid, but to be sure do # not return them. - return [fit for fit in self.__projectedFits.values() if not fit.isInvalid] + return [fit for fit in list(self.__projectedFits.values()) if not fit.isInvalid] @property def commandFits(self): - return [fit for fit in self.__commandFits.values() if not fit.isInvalid] + return [fit for fit in list(self.__commandFits.values()) if not fit.isInvalid] def getProjectionInfo(self, fitID): return self.projectedOnto.get(fitID, None) @@ -492,7 +492,7 @@ class Fit(object): def __runCommandBoosts(self, runTime="normal"): pyfalog.debug("Applying gang boosts for {0}", repr(self)) - for warfareBuffID in self.commandBonuses.keys(): + for warfareBuffID in list(self.commandBonuses.keys()): # Unpack all data required to run effect properly effect_runTime, value, thing, effect = self.commandBonuses[warfareBuffID] @@ -676,7 +676,7 @@ class Fit(object): def __resetDependentCalcs(self): self.calculated = False - for value in self.projectedOnto.values(): + for value in list(self.projectedOnto.values()): if value.victim_fit: # removing a self-projected fit causes victim fit to be None. @todo: look into why. :3 value.victim_fit.calculated = False @@ -707,14 +707,14 @@ class Fit(object): self.__resetDependentCalcs() # For fits that are under local's Command, we do the same thing - for value in self.boostedOnto.values(): + for value in list(self.boostedOnto.values()): # apparently this is a thing that happens when removing a command fit from a fit and then switching to # that command fit. Same as projected clears, figure out why. if value.boosted_fit: value.boosted_fit.__resetDependentCalcs() if targetFit and type == CalcType.PROJECTED: - pyfalog.debug(u"Calculating projections from {0} to target {1}", repr(self), repr(targetFit)) + pyfalog.debug("Calculating projections from {0} to target {1}", repr(self), repr(targetFit)) projectionInfo = self.getProjectionInfo(targetFit.ID) # Start applying any command fits that we may have. @@ -756,7 +756,7 @@ class Fit(object): # Loop through our run times here. These determine which effects are run in which order. for runTime in ("early", "normal", "late"): - pyfalog.debug("Run time: {0}", runTime) + # pyfalog.debug("Run time: {0}", runTime) # Items that are unrestricted. These items are run on the local fit # first and then projected onto the target fit it one is designated u = [ @@ -795,7 +795,7 @@ class Fit(object): # targetFit.register(item, origin=self) item.calculateModifiedAttributes(targetFit, runTime, False, True) - pyfalog.debug("Command Bonuses: {}".format(self.commandBonuses)) + # pyfalog.debug("Command Bonuses: {}".format(self.commandBonuses)) # If we are calculating our local or projected fit and have command bonuses, apply them if type != CalcType.COMMAND and self.commandBonuses: @@ -838,7 +838,7 @@ class Fit(object): for item in c: if item is not None: # apply effects onto target fit x amount of times - for _ in xrange(projectionInfo.amount): + for _ in range(projectionInfo.amount): targetFit.register(item, origin=self) item.calculateModifiedAttributes(targetFit, runTime, True) @@ -854,7 +854,7 @@ class Fit(object): for slotType in (Slot.LOW, Slot.MED, Slot.HIGH, Slot.RIG, Slot.SUBSYSTEM, Slot.SERVICE): amount = self.getSlotsFree(slotType, True) if amount > 0: - for _ in xrange(int(amount)): + for _ in range(int(amount)): self.modules.append(Module.buildEmpty(slotType)) if amount < 0: @@ -870,7 +870,7 @@ class Fit(object): self.modules.remove(mod) def unfill(self): - for i in xrange(len(self.modules) - 1, -1, -1): + for i in range(len(self.modules) - 1, -1, -1): mod = self.modules[i] if mod.isEmpty: del self.modules[i] @@ -878,7 +878,7 @@ class Fit(object): @property def modCount(self): x = 0 - for i in xrange(len(self.modules) - 1, -1, -1): + for i in range(len(self.modules) - 1, -1, -1): mod = self.modules[i] if not mod.isEmpty: x += 1 @@ -1531,11 +1531,11 @@ class Fit(object): return copy_ship def __repr__(self): - return u"Fit(ID={}, ship={}, name={}) at {}".format( + return "Fit(ID={}, ship={}, name={}) at {}".format( self.ID, self.ship.item.name, self.name, hex(id(self)) - ).encode('utf8') + ) def __str__(self): - return u"{} ({})".format( + return "{} ({})".format( self.name, self.ship.item.name - ).encode('utf8') + ) diff --git a/eos/saveddata/implant.py b/eos/saveddata/implant.py index 869e5c608..d61b9c43b 100644 --- a/eos/saveddata/implant.py +++ b/eos/saveddata/implant.py @@ -93,7 +93,7 @@ class Implant(HandledItem, ItemAttrShortcut): return if not self.active: return - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and effect.isType("passive") and effect.activeByDefault: effect.handler(fit, self, ("implant",)) diff --git a/eos/saveddata/mode.py b/eos/saveddata/mode.py index 50413906d..f0d950549 100644 --- a/eos/saveddata/mode.py +++ b/eos/saveddata/mode.py @@ -50,6 +50,6 @@ class Mode(ItemAttrShortcut, HandledItem): def calculateModifiedAttributes(self, fit, runTime, forceProjected=False): if self.item: - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and effect.activeByDefault: effect.handler(fit, self, context=("module",)) diff --git a/eos/saveddata/module.py b/eos/saveddata/module.py index ced03eb45..054828dae 100644 --- a/eos/saveddata/module.py +++ b/eos/saveddata/module.py @@ -182,7 +182,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def numShots(self): if self.charge is None: - return None + return 0 if self.__chargeCycles is None and self.charge: numCharges = self.numCharges # Usual ammo like projectiles and missiles @@ -217,7 +217,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): armorRep = self.getModifiedItemAttr("armorDamageAmount") or 0 shieldRep = self.getModifiedItemAttr("shieldBonus") or 0 if not cycles or (not armorRep and not shieldRep): - return None + return 0 hp = round((armorRep + shieldRep) * cycles) return hp @@ -255,7 +255,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): "ecmBurstRange", "warpScrambleRange", "cargoScanRange", "shipScanRange", "surveyScanRange") for attr in attrs: - maxRange = self.getModifiedItemAttr(attr) + maxRange = self.getModifiedItemAttr(attr, None) if maxRange is not None: return maxRange if self.charge is not None: @@ -284,7 +284,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def falloff(self): attrs = ("falloffEffectiveness", "falloff", "shipScanFalloff") for attr in attrs: - falloff = self.getModifiedItemAttr(attr) + falloff = self.getModifiedItemAttr(attr, None) if falloff is not None: return falloff @@ -333,9 +333,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): else: func = self.getModifiedItemAttr - volley = sum(map( - lambda attr: (func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)), - self.DAMAGE_TYPES)) + volley = sum([(func("%sDamage" % attr) or 0) * (1 - getattr(targetResists, "%sAmount" % attr, 0)) for attr in self.DAMAGE_TYPES]) volley *= self.getModifiedItemAttr("damageMultiplier") or 1 if volley: cycleTime = self.cycleTime @@ -422,19 +420,19 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): fitsOnType = set() fitsOnGroup = set() - shipType = self.getModifiedItemAttr("fitsToShipType") + shipType = self.getModifiedItemAttr("fitsToShipType", None) if shipType is not None: fitsOnType.add(shipType) - for attr in self.itemModifiedAttributes.keys(): + for attr in list(self.itemModifiedAttributes.keys()): if attr.startswith("canFitShipType"): - shipType = self.getModifiedItemAttr(attr) + shipType = self.getModifiedItemAttr(attr, None) if shipType is not None: fitsOnType.add(shipType) - for attr in self.itemModifiedAttributes.keys(): + for attr in list(self.itemModifiedAttributes.keys()): if attr.startswith("canFitShipGroup"): - shipGroup = self.getModifiedItemAttr(attr) + shipGroup = self.getModifiedItemAttr(attr, None) if shipGroup is not None: fitsOnGroup.add(shipGroup) @@ -466,7 +464,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return False # Check max group fitted - max = self.getModifiedItemAttr("maxGroupFitted") + max = self.getModifiedItemAttr("maxGroupFitted", None) if max is not None: current = 0 # if self.owner != fit else -1 # Disabled, see #1278 for mod in fit.modules: @@ -479,11 +477,10 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): # Check this only if we're told to do so if hardpointLimit: if self.hardpoint == Hardpoint.TURRET: - if (fit.ship.getModifiedItemAttr('turretSlotsLeft') or 0) - fit.getHardpointsUsed(Hardpoint.TURRET) < 1: + if fit.ship.getModifiedItemAttr('turretSlotsLeft') - fit.getHardpointsUsed(Hardpoint.TURRET) < 1: return False elif self.hardpoint == Hardpoint.MISSILE: - if (fit.ship.getModifiedItemAttr('launcherSlotsLeft') or 0) - fit.getHardpointsUsed( - Hardpoint.MISSILE) < 1: + if fit.ship.getModifiedItemAttr('launcherSlotsLeft') - fit.getHardpointsUsed(Hardpoint.MISSILE) < 1: return False return True @@ -513,7 +510,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): return True # Check if the local module is over it's max limit; if it's not, we're fine - maxGroupActive = self.getModifiedItemAttr("maxGroupActive") + maxGroupActive = self.getModifiedItemAttr("maxGroupActive", None) if maxGroupActive is None and projectedOnto is None: return True @@ -561,7 +558,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): chargeGroup = charge.groupID for i in range(5): - itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) + itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None) if itemChargeGroup is None: continue if itemChargeGroup == chargeGroup: @@ -572,7 +569,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def getValidCharges(self): validCharges = set() for i in range(5): - itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i)) + itemChargeGroup = self.getModifiedItemAttr('chargeGroup' + str(i), None) if itemChargeGroup is not None: g = eos.db.getGroup(int(itemChargeGroup), eager=("items.icon", "items.attributes")) if g is None: @@ -593,7 +590,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if item is None: return Hardpoint.NONE - for effectName, slot in effectHardpointMap.iteritems(): + for effectName, slot in effectHardpointMap.items(): if effectName in item.effects: return slot @@ -611,7 +608,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): } if item is None: return None - for effectName, slot in effectSlotMap.iteritems(): + for effectName, slot in effectSlotMap.items(): if effectName in item.effects: return slot if item.group.name == "Effect Beacon": @@ -665,7 +662,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.charge is not None: # fix for #82 and it's regression #106 if not projected or (self.projected and not forceProjected) or gang: - for effect in self.charge.effects.itervalues(): + for effect in self.charge.effects.values(): if effect.runTime == runTime and \ effect.activeByDefault and \ (effect.isType("offline") or @@ -684,7 +681,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): if self.item: if self.state >= State.OVERHEATED: - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ effect.isType("overheat") \ and not forceProjected \ @@ -692,7 +689,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): and ((gang and effect.isType("gang")) or not gang): effect.handler(fit, self, context) - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ effect.activeByDefault and \ (effect.isType("offline") or @@ -752,13 +749,12 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): @property def rawCycleTime(self): speed = max( - self.getModifiedItemAttr("speed"), # Most weapons - self.getModifiedItemAttr("duration"), # Most average modules - self.getModifiedItemAttr("durationSensorDampeningBurstProjector"), - self.getModifiedItemAttr("durationTargetIlluminationBurstProjector"), - self.getModifiedItemAttr("durationECMJammerBurstProjector"), - self.getModifiedItemAttr("durationWeaponDisruptionBurstProjector"), - 0, # Return 0 if none of the above are valid + self.getModifiedItemAttr("speed", 0), # Most weapons + self.getModifiedItemAttr("duration", 0), # Most average modules + self.getModifiedItemAttr("durationSensorDampeningBurstProjector", 0), + self.getModifiedItemAttr("durationTargetIlluminationBurstProjector", 0), + self.getModifiedItemAttr("durationECMJammerBurstProjector", 0), + self.getModifiedItemAttr("durationWeaponDisruptionBurstProjector", 0) ) return speed @@ -793,7 +789,7 @@ class Module(HandledItem, HandledCharge, ItemAttrShortcut, ChargeAttrShortcut): def __repr__(self): if self.item: - return u"Module(ID={}, name={}) at {}".format( + return "Module(ID={}, name={}) at {}".format( self.item.ID, self.item.name, hex(id(self)) ) else: diff --git a/eos/saveddata/ship.py b/eos/saveddata/ship.py index 90586dbd7..56b3f7c0d 100644 --- a/eos/saveddata/ship.py +++ b/eos/saveddata/ship.py @@ -87,7 +87,7 @@ class Ship(ItemAttrShortcut, HandledItem): def calculateModifiedAttributes(self, fit, runTime, forceProjected=False): if forceProjected: return - for effect in self.item.effects.itervalues(): + for effect in self.item.effects.values(): if effect.runTime == runTime and \ effect.isType("passive") and \ effect.activeByDefault: diff --git a/eos/saveddata/crestchar.py b/eos/saveddata/ssocharacter.py similarity index 50% rename from eos/saveddata/crestchar.py rename to eos/saveddata/ssocharacter.py index ce6ab14fe..9ffed8d46 100644 --- a/eos/saveddata/crestchar.py +++ b/eos/saveddata/ssocharacter.py @@ -18,17 +18,40 @@ # =============================================================================== from sqlalchemy.orm import reconstructor - +import datetime +import time # from tomorrow import threads -class CrestChar(object): - def __init__(self, id, name, refresh_token=None): - self.ID = id - self.name = name - self.refresh_token = refresh_token +class SsoCharacter(object): + def __init__(self, charID, name, client, accessToken=None, refreshToken=None): + self.characterID = charID + self.characterName = name + self.client = client + self.accessToken = accessToken + self.refreshToken = refreshToken + self.accessTokenExpires = None + self.esi_client = None + @reconstructor def init(self): - pass + self.esi_client = None + + def get_sso_data(self): + """ Little "helper" function to get formated data for esipy security + """ + return { + 'access_token': self.accessToken, + 'refresh_token': self.refreshToken, + 'expires_in': ( + self.accessTokenExpires - datetime.datetime.utcnow() + ).total_seconds() + } + + + def __repr__(self): + return "SsoCharacter(ID={}, name={}, client={}) at {}".format( + self.ID, self.characterName, self.client, hex(id(self)) + ) diff --git a/eos/saveddata/user.py b/eos/saveddata/user.py index 39e2eaed2..b623452db 100644 --- a/eos/saveddata/user.py +++ b/eos/saveddata/user.py @@ -33,7 +33,7 @@ class User(object): def encodeAndSetPassword(self, pw): h = hashlib.new("sha256") - salt = "".join([random.choice(string.letters) for _ in xrange(32)]) + salt = "".join([random.choice(string.letters) for _ in range(32)]) h.update(pw) h.update(salt) self.password = ("%s%s" % (h.hexdigest(), salt)) @@ -45,14 +45,14 @@ class User(object): h = hashlib.new("sha256") h.update(pw) h.update(salt) - return self.password == (u"%s%s" % (h.hexdigest(), salt)) + return self.password == ("%s%s" % (h.hexdigest(), salt)) @validates("ID", "username", "password", "admin") def validator(self, key, val): map = { "ID" : lambda _val: isinstance(_val, int), - "username": lambda _val: isinstance(_val, basestring), - "password": lambda _val: isinstance(_val, basestring) and len(_val) == 96, + "username": lambda _val: isinstance(_val, str), + "password": lambda _val: isinstance(_val, str) and len(_val) == 96, "admin" : lambda _val: isinstance(_val, bool) } diff --git a/gui/additionsPane.py b/gui/additionsPane.py index b12fe5036..8bc72f8ec 100644 --- a/gui/additionsPane.py +++ b/gui/additionsPane.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.builtinAdditionPanes.boosterView import BoosterView from gui.builtinAdditionPanes.cargoView import CargoView from gui.builtinAdditionPanes.commandView import CommandView @@ -29,22 +29,22 @@ from gui.builtinAdditionPanes.fighterView import FighterView from gui.builtinAdditionPanes.implantView import ImplantView from gui.builtinAdditionPanes.notesView import NotesView from gui.builtinAdditionPanes.projectedView import ProjectedView -from gui.chromeTabs import PFNotebook -from gui.pyfatogglepanel import TogglePanel +from gui.chrome_tabs import ChromeNotebook +from gui.toggle_panel import TogglePanel class AdditionsPane(TogglePanel): def __init__(self, parent): - TogglePanel.__init__(self, parent, forceLayout=1) + TogglePanel.__init__(self, parent, force_layout=1) self.SetLabel("Additions") - pane = self.GetContentPane() + pane = self.GetContentPanel() baseSizer = wx.BoxSizer(wx.HORIZONTAL) pane.SetSizer(baseSizer) - self.notebook = PFNotebook(pane, False) + self.notebook = ChromeNotebook(pane, False) self.notebook.SetMinSize((-1, 1000)) baseSizer.Add(self.notebook, 1, wx.EXPAND) @@ -59,28 +59,28 @@ class AdditionsPane(TogglePanel): notesImg = BitmapLoader.getImage("skill_small", "gui") self.drone = DroneView(self.notebook) - self.notebook.AddPage(self.drone, "Drones", tabImage=droneImg, showClose=False) + self.notebook.AddPage(self.drone, "Drones", image=droneImg, closeable=False) self.fighter = FighterView(self.notebook) - self.notebook.AddPage(self.fighter, "Fighters", tabImage=fighterImg, showClose=False) + self.notebook.AddPage(self.fighter, "Fighters", image=fighterImg, closeable=False) self.cargo = CargoView(self.notebook) - self.notebook.AddPage(self.cargo, "Cargo", tabImage=cargoImg, showClose=False) + self.notebook.AddPage(self.cargo, "Cargo", image=cargoImg, closeable=False) self.implant = ImplantView(self.notebook) - self.notebook.AddPage(self.implant, "Implants", tabImage=implantImg, showClose=False) + self.notebook.AddPage(self.implant, "Implants", image=implantImg, closeable=False) self.booster = BoosterView(self.notebook) - self.notebook.AddPage(self.booster, "Boosters", tabImage=boosterImg, showClose=False) + self.notebook.AddPage(self.booster, "Boosters", image=boosterImg, closeable=False) self.projectedPage = ProjectedView(self.notebook) - self.notebook.AddPage(self.projectedPage, "Projected", tabImage=projectedImg, showClose=False) + self.notebook.AddPage(self.projectedPage, "Projected", image=projectedImg, closeable=False) self.gangPage = CommandView(self.notebook) - self.notebook.AddPage(self.gangPage, "Command", tabImage=gangImg, showClose=False) + self.notebook.AddPage(self.gangPage, "Command", image=gangImg, closeable=False) self.notes = NotesView(self.notebook) - self.notebook.AddPage(self.notes, "Notes", tabImage=notesImg, showClose=False) + self.notebook.AddPage(self.notes, "Notes", image=notesImg, closeable=False) self.notebook.SetSelection(0) @@ -92,19 +92,16 @@ class AdditionsPane(TogglePanel): def getName(self, idx): return self.PANES[idx] - def toggleContent(self, event): - TogglePanel.toggleContent(self, event) - h = self.headerPanel.GetSize()[1] + 4 + def ToggleContent(self, event): + TogglePanel.ToggleContent(self, event) + h = self.header_panel.GetSize()[1] + 4 if self.IsCollapsed(): self.old_pos = self.parent.GetSashPosition() self.parent.SetMinimumPaneSize(h) self.parent.SetSashPosition(h * -1, True) - # only available in >= wx2.9 - if getattr(self.parent, "SetSashInvisible", None): - self.parent.SetSashInvisible(True) + self.parent.SetSashInvisible(True) else: - if getattr(self.parent, "SetSashInvisible", None): - self.parent.SetSashInvisible(False) + self.parent.SetSashInvisible(False) self.parent.SetMinimumPaneSize(200) self.parent.SetSashPosition(self.old_pos, True) diff --git a/gui/bitmapLoader.py b/gui/bitmap_loader.py similarity index 78% rename from gui/bitmapLoader.py rename to gui/bitmap_loader.py index eb53b1f1d..9330f1fc3 100644 --- a/gui/bitmapLoader.py +++ b/gui/bitmap_loader.py @@ -17,9 +17,10 @@ # along with pyfa. If not, see . # ============================================================================= -import cStringIO +import io import os.path import zipfile +from collections import OrderedDict # noinspection PyPackageRequirements import wx @@ -29,23 +30,18 @@ import config from logbook import Logger logging = Logger(__name__) -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict - class BitmapLoader(object): try: archive = zipfile.ZipFile(os.path.join(config.pyfaPath, 'imgs.zip'), 'r') logging.info("Using zipped image files.") - except IOError: + except (IOError, TypeError): logging.info("Using local image files.") archive = None - cachedBitmaps = OrderedDict() - dontUseCachedBitmaps = False - max_bmps = 500 + cached_bitmaps = OrderedDict() + dont_use_cached_bitmaps = False + max_cached_bitmaps = 500 @classmethod def getStaticBitmap(cls, name, parent, location): @@ -55,25 +51,25 @@ class BitmapLoader(object): @classmethod def getBitmap(cls, name, location): - if cls.dontUseCachedBitmaps: + if cls.dont_use_cached_bitmaps: img = cls.getImage(name, location) if img is not None: return img.ConvertToBitmap() path = "%s%s" % (name, location) - if len(cls.cachedBitmaps) == cls.max_bmps: - cls.cachedBitmaps.popitem(False) + if len(cls.cached_bitmaps) == cls.max_cached_bitmaps: + cls.cached_bitmaps.popitem(False) - if path not in cls.cachedBitmaps: + if path not in cls.cached_bitmaps: img = cls.getImage(name, location) if img is not None: bmp = img.ConvertToBitmap() else: bmp = None - cls.cachedBitmaps[path] = bmp + cls.cached_bitmaps[path] = bmp else: - bmp = cls.cachedBitmaps[path] + bmp = cls.cached_bitmaps[path] return bmp @@ -88,14 +84,14 @@ class BitmapLoader(object): try: img_data = cls.archive.read(path) - sbuf = cStringIO.StringIO(img_data) + sbuf = io.StringIO(img_data) return wx.ImageFromStream(sbuf) except KeyError: - print("Missing icon file from zip: {0}".format(path)) + print(("Missing icon file from zip: {0}".format(path))) else: path = os.path.join(config.pyfaPath, 'imgs' + os.sep + location + os.sep + filename) if os.path.exists(path): return wx.Image(path) else: - print("Missing icon file: {0}".format(path)) + print(("Missing icon file: {0}".format(path))) diff --git a/gui/builtinAdditionPanes/boosterView.py b/gui/builtinAdditionPanes/boosterView.py index ab80209ec..8923f83de 100644 --- a/gui/builtinAdditionPanes/boosterView.py +++ b/gui/builtinAdditionPanes/boosterView.py @@ -28,12 +28,12 @@ from gui.utils.staticHelpers import DragDropHelper from service.fit import Fit -class BoosterViewDrop(wx.PyDropTarget): +class BoosterViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(BoosterViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -131,6 +131,7 @@ class BoosterView(d.Display): fit = sFit.getFit(fitID) if not fit or fit.isStructure: + event.Skip() return trigger = sFit.addBooster(fitID, event.itemID) diff --git a/gui/builtinAdditionPanes/cargoView.py b/gui/builtinAdditionPanes/cargoView.py index cb6a5b47e..ed5687cfc 100644 --- a/gui/builtinAdditionPanes/cargoView.py +++ b/gui/builtinAdditionPanes/cargoView.py @@ -28,12 +28,12 @@ from service.fit import Fit from service.market import Market -class CargoViewDrop(wx.PyDropTarget): +class CargoViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(CargoViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -88,7 +88,7 @@ class CargoView(d.Display): row = event.GetIndex() if row != -1: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "cargo:" + str(row) data.SetText(dataStr) @@ -119,14 +119,14 @@ class CargoView(d.Display): module = fit.modules[modIdx] if dstRow != -1: # we're swapping with cargo - if mstate.CmdDown(): # if copying, append to cargo + if mstate.cmdDown: # if copying, append to cargo sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) else: # else, move / swap sFit.moveCargoToModule(self.mainFrame.getActiveFit(), module.position, dstRow) else: # dragging to blank spot, append sFit.addCargo(self.mainFrame.getActiveFit(), module.item.ID) - if not mstate.CmdDown(): # if not copying, remove module + if not mstate.cmdDown: # if not copying, remove module sFit.removeModule(self.mainFrame.getActiveFit(), module.position) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit(), action="moddel", typeID=module.item.ID)) diff --git a/gui/builtinAdditionPanes/commandView.py b/gui/builtinAdditionPanes/commandView.py index e903f0ac3..5ee0d1872 100644 --- a/gui/builtinAdditionPanes/commandView.py +++ b/gui/builtinAdditionPanes/commandView.py @@ -43,12 +43,12 @@ class DummyEntry(object): self.item = DummyItem(txt) -class CommandViewDrop(wx.PyDropTarget): +class CommandViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(CommandViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -119,7 +119,7 @@ class CommandView(d.Display): def startDrag(self, event): row = event.GetIndex() if row != -1 and isinstance(self.get(row), es_Drone): - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "command:" + str(self.GetItemData(row)) data.SetText(dataStr) @@ -136,6 +136,8 @@ class CommandView(d.Display): sFit = Fit.getInstance() fit = sFit.getFit(event.fitID) + CommandFits.populateFits(event) + self.Parent.Parent.DisablePage(self, not fit or fit.isStructure) # Clear list and get out if current fitId is None @@ -167,6 +169,8 @@ class CommandView(d.Display): self.update(stuff) + event.Skip() + def get(self, row): if row == -1: return None diff --git a/gui/builtinAdditionPanes/droneView.py b/gui/builtinAdditionPanes/droneView.py index 7fde0a105..e9b893047 100644 --- a/gui/builtinAdditionPanes/droneView.py +++ b/gui/builtinAdditionPanes/droneView.py @@ -21,6 +21,7 @@ import wx import gui.globalEvents as GE +import gui.mainFrame from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED from gui.display import Display from gui.builtinViewColumns.state import State @@ -30,12 +31,12 @@ from service.fit import Fit from service.market import Market -class DroneViewDrop(wx.PyDropTarget): +class DroneViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(DroneViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -66,6 +67,8 @@ class DroneView(Display): self.hoveredRow = None self.hoveredColumn = None + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) self.mainFrame.Bind(ITEM_SELECTED, self.addItem) self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) @@ -101,7 +104,7 @@ class DroneView(Display): if self.DEFAULT_COLS[col] == "Miscellanea": tooltip = self.activeColumns[col].getToolTip(mod) if tooltip is not None: - self.SetToolTipString(tooltip) + self.SetToolTip(tooltip) else: self.SetToolTip(None) else: @@ -123,7 +126,7 @@ class DroneView(Display): def startDrag(self, event): row = event.GetIndex() if row != -1: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "drone:" + str(row) data.SetText(dataStr) @@ -207,6 +210,7 @@ class DroneView(Display): fit = sFit.getFit(fitID) if not fit or fit.isStructure: + event.Skip() return trigger = sFit.addDrone(fitID, event.itemID) diff --git a/gui/builtinAdditionPanes/fighterView.py b/gui/builtinAdditionPanes/fighterView.py index 3e7017794..3292583fd 100644 --- a/gui/builtinAdditionPanes/fighterView.py +++ b/gui/builtinAdditionPanes/fighterView.py @@ -32,12 +32,12 @@ from service.fit import Fit from service.market import Market -class FighterViewDrop(wx.PyDropTarget): +class FighterViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(FighterViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -60,7 +60,7 @@ class FighterView(wx.Panel): mainSizer.Add(self.fighterDisplay, 1, wx.EXPAND, 0) textSizer = wx.BoxSizer(wx.HORIZONTAL) - textSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + textSizer.AddStretchSpacer() for x in self.labels: lbl = wx.StaticText(self, wx.ID_ANY, x.capitalize()) @@ -75,7 +75,7 @@ class FighterView(wx.Panel): lbl = wx.StaticText(self, wx.ID_ANY, "0") setattr(self, "label%sTotal" % (x.capitalize()), lbl) textSizer.Add(lbl, 0, wx.ALIGN_CENTER) - textSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + textSizer.AddStretchSpacer() mainSizer.Add(textSizer, 0, wx.EXPAND, 5) @@ -97,7 +97,7 @@ class FighterView(wx.Panel): slot = getattr(Slot, "F_{}".format(x.upper())) used = fit.getSlotsUsed(slot) total = fit.getNumSlots(slot) - color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings_GetColour( + color = wx.Colour(204, 51, 51) if used > total else wx.SystemSettings.GetColour( wx.SYS_COLOUR_WINDOWTEXT) lbl = getattr(self, "label%sUsed" % x.capitalize()) @@ -110,6 +110,8 @@ class FighterView(wx.Panel): self.Refresh() + event.Skip() + class FighterDisplay(d.Display): DEFAULT_COLS = ["State", @@ -166,7 +168,7 @@ class FighterDisplay(d.Display): if self.DEFAULT_COLS[col] == "Miscellanea": tooltip = self.activeColumns[col].getToolTip(mod) if tooltip is not None: - self.SetToolTipString(tooltip) + self.SetToolTip(tooltip) else: self.SetToolTip(None) else: @@ -188,7 +190,7 @@ class FighterDisplay(d.Display): def startDrag(self, event): row = event.GetIndex() if row != -1: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "fighter:" + str(row) data.SetText(dataStr) diff --git a/gui/builtinAdditionPanes/implantView.py b/gui/builtinAdditionPanes/implantView.py index 2531f997a..b1d7bbe66 100644 --- a/gui/builtinAdditionPanes/implantView.py +++ b/gui/builtinAdditionPanes/implantView.py @@ -41,12 +41,12 @@ class ImplantView(wx.Panel): mainSizer.Add(self.implantDisplay, 1, wx.EXPAND, 0) radioSizer = wx.BoxSizer(wx.HORIZONTAL) - radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + radioSizer.AddStretchSpacer() self.rbFit = wx.RadioButton(self, id=wx.ID_ANY, label="Use Fit-specific Implants", style=wx.RB_GROUP) self.rbChar = wx.RadioButton(self, id=wx.ID_ANY, label="Use Character Implants") radioSizer.Add(self.rbFit, 0, wx.ALL, 5) radioSizer.Add(self.rbChar, 0, wx.ALL, 5) - radioSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + radioSizer.AddStretchSpacer() mainSizer.Add(radioSizer, 0, wx.EXPAND, 5) @@ -71,6 +71,8 @@ class ImplantView(wx.Panel): self.rbFit.Enable(fit is not None) self.rbChar.Enable(fit is not None) + event.Skip() + def OnRadioSelect(self, event): fitID = self.mainFrame.getActiveFit() sFit = Fit.getInstance() @@ -150,6 +152,7 @@ class ImplantDisplay(d.Display): fit = sFit.getFit(fitID) if not fit or fit.isStructure: + event.Skip() return trigger = sFit.addImplant(fitID, event.itemID) diff --git a/gui/builtinAdditionPanes/notesView.py b/gui/builtinAdditionPanes/notesView.py index 5c44a26c2..35a75b5a8 100644 --- a/gui/builtinAdditionPanes/notesView.py +++ b/gui/builtinAdditionPanes/notesView.py @@ -40,6 +40,8 @@ class NotesView(wx.Panel): self.lastFitId = event.fitID self.editNotes.SetValue(fit.notes or "") + event.Skip() + def onText(self, event): # delay the save so we're not writing to sqlite on every keystroke self.saveTimer.Stop() # cancel the existing timer diff --git a/gui/builtinAdditionPanes/projectedView.py b/gui/builtinAdditionPanes/projectedView.py index 93350e7f7..a5654d1a4 100644 --- a/gui/builtinAdditionPanes/projectedView.py +++ b/gui/builtinAdditionPanes/projectedView.py @@ -47,12 +47,12 @@ class DummyEntry(object): self.item = DummyItem(txt) -class ProjectedViewDrop(wx.PyDropTarget): +class ProjectedViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(ProjectedViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -139,7 +139,7 @@ class ProjectedView(d.Display): def startDrag(self, event): row = event.GetIndex() if row != -1 and isinstance(self.get(row), es_Drone): - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "projected:" + str(self.GetItemData(row)) data.SetText(dataStr) @@ -229,6 +229,8 @@ class ProjectedView(d.Display): self.update(stuff) + event.Skip() + def get(self, row): if row == -1: return None diff --git a/gui/builtinContextMenus/changeAffectingSkills.py b/gui/builtinContextMenus/changeAffectingSkills.py index a708ec51f..148c18218 100644 --- a/gui/builtinContextMenus/changeAffectingSkills.py +++ b/gui/builtinContextMenus/changeAffectingSkills.py @@ -3,7 +3,7 @@ from gui.contextMenu import ContextMenu import gui.mainFrame # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from eos.saveddata.character import Skill import gui.globalEvents as GE from service.fit import Fit @@ -49,7 +49,7 @@ class ChangeAffectingSkills(ContextMenu): if cont[attrName] == 0: continue - for fit, afflictors in cont.getAfflictions(attrName).iteritems(): + for fit, afflictors in cont.getAfflictions(attrName).items(): for afflictor, modifier, amount, used in afflictors: # only add Skills if not isinstance(afflictor, Skill): @@ -89,12 +89,12 @@ class ChangeAffectingSkills(ContextMenu): if bitmap is not None: skillItem.SetBitmap(bitmap) - for i in xrange(-1, 6): + for i in range(-1, 6): levelItem = self.addSkill(rootMenu if msw else grandSub, skill, i) - grandSub.AppendItem(levelItem) + grandSub.Append(levelItem) if (not skill.learned and i == -1) or (skill.learned and skill.level == i): levelItem.Check(True) - sub.AppendItem(skillItem) + sub.Append(skillItem) return sub diff --git a/gui/builtinContextMenus/commandFits.py b/gui/builtinContextMenus/commandFits.py index 782e3cbff..936dd0cc0 100644 --- a/gui/builtinContextMenus/commandFits.py +++ b/gui/builtinContextMenus/commandFits.py @@ -30,6 +30,7 @@ class CommandFits(ContextMenu): if evt is None or not ids.isdisjoint(cls.commandTypeIDs): # we are adding or removing an item that defines a command fit. Need to refresh fit list cls.populateFits(evt) + evt.Skip() @classmethod def populateFits(cls, evt): @@ -50,7 +51,7 @@ class CommandFits(ContextMenu): return "Command Fits" def addFit(self, menu, fit, includeShip=False): - label = fit.name if not includeShip else u"({}) {}".format(fit.ship.item.name, fit.name) + label = fit.name if not includeShip else "({}) {}".format(fit.ship.item.name, fit.name) id = ContextMenu.nextID() self.fitMenuItemIds[id] = fit menuItem = wx.MenuItem(menu, id, label) @@ -67,7 +68,7 @@ class CommandFits(ContextMenu): if len(self.__class__.commandFits) < 15: for fit in sorted(self.__class__.commandFits, key=lambda x: x.name): menuItem = self.addFit(rootMenu if msw else sub, fit, True) - sub.AppendItem(menuItem) + sub.Append(menuItem) else: typeDict = {} @@ -84,9 +85,9 @@ class CommandFits(ContextMenu): for fit in sorted(typeDict[ship], key=lambda x: x.name): fitItem = self.addFit(rootMenu if msw else grandSub, fit, False) - grandSub.AppendItem(fitItem) + grandSub.Append(fitItem) - sub.AppendItem(shipItem) + sub.Append(shipItem) return sub diff --git a/gui/builtinContextMenus/damagePattern.py b/gui/builtinContextMenus/damagePattern.py index 8399dd069..ca65cf9c9 100644 --- a/gui/builtinContextMenus/damagePattern.py +++ b/gui/builtinContextMenus/damagePattern.py @@ -3,15 +3,12 @@ import gui.mainFrame import gui.globalEvents as GE # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.fit import Fit from service.damagePattern import DamagePattern as import_DamagePattern from service.settings import ContextMenuSettings -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +from collections import OrderedDict class DamagePattern(ContextMenu): @@ -52,7 +49,7 @@ class DamagePattern(ContextMenu): self.singles.append(pattern) # return list of names, with singles first followed by submenu names - self.m = map(lambda p: p.name, self.singles) + self.subMenus.keys() + self.m = [p.name for p in self.singles] + list(self.subMenus.keys()) return self.m def addPattern(self, rootMenu, pattern): @@ -96,7 +93,7 @@ class DamagePattern(ContextMenu): # Items that have a parent for pattern in self.subMenus[self.m[i]]: - sub.AppendItem(self.addPattern(rootMenu if msw else sub, pattern)) + sub.Append(self.addPattern(rootMenu if msw else sub, pattern)) return sub diff --git a/gui/builtinContextMenus/droneSplit.py b/gui/builtinContextMenus/droneSplit.py index 7a121ae96..ec52db3f8 100644 --- a/gui/builtinContextMenus/droneSplit.py +++ b/gui/builtinContextMenus/droneSplit.py @@ -45,7 +45,7 @@ class DroneSpinner(wx.Dialog): bSizer1.Add(self.spinner, 1, wx.ALL, 5) - self.button = wx.Button(self, wx.ID_OK, u"Split") + self.button = wx.Button(self, wx.ID_OK, "Split") bSizer1.Add(self.button, 0, wx.ALL, 5) self.SetSizer(bSizer1) diff --git a/gui/builtinContextMenus/factorReload.py b/gui/builtinContextMenus/factorReload.py index 6770e64af..5600c4c5c 100644 --- a/gui/builtinContextMenus/factorReload.py +++ b/gui/builtinContextMenus/factorReload.py @@ -3,7 +3,7 @@ import gui.mainFrame import gui.globalEvents as GE # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.fit import Fit from service.settings import ContextMenuSettings @@ -31,9 +31,7 @@ class FactorReload(ContextMenu): def getBitmap(self, context, selection): sFit = Fit.getInstance() - fitID = self.mainFrame.getActiveFit() - fit = sFit.getFit(fitID) - if fit.factorReload: + if sFit.serviceFittingOptions["useGlobalForceReload"]: return BitmapLoader.getBitmap("state_active_small", "gui") else: return None diff --git a/gui/builtinContextMenus/fighterAbilities.py b/gui/builtinContextMenus/fighterAbilities.py index 6f6bc8051..76eab5c1e 100644 --- a/gui/builtinContextMenus/fighterAbilities.py +++ b/gui/builtinContextMenus/fighterAbilities.py @@ -44,7 +44,7 @@ class FighterAbility(ContextMenu): if not ability.effect.isImplemented: continue menuItem = self.addAbility(rootMenu if msw else sub, ability) - sub.AppendItem(menuItem) + sub.Append(menuItem) menuItem.Check(ability.active) return sub diff --git a/gui/builtinContextMenus/implantSets.py b/gui/builtinContextMenus/implantSets.py index 0ef7cb2ee..1eb12d1c3 100644 --- a/gui/builtinContextMenus/implantSets.py +++ b/gui/builtinContextMenus/implantSets.py @@ -61,7 +61,7 @@ class ImplantSets(ContextMenu): mitem = wx.MenuItem(rootMenu, id, set.name) bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem) self.idmap[id] = set - m.AppendItem(mitem) + m.Append(mitem) return m diff --git a/gui/builtinContextMenus/itemStats.py b/gui/builtinContextMenus/itemStats.py index 4e1b8187e..6221fa1e0 100644 --- a/gui/builtinContextMenus/itemStats.py +++ b/gui/builtinContextMenus/itemStats.py @@ -47,7 +47,7 @@ class ItemStats(ContextMenu): mstate = wx.GetMouseState() reuse = False - if mstate.CmdDown(): + if mstate.cmdDown: reuse = True if self.mainFrame.GetActiveStatsWindow() is None and reuse: diff --git a/gui/builtinContextMenus/metaSwap.py b/gui/builtinContextMenus/metaSwap.py index e4432abdd..a5f6371e3 100644 --- a/gui/builtinContextMenus/metaSwap.py +++ b/gui/builtinContextMenus/metaSwap.py @@ -91,7 +91,7 @@ class MetaSwap(ContextMenu): # Sort items by metalevel, and group within that metalevel items = list(self.variations) - print context + print(context) if "implantItem" in context: # sort implants based on name items.sort(key=lambda x: x.name) @@ -116,14 +116,14 @@ class MetaSwap(ContextMenu): if thisgroup != group and context not in ("implantItem", "boosterItem"): group = thisgroup id = ContextMenu.nextID() - m.Append(id, u'─ %s ─' % group) + m.Append(id, '─ %s ─' % group) m.Enable(id, False) id = ContextMenu.nextID() mitem = wx.MenuItem(rootMenu, id, item.name) bindmenu.Bind(wx.EVT_MENU, self.handleModule, mitem) self.moduleLookup[id] = item - m.AppendItem(mitem) + m.Append(mitem) return m def handleModule(self, event): diff --git a/gui/builtinContextMenus/moduleAmmoPicker.py b/gui/builtinContextMenus/moduleAmmoPicker.py index 1af970190..47a6fc87d 100644 --- a/gui/builtinContextMenus/moduleAmmoPicker.py +++ b/gui/builtinContextMenus/moduleAmmoPicker.py @@ -9,7 +9,7 @@ from eos.saveddata.module import Hardpoint import gui.mainFrame import gui.globalEvents as GE from gui.contextMenu import ContextMenu -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.settings import ContextMenuSettings @@ -50,7 +50,7 @@ class ModuleAmmoPicker(ContextMenu): return False self.modules = modules - self.charges = list(filter(lambda charge: Market.getInstance().getPublicityByItem(charge), validCharges)) + self.charges = list([charge for charge in validCharges if Market.getInstance().getPublicityByItem(charge)]) return len(self.charges) > 0 def getText(self, itmContext, selection): @@ -58,9 +58,9 @@ class ModuleAmmoPicker(ContextMenu): def turretSorter(self, charge): damage = 0 - range_ = (self.module.getModifiedItemAttr("maxRange") or 0) * \ + range_ = (self.module.getModifiedItemAttr("maxRange")) * \ (charge.getAttribute("weaponRangeMultiplier") or 1) - falloff = (self.module.getModifiedItemAttr("falloff") or 0) * \ + falloff = (self.module.getModifiedItemAttr("falloff")) * \ (charge.getAttribute("fallofMultiplier") or 1) for type_ in self.DAMAGE_TYPES: d = charge.getAttribute("%sDamage" % type_) @@ -108,7 +108,7 @@ class ModuleAmmoPicker(ContextMenu): def nameSorter(self, charge): parts = charge.name.split(" ") - return map(self.numericConverter, parts) + return list(map(self.numericConverter, parts)) def addCharge(self, menu, charge): id_ = ContextMenu.nextID() @@ -127,7 +127,7 @@ class ModuleAmmoPicker(ContextMenu): @staticmethod def addSeperator(m, text): id_ = ContextMenu.nextID() - m.Append(id_, u'─ %s ─' % text) + m.Append(id_, '─ %s ─' % text) m.Enable(id_, False) def getSubMenu(self, context, selection, rootMenu, i, pitem): @@ -137,7 +137,7 @@ class ModuleAmmoPicker(ContextMenu): hardpoint = self.module.hardpoint moduleName = self.module.item.name # Make sure we do not consider mining turrets as combat turrets - if hardpoint == Hardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount") is None: + if hardpoint == Hardpoint.TURRET and self.module.getModifiedItemAttr("miningAmount", None) is None: self.addSeperator(m, "Long Range") items = [] range_ = None @@ -170,15 +170,15 @@ class ModuleAmmoPicker(ContextMenu): sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) self.addSeperator(sub, "Less Damage") item.SetSubMenu(sub) - sub.AppendItem(self.addCharge(rootMenu if msw else sub, base)) + sub.Append(self.addCharge(rootMenu if msw else sub, base)) - sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) + sub.Append(self.addCharge(rootMenu if msw else sub, charge)) if sub is not None: self.addSeperator(sub, "More Damage") for item in items: - m.AppendItem(item) + m.Append(item) self.addSeperator(m, "Short Range") elif hardpoint == Hardpoint.MISSILE and moduleName != 'Festival Launcher': @@ -203,23 +203,23 @@ class ModuleAmmoPicker(ContextMenu): sub.Bind(wx.EVT_MENU, self.handleAmmoSwitch) self.addSeperator(sub, "Less Damage") item.SetSubMenu(sub) - m.AppendItem(item) + m.Append(item) if charge.name not in ("Light Defender Missile I", "Heavy Defender Missile I"): - sub.AppendItem(self.addCharge(rootMenu if msw else sub, charge)) + sub.Append(self.addCharge(rootMenu if msw else sub, charge)) else: defender = charge if defender is not None: - m.AppendItem(self.addCharge(rootMenu if msw else m, defender)) + m.Append(self.addCharge(rootMenu if msw else m, defender)) if sub is not None: self.addSeperator(sub, "More Damage") else: self.charges.sort(key=self.nameSorter) for charge in self.charges: - m.AppendItem(self.addCharge(rootMenu if msw else m, charge)) + m.Append(self.addCharge(rootMenu if msw else m, charge)) - m.AppendItem(self.addCharge(rootMenu if msw else m, None)) + m.Append(self.addCharge(rootMenu if msw else m, None)) return m def handleAmmoSwitch(self, event): diff --git a/gui/builtinContextMenus/priceOptions.py b/gui/builtinContextMenus/priceOptions.py index 905ca9d4b..c9cb7f74a 100644 --- a/gui/builtinContextMenus/priceOptions.py +++ b/gui/builtinContextMenus/priceOptions.py @@ -35,7 +35,7 @@ class PriceOptions(ContextMenu): for option in self.optionList: menuItem = self.addOption(rootMenu if msw else sub, option) - sub.AppendItem(menuItem) + sub.Append(menuItem) menuItem.Check(self.settings.get(option.lower())) return sub diff --git a/gui/builtinContextMenus/tabbedFits.py b/gui/builtinContextMenus/tabbedFits.py index 30a7bee7a..c55a90b35 100644 --- a/gui/builtinContextMenus/tabbedFits.py +++ b/gui/builtinContextMenus/tabbedFits.py @@ -38,15 +38,15 @@ class TabbedFits(ContextMenu): else: bindmenu = m - for page in self.mainFrame.fitMultiSwitch.pages: + for page in self.mainFrame.fitMultiSwitch._pages: if isinstance(page, BlankPage): continue fit = sFit.getFit(page.activeFitID, basic=True) id = ContextMenu.nextID() - mitem = wx.MenuItem(rootMenu, id, u"{}: {}".format(fit.ship.item.name, fit.name)) + mitem = wx.MenuItem(rootMenu, id, "{}: {}".format(fit.ship.item.name, fit.name)) bindmenu.Bind(wx.EVT_MENU, self.handleSelection, mitem) self.fitLookup[id] = fit - m.AppendItem(mitem) + m.Append(mitem) return m diff --git a/gui/builtinContextMenus/tacticalMode.py b/gui/builtinContextMenus/tacticalMode.py index 8b87dd10b..e09b26940 100644 --- a/gui/builtinContextMenus/tacticalMode.py +++ b/gui/builtinContextMenus/tacticalMode.py @@ -49,7 +49,7 @@ class TacticalMode(ContextMenu): for mode in self.modes: menuItem = self.addMode(rootMenu if msw else sub, mode) - sub.AppendItem(menuItem) + sub.Append(menuItem) menuItem.Check(self.currMode.item == mode.item) return sub diff --git a/gui/builtinContextMenus/targetResists.py b/gui/builtinContextMenus/targetResists.py index 95468ecd0..03478b8d2 100644 --- a/gui/builtinContextMenus/targetResists.py +++ b/gui/builtinContextMenus/targetResists.py @@ -3,15 +3,11 @@ import gui.mainFrame import gui.globalEvents as GE # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.targetResists import TargetResists as svc_TargetResists from service.fit import Fit from service.settings import ContextMenuSettings - -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +from collections import OrderedDict class TargetResists(ContextMenu): @@ -87,15 +83,15 @@ class TargetResists(ContextMenu): else: self.singles.append(pattern) - sub.AppendItem(self.addPattern(rootMenu if msw else sub, None)) # Add reset + sub.Append(self.addPattern(rootMenu if msw else sub, None)) # Add reset sub.AppendSeparator() # Single items, no parent for pattern in self.singles: - sub.AppendItem(self.addPattern(rootMenu if msw else sub, pattern)) + sub.Append(self.addPattern(rootMenu if msw else sub, pattern)) # Items that have a parent - for menuName, patterns in self.subMenus.items(): + for menuName, patterns in list(self.subMenus.items()): # Create parent item for root menu that is simply name of parent item = wx.MenuItem(rootMenu, ContextMenu.nextID(), menuName) @@ -108,8 +104,8 @@ class TargetResists(ContextMenu): # Append child items to child menu for pattern in patterns: - grandSub.AppendItem(self.addPattern(rootMenu if msw else grandSub, pattern)) - sub.AppendItem(item) # finally, append parent item to root menu + grandSub.Append(self.addPattern(rootMenu if msw else grandSub, pattern)) + sub.Append(item) # finally, append parent item to root menu return sub diff --git a/gui/builtinContextMenus/whProjector.py b/gui/builtinContextMenus/whProjector.py index a677ceabf..bafca94ab 100644 --- a/gui/builtinContextMenus/whProjector.py +++ b/gui/builtinContextMenus/whProjector.py @@ -34,7 +34,7 @@ class WhProjector(ContextMenu): subItem = wx.MenuItem(sub, wx.ID_ANY, swType) grandSub = wx.Menu() subItem.SetSubMenu(grandSub) - sub.AppendItem(subItem) + sub.Append(subItem) for swData in sorted(effdata[swType], key=lambda tpl: tpl[2]): wxid = ContextMenu.nextID() @@ -45,7 +45,7 @@ class WhProjector(ContextMenu): rootMenu.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem) else: grandSub.Bind(wx.EVT_MENU, self.handleSelection, grandSubItem) - grandSub.AppendItem(grandSubItem) + grandSub.Append(grandSubItem) return sub def handleSelection(self, event): diff --git a/gui/builtinGraphs/fitDps.py b/gui/builtinGraphs/fitDps.py index ae03c180b..d848d3f9c 100644 --- a/gui/builtinGraphs/fitDps.py +++ b/gui/builtinGraphs/fitDps.py @@ -18,7 +18,7 @@ # ============================================================================= from gui.graph import Graph -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from eos.graph.fitDps import FitDpsGraph as FitDps from eos.graph import Data import gui.mainFrame @@ -54,7 +54,7 @@ class FitDpsGraph(Graph): def getIcons(self): icons = {} sAttr = Attribute.getInstance() - for key, attrName in self.propertyAttributeMap.iteritems(): + for key, attrName in self.propertyAttributeMap.items(): iconFile = sAttr.getAttributeInfo(attrName).icon.iconFile bitmap = BitmapLoader.getBitmap(iconFile, "icons") if bitmap: @@ -69,7 +69,7 @@ class FitDpsGraph(Graph): fitDps.clearData() variable = None - for fieldName, value in fields.iteritems(): + for fieldName, value in fields.items(): d = Data(fieldName, value) if not d.isConstant(): if variable is None: diff --git a/gui/builtinItemStatsViews/itemAffectedBy.py b/gui/builtinItemStatsViews/itemAffectedBy.py index b29d84b89..ed4280045 100644 --- a/gui/builtinItemStatsViews/itemAffectedBy.py +++ b/gui/builtinItemStatsViews/itemAffectedBy.py @@ -14,7 +14,7 @@ from eos.saveddata.fit import Fit import gui.mainFrame from gui.contextMenu import ContextMenu -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class ItemAffectedBy(wx.Panel): @@ -43,17 +43,17 @@ class ItemAffectedBy(wx.Panel): mainSizer.Add(self.m_staticline, 0, wx.EXPAND) bSizer = wx.BoxSizer(wx.HORIZONTAL) - self.toggleExpandBtn = wx.ToggleButton(self, wx.ID_ANY, u"Expand All", wx.DefaultPosition, wx.DefaultSize, 0) + self.toggleExpandBtn = wx.ToggleButton(self, wx.ID_ANY, "Expand All", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.toggleExpandBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.toggleNameBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle Names", wx.DefaultPosition, wx.DefaultSize, 0) + self.toggleNameBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle Names", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.toggleNameBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle View", wx.DefaultPosition, wx.DefaultSize, 0) + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle View", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) if stuff is not None: - self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT) + self.refreshBtn = wx.Button(self, wx.ID_ANY, "Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT) bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshTree) @@ -74,9 +74,9 @@ class ItemAffectedBy(wx.Panel): def spawnMenu(self, item): self.affectedBy.SelectItem(item) - stuff = self.affectedBy.GetPyData(item) + stuff = self.affectedBy.GetItemData(item) # String is set as data when we are dealing with attributes, not stuff containers - if stuff is None or isinstance(stuff, basestring): + if stuff is None or isinstance(stuff, str): return contexts = [] @@ -109,10 +109,10 @@ class ItemAffectedBy(wx.Panel): self.Freeze() for item in self.treeItems: - change = self.affectedBy.GetPyData(item) + change = self.affectedBy.GetItemData(item) display = self.affectedBy.GetItemText(item) self.affectedBy.SetItemText(item, change) - self.affectedBy.SetPyData(item, display) + self.affectedBy.SetItemData(item, display) self.Thaw() @@ -141,7 +141,7 @@ class ItemAffectedBy(wx.Panel): # sheri was here del self.treeItems[:] root = self.affectedBy.AddRoot("WINPWNZ0R") - self.affectedBy.SetPyData(root, None) + self.affectedBy.SetItemData(root, None) self.imageList = wx.ImageList(16, 16) self.affectedBy.SetImageList(self.imageList) @@ -185,7 +185,7 @@ class ItemAffectedBy(wx.Panel): if attributes[attrName] == (attributes.getOriginal(attrName, 0)): continue - for fit, afflictors in attributes.getAfflictions(attrName).iteritems(): + for fit, afflictors in attributes.getAfflictions(attrName).items(): for afflictor, modifier, amount, used in afflictors: if not used or afflictor.item is None: @@ -216,7 +216,7 @@ class ItemAffectedBy(wx.Panel): (type(afflictor), afflictor, item, modifier, amount, getattr(afflictor, "projected", False))) # Make sure projected fits are on top - rootOrder = container.keys() + rootOrder = list(container.keys()) rootOrder.sort(key=lambda x: self.ORDER.index(type(x))) # Now, we take our created dictionary and start adding stuff to our tree @@ -230,7 +230,7 @@ class ItemAffectedBy(wx.Panel): parent = child attributes = container[thing] - attrOrder = sorted(attributes.keys(), key=self.sortAttrDisplayName) + attrOrder = sorted(list(attributes.keys()), key=self.sortAttrDisplayName) for attrName in attrOrder: attrInfo = self.stuff.item.attributes.get(attrName) @@ -257,7 +257,7 @@ class ItemAffectedBy(wx.Panel): # this is the attribute node child = self.affectedBy.AppendItem(parent, display, attrIcon) - self.affectedBy.SetPyData(child, saved) + self.affectedBy.SetItemData(child, saved) self.treeItems.append(child) items = attributes[attrName] @@ -284,12 +284,12 @@ class ItemAffectedBy(wx.Panel): penalized += "(penalized)" if 'r' in attrModifier: penalized += "(resisted)" - attrModifier = "*" + attrModifier = "*" # this is the Module node, the attribute will be attached to this display = "%s %s %.2f %s" % (displayStr, attrModifier, attrAmount, penalized) treeItem = self.affectedBy.AppendItem(child, display, itemIcon) - self.affectedBy.SetPyData(treeItem, afflictor) + self.affectedBy.SetItemData(treeItem, afflictor) def buildModuleView(self, root): """ @@ -314,7 +314,7 @@ class ItemAffectedBy(wx.Panel): if attributes[attrName] == (attributes.getOriginal(attrName, 0)): continue - for fit, afflictors in attributes.getAfflictions(attrName).iteritems(): + for fit, afflictors in attributes.getAfflictions(attrName).items(): for afflictor, modifier, amount, used in afflictors: if not used or getattr(afflictor, 'item', None) is None: continue @@ -350,7 +350,7 @@ class ItemAffectedBy(wx.Panel): info[2].append((attrName, modifier, amount)) # Make sure projected fits are on top - rootOrder = container.keys() + rootOrder = list(container.keys()) rootOrder.sort(key=lambda x: self.ORDER.index(type(x))) # Now, we take our created dictionary and start adding stuff to our tree @@ -364,7 +364,7 @@ class ItemAffectedBy(wx.Panel): parent = child items = container[thing] - order = items.keys() + order = list(items.keys()) order.sort(key=lambda x: (self.ORDER.index(items[x][0]), x)) for itemName in order: @@ -389,7 +389,7 @@ class ItemAffectedBy(wx.Panel): # this is the Module node, the attribute will be attached to this child = self.affectedBy.AppendItem(parent, displayStr, itemIcon) - self.affectedBy.SetPyData(child, afflictors.pop()) + self.affectedBy.SetItemData(child, afflictors.pop()) if counter > 0: attributes = [] @@ -416,7 +416,7 @@ class ItemAffectedBy(wx.Panel): penalized += "(penalized)" if 'r' in attrModifier: penalized += "(resisted)" - attrModifier = "*" + attrModifier = "*" attributes.append((attrName, (displayName if displayName != "" else attrName), attrModifier, attrAmount, penalized, attrIcon)) @@ -443,5 +443,5 @@ class ItemAffectedBy(wx.Panel): saved = "%s %s %.2f %s" % (attrName, attrModifier, attrAmount, penalized) treeitem = self.affectedBy.AppendItem(child, display, attrIcon) - self.affectedBy.SetPyData(treeitem, saved) + self.affectedBy.SetItemData(treeitem, saved) self.treeItems.append(treeitem) diff --git a/gui/builtinItemStatsViews/itemAttributes.py b/gui/builtinItemStatsViews/itemAttributes.py index cbb8cf246..e40710c45 100644 --- a/gui/builtinItemStatsViews/itemAttributes.py +++ b/gui/builtinItemStatsViews/itemAttributes.py @@ -1,13 +1,12 @@ -import sys import csv import config # noinspection PyPackageRequirements import wx -from helpers import AutoListCtrl +from .helpers import AutoListCtrl -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.market import Market from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount @@ -34,19 +33,19 @@ class ItemParams(wx.Panel): mainSizer.Add(self.m_staticline, 0, wx.EXPAND) bSizer = wx.BoxSizer(wx.HORIZONTAL) - self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0) + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) - self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, wx.DefaultSize, + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle view mode", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, u"Export Item Stats", wx.DefaultPosition, wx.DefaultSize, + self.exportStatsBtn = wx.ToggleButton(self, wx.ID_ANY, "Export Item Stats", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.exportStatsBtn, 0, wx.ALIGN_CENTER_VERTICAL) if stuff is not None: - self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT) + self.refreshBtn = wx.Button(self, wx.ID_ANY, "Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT) bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshValues) @@ -166,7 +165,7 @@ class ItemParams(wx.Panel): self.imageList = wx.ImageList(16, 16) self.paramList.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) - names = list(self.attrValues.iterkeys()) + names = list(self.attrValues.keys()) names.sort() idNameMap = {} @@ -203,7 +202,7 @@ class ItemParams(wx.Panel): else: attrIcon = self.imageList.Add(BitmapLoader.getBitmap("7_15", "icons")) - index = self.paramList.InsertImageStringItem(sys.maxint, attrName, attrIcon) + index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName, attrIcon) idNameMap[idCount] = attrName self.paramList.SetItemData(index, idCount) idCount += 1 @@ -222,11 +221,12 @@ class ItemParams(wx.Panel): else: valueUnitDefault = formatAmount(valueDefault, 3, 0, 0) - self.paramList.SetStringItem(index, 1, valueUnit) + self.paramList.SetItem(index, 1, valueUnit) if self.stuff is not None: - self.paramList.SetStringItem(index, 2, valueUnitDefault) - - self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2])) + self.paramList.SetItem(index, 2, valueUnitDefault) + # @todo: pheonix, this lamda used cmp() which no longer exists in py3. Probably a better way to do this in the + # long run, take a look + self.paramList.SortItems(lambda id1, id2: (idNameMap[id1] > idNameMap[id2]) - (idNameMap[id1] < idNameMap[id2])) self.paramList.RefreshRows() self.totalAttrsLabel.SetLabel("%d attributes. " % idCount) self.Layout() @@ -252,7 +252,7 @@ class ItemParams(wx.Panel): "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), "Modifier Percent" : ( lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), - "Volume" : (lambda: value, u"m\u00B3"), + "Volume" : (lambda: value, "m\u00B3"), "Sizeclass" : (lambda: value, ""), "Absolute Percent" : (lambda: (value * 100), unitName), "Milliseconds" : (lambda: value / 1000.0, unitName), @@ -266,7 +266,7 @@ class ItemParams(wx.Panel): v = override[0]() if isinstance(v, str): fvalue = v - elif isinstance(v, (int, float, long)): + elif isinstance(v, (int, float)): fvalue = formatAmount(v, 3, 0, 0) else: fvalue = v diff --git a/gui/builtinItemStatsViews/itemCompare.py b/gui/builtinItemStatsViews/itemCompare.py index 4e560883e..9e9ca53af 100644 --- a/gui/builtinItemStatsViews/itemCompare.py +++ b/gui/builtinItemStatsViews/itemCompare.py @@ -1,9 +1,7 @@ -import sys - # noinspection PyPackageRequirements import wx -from helpers import AutoListCtrl +from .helpers import AutoListCtrl from service.price import Price as ServicePrice from service.market import Market from service.attribute import Attribute @@ -30,17 +28,17 @@ class ItemCompare(wx.Panel): self.sortReverse = False self.item = item self.items = sorted(items, - key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else None) + key=lambda x: x.attributes['metaLevel'].value if 'metaLevel' in x.attributes else 0) self.attrs = {} # get a dict of attrName: attrInfo of all unique attributes across all items for item in self.items: - for attr in item.attributes.keys(): + for attr in list(item.attributes.keys()): if item.attributes[attr].info.displayName: self.attrs[attr] = item.attributes[attr].info # Process attributes for items and find ones that differ - for attr in self.attrs.keys(): + for attr in list(self.attrs.keys()): value = None for item in self.items: @@ -65,14 +63,14 @@ class ItemCompare(wx.Panel): mainSizer.Add(self.m_staticline, 0, wx.EXPAND) bSizer = wx.BoxSizer(wx.HORIZONTAL) - self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0) + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) - self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, u"Toggle view mode", wx.DefaultPosition, + self.toggleViewBtn = wx.ToggleButton(self, wx.ID_ANY, "Toggle view mode", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.toggleViewBtn, 0, wx.ALIGN_CENTER_VERTICAL) - self.refreshBtn = wx.Button(self, wx.ID_ANY, u"Refresh", wx.DefaultPosition, wx.DefaultSize, + self.refreshBtn = wx.Button(self, wx.ID_ANY, "Refresh", wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT) bSizer.Add(self.refreshBtn, 0, wx.ALIGN_CENTER_VERTICAL) self.refreshBtn.Bind(wx.EVT_BUTTON, self.RefreshValues) @@ -109,7 +107,7 @@ class ItemCompare(wx.Panel): def processPrices(self, prices): for i, price in enumerate(prices): - self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(price.value, 3, 3, 9, currency=True)) + self.paramList.SetItem(i, len(self.attrs) + 1, formatAmount(price.value, 3, 3, 9, currency=True)) def PopulateList(self, sort=None): @@ -126,7 +124,7 @@ class ItemCompare(wx.Panel): try: # Remember to reduce by 1, because the attrs array # starts at 0 while the list has the item name as column 0. - attr = str(self.attrs.keys()[sort - 1]) + attr = str(list(self.attrs.keys())[sort - 1]) func = lambda _val: _val.attributes[attr].value if attr in _val.attributes else None except IndexError: # Clicked on a column that's not part of our array (price most likely) @@ -147,7 +145,7 @@ class ItemCompare(wx.Panel): self.paramList.SetColumnWidth(len(self.attrs) + 1, 60) for item in self.items: - i = self.paramList.InsertStringItem(sys.maxint, item.name) + i = self.paramList.InsertItem(self.paramList.GetItemCount(), item.name) for x, attr in enumerate(self.attrs.keys()): if attr in item.attributes: info = self.attrs[attr] @@ -159,10 +157,10 @@ class ItemCompare(wx.Panel): else: valueUnit = formatAmount(value, 3, 0, 0) - self.paramList.SetStringItem(i, x + 1, valueUnit) + self.paramList.SetItem(i, x + 1, valueUnit) # Add prices - self.paramList.SetStringItem(i, len(self.attrs) + 1, formatAmount(item.price.price, 3, 3, 9, currency=True)) + self.paramList.SetItem(i, len(self.attrs) + 1, formatAmount(item.price.price, 3, 3, 9, currency=True)) self.paramList.RefreshRows() self.Layout() @@ -185,7 +183,7 @@ class ItemCompare(wx.Panel): "Inverse Absolute Percent" : (lambda: (1 - value) * 100, unitName), "Inversed Modifier Percent": (lambda: (1 - value) * 100, unitName), "Modifier Percent" : (lambda: ("%+.2f" if ((value - 1) * 100) % 1 else "%+d") % ((value - 1) * 100), unitName), - "Volume" : (lambda: value, u"m\u00B3"), + "Volume" : (lambda: value, "m\u00B3"), "Sizeclass" : (lambda: value, ""), "Absolute Percent" : (lambda: (value * 100), unitName), "Milliseconds" : (lambda: value / 1000.0, unitName), @@ -199,7 +197,7 @@ class ItemCompare(wx.Panel): v = override[0]() if isinstance(v, str): fvalue = v - elif isinstance(v, (int, float, long)): + elif isinstance(v, (int, float)): fvalue = formatAmount(v, 3, 0, 0) else: fvalue = v diff --git a/gui/builtinItemStatsViews/itemDependants.py b/gui/builtinItemStatsViews/itemDependants.py index 3a8110b55..9f7e459c9 100644 --- a/gui/builtinItemStatsViews/itemDependants.py +++ b/gui/builtinItemStatsViews/itemDependants.py @@ -1,7 +1,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class ItemDependents(wx.Panel): @@ -19,7 +19,7 @@ class ItemDependents(wx.Panel): self.SetSizer(mainSizer) self.root = self.reqTree.AddRoot("WINRARZOR") - self.reqTree.SetPyData(self.root, None) + self.reqTree.SetItemData(self.root, None) self.imageList = wx.ImageList(16, 16) self.reqTree.SetImageList(self.imageList) @@ -32,7 +32,7 @@ class ItemDependents(wx.Panel): def getFullSkillTree(self, parentSkill, parent, sbIconId): levelToItems = {} - for item, level in parentSkill.requiredFor.iteritems(): + for item, level in parentSkill.requiredFor.items(): if level not in levelToItems: levelToItems[level] = [] levelToItems[level].append(item) diff --git a/gui/builtinItemStatsViews/itemDescription.py b/gui/builtinItemStatsViews/itemDescription.py index c94bd2b66..ddf30dfb4 100644 --- a/gui/builtinItemStatsViews/itemDescription.py +++ b/gui/builtinItemStatsViews/itemDescription.py @@ -11,8 +11,8 @@ class ItemDescription(wx.Panel): mainSizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(mainSizer) - bgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - fgcolor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + bgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + fgcolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) self.description = wx.html.HtmlWindow(self) diff --git a/gui/builtinItemStatsViews/itemEffects.py b/gui/builtinItemStatsViews/itemEffects.py index 81617fdc6..e586786a6 100644 --- a/gui/builtinItemStatsViews/itemEffects.py +++ b/gui/builtinItemStatsViews/itemEffects.py @@ -1,4 +1,3 @@ -import sys import os import subprocess import config @@ -6,7 +5,7 @@ import config # noinspection PyPackageRequirements import wx -from helpers import AutoListCtrl +from .helpers import AutoListCtrl class ItemEffects(wx.Panel): @@ -47,11 +46,11 @@ class ItemEffects(wx.Panel): item = self.item effects = item.effects - names = list(effects.iterkeys()) + names = list(effects.keys()) names.sort() for name in names: - index = self.effectList.InsertStringItem(sys.maxint, name) + index = self.effectList.InsertItem(self.effectList.GetItemCount(), name) if effects[name].isImplemented: if effects[name].activeByDefault: @@ -72,11 +71,11 @@ class ItemEffects(wx.Panel): else: effectRunTime = "" - self.effectList.SetStringItem(index, 1, activeByDefault) - self.effectList.SetStringItem(index, 2, effectTypeText) + self.effectList.SetItem(index, 1, activeByDefault) + self.effectList.SetItem(index, 2, effectTypeText) if config.debug: - self.effectList.SetStringItem(index, 3, effectRunTime) - self.effectList.SetStringItem(index, 4, str(effects[name].ID)) + self.effectList.SetItem(index, 3, effectRunTime) + self.effectList.SetItem(index, 4, str(effects[name].ID)) self.effectList.RefreshRows() self.Layout() diff --git a/gui/builtinItemStatsViews/itemProperties.py b/gui/builtinItemStatsViews/itemProperties.py index b8ce86dd5..afc8c2645 100644 --- a/gui/builtinItemStatsViews/itemProperties.py +++ b/gui/builtinItemStatsViews/itemProperties.py @@ -1,9 +1,7 @@ -import sys - # noinspection PyPackageRequirements import wx -from helpers import AutoListCtrl +from .helpers import AutoListCtrl class ItemProperties(wx.Panel): @@ -27,7 +25,7 @@ class ItemProperties(wx.Panel): mainSizer.Add(self.m_staticline, 0, wx.EXPAND) bSizer = wx.BoxSizer(wx.HORIZONTAL) - self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, u" ", wx.DefaultPosition, wx.DefaultSize, 0) + self.totalAttrsLabel = wx.StaticText(self, wx.ID_ANY, " ", wx.DefaultPosition, wx.DefaultSize, 0) bSizer.Add(self.totalAttrsLabel, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) mainSizer.Add(bSizer, 0, wx.ALIGN_RIGHT) @@ -79,7 +77,7 @@ class ItemProperties(wx.Panel): attrName = name.title() value = getattr(self.item, name) - index = self.paramList.InsertStringItem(sys.maxint, attrName) + index = self.paramList.InsertItem(self.paramList.GetItemCount(), attrName) # index = self.paramList.InsertImageStringItem(sys.maxint, attrName) idNameMap[idCount] = attrName self.paramList.SetItemData(index, idCount) @@ -87,13 +85,13 @@ class ItemProperties(wx.Panel): valueUnit = str(value) - self.paramList.SetStringItem(index, 1, valueUnit) + self.paramList.SetItem(index, 1, valueUnit) except: # TODO: Add logging to this. # We couldn't get a property for some reason. Skip it for now. continue - self.paramList.SortItems(lambda id1, id2: cmp(idNameMap[id1], idNameMap[id2])) + self.paramList.SortItems(lambda id1, id2: (idNameMap[id1] > idNameMap[id2]) - (idNameMap[id1] < idNameMap[id2])) self.paramList.RefreshRows() self.totalAttrsLabel.SetLabel("%d attributes. " % idCount) self.Layout() diff --git a/gui/builtinItemStatsViews/itemRequirements.py b/gui/builtinItemStatsViews/itemRequirements.py index ff493bcab..dc3c947cd 100644 --- a/gui/builtinItemStatsViews/itemRequirements.py +++ b/gui/builtinItemStatsViews/itemRequirements.py @@ -1,7 +1,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class ItemRequirements(wx.Panel): @@ -19,7 +19,7 @@ class ItemRequirements(wx.Panel): self.SetSizer(mainSizer) self.root = self.reqTree.AddRoot("WINRARZOR") - self.reqTree.SetPyData(self.root, None) + self.reqTree.SetItemData(self.root, None) self.imageList = wx.ImageList(16, 16) self.reqTree.SetImageList(self.imageList) @@ -32,7 +32,7 @@ class ItemRequirements(wx.Panel): self.Layout() def getFullSkillTree(self, parentSkill, parent, sbIconId): - for skill, level in parentSkill.requiredSkills.iteritems(): + for skill, level in parentSkill.requiredSkills.items(): child = self.reqTree.AppendItem(parent, "%s %s" % (skill.name, self.romanNb[int(level)]), sbIconId) if skill.ID not in self.skillIdHistory: self.getFullSkillTree(skill, child, sbIconId) diff --git a/gui/builtinMarketBrowser/itemView.py b/gui/builtinMarketBrowser/itemView.py index 3c971a699..7a26cfed1 100644 --- a/gui/builtinMarketBrowser/itemView.py +++ b/gui/builtinMarketBrowser/itemView.py @@ -1,5 +1,6 @@ import wx +import config import gui.builtinMarketBrowser.pfSearchBox as SBox from gui.contextMenu import ContextMenu from gui.display import Display @@ -9,7 +10,7 @@ from gui.utils.staticHelpers import DragDropHelper from logbook import Logger -import events +from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES, MAX_RECENTLY_USED_MODULES, ItemSelected pyfalog = Logger(__name__) @@ -66,7 +67,7 @@ class ItemView(Display): row = self.GetFirstSelected() if row != -1: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "market:" + str(self.active[row].ID) pyfalog.debug("Dragging from market: " + dataStr) @@ -89,10 +90,10 @@ class ItemView(Display): for itemID in self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]: self.recentlyUsedModules.add(self.sMkt.getItem(itemID)) - wx.PostEvent(self.mainFrame, events.ItemSelected(itemID=self.active[sel].ID)) + wx.PostEvent(self.mainFrame, ItemSelected(itemID=self.active[sel].ID)) def storeRecentlyUsedMarketItem(self, itemID): - if len(self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]) > events.MAX_RECENTLY_USED_MODULES: + if len(self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"]) > MAX_RECENTLY_USED_MODULES: self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].pop(0) self.sMkt.serviceMarketRecentlyUsedModules["pyfaMarketRecentlyUsedModules"].append(itemID) @@ -103,8 +104,8 @@ class ItemView(Display): sel = self.marketView.GetSelection() if sel.IsOk(): # Get data field of the selected item (which is a marketGroup ID if anything was selected) - seldata = self.marketView.GetPyData(sel) - if seldata is not None and seldata != events.RECENTLY_USED_MODULES: + seldata = self.marketView.GetItemData(sel) + if seldata is not None and seldata != RECENTLY_USED_MODULES: # If market group treeview item doesn't have children (other market groups or dummies), # then it should have items in it and we want to request them if self.marketView.ItemHasChildren(sel) is False: @@ -117,7 +118,7 @@ class ItemView(Display): items = set() else: # If method was called but selection wasn't actually made or we have a hit on recently used modules - if seldata == events.RECENTLY_USED_MODULES: + if seldata == RECENTLY_USED_MODULES: items = self.recentlyUsedModules else: items = set() @@ -126,7 +127,7 @@ class ItemView(Display): self.updateItemStore(items) # Set toggle buttons / use search mode flag if recently used modules category is selected (in order to have all modules listed and not filtered) - if seldata is not events.RECENTLY_USED_MODULES: + if seldata is not RECENTLY_USED_MODULES: self.setToggles() else: self.marketBrowser.searchMode = True @@ -170,10 +171,6 @@ class ItemView(Display): if len(realsearch) == 0: self.selectionMade() return - # Show nothing if query is too short - elif len(realsearch) < 3: - self.clearSearch() - return self.marketBrowser.searchMode = True self.sMkt.searchItems(search, self.populateSearch) @@ -204,13 +201,14 @@ class ItemView(Display): try: mktgrpid = sMkt.getMarketGroupByItem(item).ID except AttributeError: - mktgrpid = None - print("unable to find market group for", item.name) + mktgrpid = -1 + print(("unable to find market group for", item.name)) parentname = sMkt.getParentItemByItem(item).name # Get position of market group metagrpid = sMkt.getMetaGroupIdByItem(item) metatab = self.metaMap.get(metagrpid) metalvl = self.metalvls.get(item.ID, 0) + return catname, mktgrpid, parentname, metatab, metalvl, item.name def contextMenu(self, event): @@ -266,7 +264,7 @@ class ItemView(Display): """ revmap = {} i = 0 - for mgids in self.sMkt.META_MAP.itervalues(): + for mgids in self.sMkt.META_MAP.values(): for mgid in mgids: revmap[mgid] = i i += 1 diff --git a/gui/builtinMarketBrowser/marketTree.py b/gui/builtinMarketBrowser/marketTree.py index c55c7e3b5..8b1286e50 100644 --- a/gui/builtinMarketBrowser/marketTree.py +++ b/gui/builtinMarketBrowser/marketTree.py @@ -1,7 +1,7 @@ import wx from gui.cachingImageList import CachingImageList -import gui.builtinMarketBrowser.events as events +from gui.builtinMarketBrowser.events import RECENTLY_USED_MODULES from logbook import Logger @@ -24,7 +24,7 @@ class MarketTree(wx.TreeCtrl): sMkt = self.sMkt for mktGrp in sMkt.getMarketRoot(): iconId = self.addImage(sMkt.getIconByMarketGroup(mktGrp)) - childId = self.AppendItem(self.root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) + childId = self.AppendItem(self.root, mktGrp.name, iconId, data=mktGrp.ID) # All market groups which were never expanded are dummies, here we assume # that all root market groups are expandable self.AppendItem(childId, "dummy") @@ -32,7 +32,7 @@ class MarketTree(wx.TreeCtrl): # Add recently used modules node rumIconId = self.addImage("market_small", "gui") - self.AppendItem(self.root, "Recently Used Modules", rumIconId, data=wx.TreeItemData(events.RECENTLY_USED_MODULES)) + self.AppendItem(self.root, "Recently Used Modules", rumIconId, data=RECENTLY_USED_MODULES) # Bind our lookup method to when the tree gets expanded self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) @@ -52,14 +52,14 @@ class MarketTree(wx.TreeCtrl): self.Delete(child) # And add real market group contents sMkt = self.sMkt - currentMktGrp = sMkt.getMarketGroup(self.GetPyData(root), eager="children") + currentMktGrp = sMkt.getMarketGroup(self.GetItemData(root), eager="children") for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): # If market should have items but it doesn't, do not show it if sMkt.marketGroupValidityCheck(childMktGrp) is False: continue iconId = self.addImage(sMkt.getIconByMarketGroup(childMktGrp)) try: - childId = self.AppendItem(root, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) + childId = self.AppendItem(root, childMktGrp.name, iconId, data=childMktGrp.ID) except Exception as e: pyfalog.debug("Error appending item.") pyfalog.debug(e) @@ -88,7 +88,7 @@ class MarketTree(wx.TreeCtrl): for i in range(len(jumpList) - 1, -1, -1): target = jumpList[i] child, cookie = self.GetFirstChild(item) - while self.GetItemPyData(child) != target: + while self.GetItemData(child) != target: child, cookie = self.GetNextChild(item, cookie) item = child diff --git a/gui/builtinMarketBrowser/pfSearchBox.py b/gui/builtinMarketBrowser/pfSearchBox.py index f5f8d8c6d..8d0871f6a 100644 --- a/gui/builtinMarketBrowser/pfSearchBox.py +++ b/gui/builtinMarketBrowser/pfSearchBox.py @@ -1,7 +1,7 @@ # noinspection PyPackageRequirements import wx -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils SearchButton, EVT_SEARCH_BTN = wx.lib.newevent.NewEvent() CancelButton, EVT_CANCEL_BTN = wx.lib.newevent.NewEvent() @@ -59,6 +59,8 @@ class PFSearchBox(wx.Window): self.EditBox.Bind(wx.EVT_TEXT, self.OnText) self.EditBox.Bind(wx.EVT_TEXT_ENTER, self.OnTextEnter) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + self.SetMinSize(size) def OnText(self, event): @@ -224,10 +226,10 @@ class PFSearchBox(wx.Window): self.EditBox.SetSize((self.cancelButtonX - self.padding - self.editX, -1)) def OnPaint(self, event): - dc = wx.BufferedPaintDC(self) + dc = wx.AutoBufferedPaintDC(self) - bkColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - sepColor = colorUtils.GetSuitableColor(bkColor, 0.2) + bkColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + sepColor = colorUtils.GetSuitable(bkColor, 0.2) rect = self.GetRect() if self.resized: diff --git a/gui/builtinMarketBrowser/searchBox.py b/gui/builtinMarketBrowser/searchBox.py index 4132f451b..99bfa2983 100644 --- a/gui/builtinMarketBrowser/searchBox.py +++ b/gui/builtinMarketBrowser/searchBox.py @@ -1,5 +1,5 @@ -from gui.bitmapLoader import BitmapLoader -from pfSearchBox import PFSearchBox +from gui.bitmap_loader import BitmapLoader +from .pfSearchBox import PFSearchBox class SearchBox(PFSearchBox): diff --git a/gui/builtinPreferenceViews/__init__.py b/gui/builtinPreferenceViews/__init__.py index 8b61beb13..32117a9ec 100644 --- a/gui/builtinPreferenceViews/__init__.py +++ b/gui/builtinPreferenceViews/__init__.py @@ -1,6 +1,3 @@ -# noinspection PyPackageRequirements -import wx - __all__ = [ "pyfaGeneralPreferences", "pyfaHTMLExportPreferences", @@ -9,8 +6,6 @@ __all__ = [ "pyfaDatabasePreferences", "pyfaLoggingPreferences", "pyfaEnginePreferences", - "pyfaStatViewPreferences", -] + "pyfaEsiPreferences", + "pyfaStatViewPreferences"] -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - __all__.append("pyfaCrestPreferences") diff --git a/gui/builtinPreferenceViews/dummyView.py b/gui/builtinPreferenceViews/dummyView.py index 5bded0d05..959057f11 100644 --- a/gui/builtinPreferenceViews/dummyView.py +++ b/gui/builtinPreferenceViews/dummyView.py @@ -50,7 +50,7 @@ class DummyView(PreferenceView): def initHeader(self, panel): headerSizer = wx.BoxSizer(wx.VERTICAL) - self.stTitle = wx.StaticText(panel, wx.ID_ANY, u"Dummy", wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle = wx.StaticText(panel, wx.ID_ANY, "Dummy", wx.DefaultPosition, wx.DefaultSize, 0) self.stTitle.Wrap(-1) self.stTitle.SetFont(wx.Font(14, 70, 90, 90, False, wx.EmptyString)) headerSizer.Add(self.stTitle, 0, wx.ALL, 5) @@ -60,10 +60,10 @@ class DummyView(PreferenceView): def initContent(self, panel): contentSizer = wx.BoxSizer(wx.VERTICAL) - self.m_checkBox2 = wx.CheckBox(panel, wx.ID_ANY, u"Check Me!", wx.DefaultPosition, wx.DefaultSize, 0) + self.m_checkBox2 = wx.CheckBox(panel, wx.ID_ANY, "Check Me!", wx.DefaultPosition, wx.DefaultSize, 0) contentSizer.Add(self.m_checkBox2, 0, wx.ALL, 5) - self.m_radioBtn2 = wx.RadioButton(panel, wx.ID_ANY, u"RadioBtn", wx.DefaultPosition, wx.DefaultSize, 0) + self.m_radioBtn2 = wx.RadioButton(panel, wx.ID_ANY, "RadioBtn", wx.DefaultPosition, wx.DefaultSize, 0) contentSizer.Add(self.m_radioBtn2, 0, wx.ALL, 5) self.m_slider2 = wx.Slider(panel, wx.ID_ANY, 50, 0, 100, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL) @@ -80,14 +80,13 @@ class DummyView(PreferenceView): def initFooter(self, panel): footerSizer = wx.BoxSizer(wx.HORIZONTAL) - footerSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - - self.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore", wx.DefaultPosition, wx.DefaultSize, 0) + footerSizer.AddStretchSpacer() + self.btnRestore = wx.Button(panel, wx.ID_ANY, "Restore", wx.DefaultPosition, wx.DefaultSize, 0) self.btnRestore.Enable(False) footerSizer.Add(self.btnRestore, 0, wx.ALL, 5) - self.btnApply = wx.Button(panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnApply = wx.Button(panel, wx.ID_ANY, "Apply", wx.DefaultPosition, wx.DefaultSize, 0) footerSizer.Add(self.btnApply, 0, wx.ALL, 5) return footerSizer diff --git a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py index 58ed81556..458424dce 100644 --- a/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py +++ b/gui/builtinPreferenceViews/pyfaContextMenuPreferences.py @@ -1,7 +1,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.mainFrame from service.settings import ContextMenuSettings @@ -23,7 +23,7 @@ class PFContextMenuPref(PreferenceView): mainSizer.Add(self.stTitle, 0, wx.ALL, 5) self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, - u"Disabling context menus can improve responsiveness.", + "Disabling context menus can improve responsiveness.", wx.DefaultPosition, wx.DefaultSize, 0) self.stSubTitle.Wrap(-1) mainSizer.Add(self.stSubTitle, 0, wx.ALL, 5) diff --git a/gui/builtinPreferenceViews/pyfaCrestPreferences.py b/gui/builtinPreferenceViews/pyfaCrestPreferences.py deleted file mode 100644 index 4a9208d0a..000000000 --- a/gui/builtinPreferenceViews/pyfaCrestPreferences.py +++ /dev/null @@ -1,151 +0,0 @@ -# noinspection PyPackageRequirements -import wx - -from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader - -import gui.mainFrame - -from service.settings import CRESTSettings - -# noinspection PyPackageRequirements -from wx.lib.intctrl import IntCtrl - -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - from service.crest import Crest - - -class PFCrestPref(PreferenceView): - title = "CREST" - - def populatePanel(self, panel): - - self.mainFrame = gui.mainFrame.MainFrame.getInstance() - self.settings = CRESTSettings.getInstance() - self.dirtySettings = False - dlgWidth = panel.GetParent().GetParent().ClientSize.width - mainSizer = wx.BoxSizer(wx.VERTICAL) - - self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) - self.stTitle.Wrap(-1) - self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - - mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - - self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) - mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - - self.stInfo = wx.StaticText(panel, wx.ID_ANY, - u"Please see the pyfa wiki on GitHub for information regarding these options.", - wx.DefaultPosition, wx.DefaultSize, 0) - self.stInfo.Wrap(dlgWidth - 50) - mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - - rbSizer = wx.BoxSizer(wx.HORIZONTAL) - self.rbMode = wx.RadioBox(panel, -1, "Mode", wx.DefaultPosition, wx.DefaultSize, - ['Implicit', 'User-supplied details'], 1, wx.RA_SPECIFY_COLS) - self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize, - ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS) - - self.rbMode.SetSelection(self.settings.get('mode')) - self.rbServer.SetSelection(self.settings.get('server')) - - rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5) - rbSizer.Add(self.rbServer, 1, wx.ALL, 5) - - self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) - self.rbServer.Bind(wx.EVT_RADIOBOX, self.OnServerChange) - - mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) - - timeoutSizer = wx.BoxSizer(wx.HORIZONTAL) - - self.stTimout = wx.StaticText(panel, wx.ID_ANY, u"Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0) - self.stTimout.Wrap(-1) - - timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - - self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout')) - timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5) - self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange) - - mainSizer.Add(timeoutSizer, 0, wx.ALL | wx.EXPAND, 0) - - detailsTitle = wx.StaticText(panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0) - detailsTitle.Wrap(-1) - detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) - - mainSizer.Add(detailsTitle, 0, wx.ALL, 5) - mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, - wx.EXPAND, 5) - - fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) - fgAddrSizer.AddGrowableCol(1) - fgAddrSizer.SetFlexibleDirection(wx.BOTH) - fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - - self.stSetID = wx.StaticText(panel, wx.ID_ANY, u"Client ID:", wx.DefaultPosition, wx.DefaultSize, 0) - self.stSetID.Wrap(-1) - fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - - self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, - wx.DefaultSize, 0) - - fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - - self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, u"Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0) - self.stSetSecret.Wrap(-1) - - fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - - self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, - wx.DefaultSize, 0) - - fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - - self.btnApply = wx.Button(panel, wx.ID_ANY, u"Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0) - self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) - - mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) - mainSizer.Add(self.btnApply, 0, wx.ALIGN_RIGHT, 5) - - self.ToggleProxySettings(self.settings.get('mode')) - - panel.SetSizer(mainSizer) - panel.Layout() - - def OnTimeoutChange(self, event): - self.settings.set('timeout', event.GetEventObject().GetValue()) - - def OnModeChange(self, event): - self.settings.set('mode', event.GetInt()) - self.ToggleProxySettings(self.settings.get('mode')) - Crest.restartService() - - def OnServerChange(self, event): - self.settings.set('server', event.GetInt()) - Crest.restartService() - - def OnBtnApply(self, event): - self.settings.set('clientID', self.inputClientID.GetValue().strip()) - self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip()) - sCrest = Crest.getInstance() - sCrest.delAllCharacters() - - def ToggleProxySettings(self, mode): - if mode: - self.stSetID.Enable() - self.inputClientID.Enable() - self.stSetSecret.Enable() - self.inputClientSecret.Enable() - else: - self.stSetID.Disable() - self.inputClientID.Disable() - self.stSetSecret.Disable() - self.inputClientSecret.Disable() - - def getImage(self): - return BitmapLoader.getBitmap("eve", "gui") - - -PFCrestPref.register() diff --git a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py index ab93d4655..5ecdf9570 100644 --- a/gui/builtinPreferenceViews/pyfaDatabasePreferences.py +++ b/gui/builtinPreferenceViews/pyfaDatabasePreferences.py @@ -1,7 +1,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils import helpers_wxPython as wxHelpers import config from eos.db.saveddata.queries import clearPrices, clearDamagePatterns, clearTargetResists @@ -24,7 +24,7 @@ class PFGeneralPref(PreferenceView): self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, u"(Cannot be changed while pyfa is running. Set via command line switches.)", + self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, "(Cannot be changed while pyfa is running. Set via command line switches.)", wx.DefaultPosition, wx.DefaultSize, 0) self.stSubTitle.Wrap(-1) mainSizer.Add(self.stSubTitle, 0, wx.ALL, 3) @@ -33,11 +33,11 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) # Save in Root - self.cbsaveInRoot = wx.CheckBox(panel, wx.ID_ANY, u"Using Executable Path for Saved Fit Database and Settings", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbsaveInRoot = wx.CheckBox(panel, wx.ID_ANY, "Using Executable Path for Saved Fit Database and Settings", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbsaveInRoot, 0, wx.ALL | wx.EXPAND, 5) # Database path - self.stSetUserPath = wx.StaticText(panel, wx.ID_ANY, u"pyfa User Path:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stSetUserPath = wx.StaticText(panel, wx.ID_ANY, "pyfa User Path:", wx.DefaultPosition, wx.DefaultSize, 0) self.stSetUserPath.Wrap(-1) mainSizer.Add(self.stSetUserPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.inputUserPath = wx.TextCtrl(panel, wx.ID_ANY, config.savePath, wx.DefaultPosition, wx.DefaultSize, 0) @@ -46,7 +46,7 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.inputUserPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) # Save DB - self.stFitDB = wx.StaticText(panel, wx.ID_ANY, u"Fitting Database:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stFitDB = wx.StaticText(panel, wx.ID_ANY, "Fitting Database:", wx.DefaultPosition, wx.DefaultSize, 0) self.stFitDB.Wrap(-1) mainSizer.Add(self.stFitDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -56,7 +56,7 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.inputFitDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) # Game Data DB - self.stGameDB = wx.StaticText(panel, wx.ID_ANY, u"Game Database:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stGameDB = wx.StaticText(panel, wx.ID_ANY, "Game Database:", wx.DefaultPosition, wx.DefaultSize, 0) self.stGameDB.Wrap(-1) mainSizer.Add(self.stGameDB, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -76,15 +76,15 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.m_staticline3, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) btnSizer = wx.BoxSizer(wx.VERTICAL) - btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + btnSizer.AddStretchSpacer() - self.btnDeleteDamagePatterns = wx.Button(panel, wx.ID_ANY, u"Delete All Damage Pattern Profiles", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnDeleteDamagePatterns = wx.Button(panel, wx.ID_ANY, "Delete All Damage Pattern Profiles", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnDeleteDamagePatterns, 0, wx.ALL, 5) - self.btnDeleteTargetResists = wx.Button(panel, wx.ID_ANY, u"Delete All Target Resist Profiles", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnDeleteTargetResists = wx.Button(panel, wx.ID_ANY, "Delete All Target Resist Profiles", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnDeleteTargetResists, 0, wx.ALL, 5) - self.btnPrices = wx.Button(panel, wx.ID_ANY, u"Delete All Prices", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnPrices = wx.Button(panel, wx.ID_ANY, "Delete All Prices", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnPrices, 0, wx.ALL, 5) mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) @@ -97,17 +97,17 @@ class PFGeneralPref(PreferenceView): panel.Layout() def DeleteDamagePatterns(self, event): - question = u"This is a destructive action that will delete all damage pattern profiles.\nAre you sure you want to do this?" + question = "This is a destructive action that will delete all damage pattern profiles.\nAre you sure you want to do this?" if wxHelpers.YesNoDialog(question, "Confirm"): clearDamagePatterns() def DeleteTargetResists(self, event): - question = u"This is a destructive action that will delete all target resist profiles.\nAre you sure you want to do this?" + question = "This is a destructive action that will delete all target resist profiles.\nAre you sure you want to do this?" if wxHelpers.YesNoDialog(question, "Confirm"): clearTargetResists() def DeletePrices(self, event): - question = u"This is a destructive action that will delete all cached prices out of the database.\nAre you sure you want to do this?" + question = "This is a destructive action that will delete all cached prices out of the database.\nAre you sure you want to do this?" if wxHelpers.YesNoDialog(question, "Confirm"): clearPrices() diff --git a/gui/builtinPreferenceViews/pyfaEnginePreferences.py b/gui/builtinPreferenceViews/pyfaEnginePreferences.py index f9fba7612..815e1b124 100644 --- a/gui/builtinPreferenceViews/pyfaEnginePreferences.py +++ b/gui/builtinPreferenceViews/pyfaEnginePreferences.py @@ -3,7 +3,7 @@ import logging import wx from service.fit import Fit -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.preferenceView import PreferenceView from service.settings import EOSSettings @@ -24,7 +24,7 @@ class PFFittingEnginePref(PreferenceView): mainSizer = wx.BoxSizer(wx.VERTICAL) - helpCursor = wx.StockCursor(wx.CURSOR_QUESTION_ARROW) + helpCursor = wx.Cursor(wx.CURSOR_QUESTION_ARROW) self.engine_settings = EOSSettings.getInstance() @@ -36,25 +36,25 @@ class PFFittingEnginePref(PreferenceView): self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.cbGlobalForceReload = wx.CheckBox(panel, wx.ID_ANY, u"Factor in reload time when calculating capacitor usage, damage, and tank.", + self.cbGlobalForceReload = wx.CheckBox(panel, wx.ID_ANY, "Factor in reload time when calculating capacitor usage, damage, and tank.", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbGlobalForceReload, 0, wx.ALL | wx.EXPAND, 5) self.cbStrictSkillLevels = wx.CheckBox(panel, wx.ID_ANY, - u"Enforce strict skill level requirements", + "Enforce strict skill level requirements", wx.DefaultPosition, wx.DefaultSize, 0) self.cbStrictSkillLevels.SetCursor(helpCursor) self.cbStrictSkillLevels.SetToolTip(wx.ToolTip( - u'When enabled, skills will check their dependencies\' requirements when their levels change and reset ' + - u'skills that no longer meet the requirement.\neg: Setting Drones from level V to IV will reset the Heavy ' + - u'Drone Operation skill, as that requires Drones V')) + 'When enabled, skills will check their dependencies\' requirements when their levels change and reset ' + + 'skills that no longer meet the requirement.\neg: Setting Drones from level V to IV will reset the Heavy ' + + 'Drone Operation skill, as that requires Drones V')) mainSizer.Add(self.cbStrictSkillLevels, 0, wx.ALL | wx.EXPAND, 5) self.cbUniversalAdaptiveArmorHardener = wx.CheckBox(panel, wx.ID_ANY, - u"When damage profile is Uniform, set Reactive Armor " + - u"Hardener to match (old behavior).", + "When damage profile is Uniform, set Reactive Armor " + + "Hardener to match (old behavior).", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbUniversalAdaptiveArmorHardener, 0, wx.ALL | wx.EXPAND, 5) diff --git a/gui/builtinPreferenceViews/pyfaEsiPreferences.py b/gui/builtinPreferenceViews/pyfaEsiPreferences.py new file mode 100644 index 000000000..029003a83 --- /dev/null +++ b/gui/builtinPreferenceViews/pyfaEsiPreferences.py @@ -0,0 +1,137 @@ +# noinspection PyPackageRequirements +import wx + +from gui.preferenceView import PreferenceView +from gui.bitmap_loader import BitmapLoader + +import gui.mainFrame + +from service.settings import EsiSettings + +# noinspection PyPackageRequirements +from wx.lib.intctrl import IntCtrl + +from service.esi import Esi + + +class PFEsiPref(PreferenceView): + title = "EVE SSO" + + def populatePanel(self, panel): + + self.mainFrame = gui.mainFrame.MainFrame.getInstance() + self.settings = EsiSettings.getInstance() + self.dirtySettings = False + dlgWidth = panel.GetParent().GetParent().ClientSize.width + mainSizer = wx.BoxSizer(wx.VERTICAL) + + self.stTitle = wx.StaticText(panel, wx.ID_ANY, self.title, wx.DefaultPosition, wx.DefaultSize, 0) + self.stTitle.Wrap(-1) + self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) + + mainSizer.Add(self.stTitle, 0, wx.ALL, 5) + + self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) + mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + self.stInfo = wx.StaticText(panel, wx.ID_ANY, + "Please see the pyfa wiki on GitHub for information regarding these options.", + wx.DefaultPosition, wx.DefaultSize, 0) + self.stInfo.Wrap(dlgWidth - 50) + mainSizer.Add(self.stInfo, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + rbSizer = wx.BoxSizer(wx.HORIZONTAL) + self.rbMode = wx.RadioBox(panel, -1, "Login Authentication Method", wx.DefaultPosition, wx.DefaultSize, + ['Local Server', 'Manual'], 1, wx.RA_SPECIFY_COLS) + self.rbMode.SetItemToolTip(0, "This options starts a local webserver that the web application will call back to with information about the character login.") + self.rbMode.SetItemToolTip(1, "This option prompts users to copy and paste information from the web application to allow for character login. Use this if having issues with the local server.") + # self.rbServer = wx.RadioBox(panel, -1, "Server", wx.DefaultPosition, wx.DefaultSize, + # ['Tranquility', 'Singularity'], 1, wx.RA_SPECIFY_COLS) + + self.rbMode.SetSelection(self.settings.get('loginMode')) + # self.rbServer.SetSelection(self.settings.get('server')) + + rbSizer.Add(self.rbMode, 1, wx.TOP | wx.RIGHT, 5) + # rbSizer.Add(self.rbServer, 1, wx.ALL, 5) + + self.rbMode.Bind(wx.EVT_RADIOBOX, self.OnModeChange) + # self.rbServer.Bind(wx.EVT_RADIOBOX, self.OnServerChange) + + mainSizer.Add(rbSizer, 1, wx.ALL | wx.EXPAND, 0) + + timeoutSizer = wx.BoxSizer(wx.HORIZONTAL) + + # self.stTimout = wx.StaticText(panel, wx.ID_ANY, "Timeout (seconds):", wx.DefaultPosition, wx.DefaultSize, 0) + # self.stTimout.Wrap(-1) + # + # timeoutSizer.Add(self.stTimout, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + + # self.intTimeout = IntCtrl(panel, max=300000, limited=True, value=self.settings.get('timeout')) + # timeoutSizer.Add(self.intTimeout, 0, wx.ALL, 5) + # self.intTimeout.Bind(wx.lib.intctrl.EVT_INT, self.OnTimeoutChange) + # + # mainSizer.Add(timeoutSizer, 0, wx.ALL | wx.EXPAND, 0) + + # detailsTitle = wx.StaticText(panel, wx.ID_ANY, "CREST client details", wx.DefaultPosition, wx.DefaultSize, 0) + # detailsTitle.Wrap(-1) + # detailsTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) + # + # mainSizer.Add(detailsTitle, 0, wx.ALL, 5) + # mainSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, + # wx.EXPAND, 5) + + # fgAddrSizer = wx.FlexGridSizer(2, 2, 0, 0) + # fgAddrSizer.AddGrowableCol(1) + # fgAddrSizer.SetFlexibleDirection(wx.BOTH) + # fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) + # + # self.stSetID = wx.StaticText(panel, wx.ID_ANY, "Client ID:", wx.DefaultPosition, wx.DefaultSize, 0) + # self.stSetID.Wrap(-1) + # fgAddrSizer.Add(self.stSetID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + # + # self.inputClientID = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientID'), wx.DefaultPosition, + # wx.DefaultSize, 0) + # + # fgAddrSizer.Add(self.inputClientID, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) + # + # self.stSetSecret = wx.StaticText(panel, wx.ID_ANY, "Client Secret:", wx.DefaultPosition, wx.DefaultSize, 0) + # self.stSetSecret.Wrap(-1) + # + # fgAddrSizer.Add(self.stSetSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + # + # self.inputClientSecret = wx.TextCtrl(panel, wx.ID_ANY, self.settings.get('clientSecret'), wx.DefaultPosition, + # wx.DefaultSize, 0) + # + # fgAddrSizer.Add(self.inputClientSecret, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) + # + # self.btnApply = wx.Button(panel, wx.ID_ANY, "Save Client Settings", wx.DefaultPosition, wx.DefaultSize, 0) + # self.btnApply.Bind(wx.EVT_BUTTON, self.OnBtnApply) + # + # mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) + # mainSizer.Add(self.btnApply, 0, wx.ALIGN_RIGHT, 5) + + # self.ToggleProxySettings(self.settings.get('loginMode')) + + panel.SetSizer(mainSizer) + panel.Layout() + + def OnTimeoutChange(self, event): + self.settings.set('timeout', event.GetEventObject().GetValue()) + + def OnModeChange(self, event): + self.settings.set('loginMode', event.GetInt()) + + def OnServerChange(self, event): + self.settings.set('server', event.GetInt()) + + def OnBtnApply(self, event): + self.settings.set('clientID', self.inputClientID.GetValue().strip()) + self.settings.set('clientSecret', self.inputClientSecret.GetValue().strip()) + sEsi = Esi.getInstance() + sEsi.delAllCharacters() + + def getImage(self): + return BitmapLoader.getBitmap("eve", "gui") + + +PFEsiPref.register() diff --git a/gui/builtinPreferenceViews/pyfaGaugePreferences.py b/gui/builtinPreferenceViews/pyfaGaugePreferences.py index 5f6509dc0..c0ca68a98 100644 --- a/gui/builtinPreferenceViews/pyfaGaugePreferences.py +++ b/gui/builtinPreferenceViews/pyfaGaugePreferences.py @@ -1,15 +1,14 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - # noinspection PyPackageRequirements import wx import copy from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader -from gui.utils import colorUtils -import gui.utils.drawUtils as drawUtils +from gui.bitmap_loader import BitmapLoader +from gui.utils.color import CalculateTransition +import gui.utils.draw as drawUtils ########################################################################### @@ -49,6 +48,7 @@ class PFGaugePreview(wx.Window): self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) self.Bind(wx.EVT_TIMER, self.OnTimer) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) def OnEraseBk(self, event): pass @@ -109,44 +109,45 @@ class PFGaugePreview(wx.Window): self.Refresh() def OnPaint(self, event): - rect = self.GetClientRect() - dc = wx.BufferedPaintDC(self) - dc.SetBackground(wx.Brush(self.bkColor)) - dc.Clear() - - value = float(self.value) - if self.percS >= 100: - w = rect.width - else: - w = rect.width * (float(value) / 100) - r = copy.copy(rect) - r.width = w - - color = colorUtils.CalculateTransitionColor(self.colorS, self.colorE, float(value) / 100) - if self.gradientStart > 0: - gcolor = colorUtils.BrightenColor(color, float(self.gradientStart) / 100) - gMid = colorUtils.BrightenColor(color, float(self.gradientStart / 2) / 100) - else: - gcolor = colorUtils.DarkenColor(color, float(-self.gradientStart) / 100) - gMid = colorUtils.DarkenColor(color, float(-self.gradientStart / 2) / 100) - - gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) - dc.DrawBitmap(gBmp, 0, 0) - dc.SetFont(self.font) - - r = copy.copy(rect) - r.left += 1 - r.top += 1 - - formatStr = "{0:." + str(self._fractionDigits) + "f}%" - value = (self.percE - self.percS) * value / (self.percE - self.percS) - value = self.percS + (self.percE - self.percS) * value / 100 - - dc.SetTextForeground(wx.Colour(80, 80, 80)) - dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) - - dc.SetTextForeground(wx.Colour(255, 255, 255)) - dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) + pass + # rect = self.GetClientRect() + # dc = wx.AutoBufferedPaintDC(self) + # dc.SetBackground(wx.Brush(self.bkColor)) + # dc.Clear() + # + # value = float(self.value) + # if self.percS >= 100: + # w = rect.width + # else: + # w = rect.width * (float(value) / 100) + # r = copy.copy(rect) + # r.width = w + # + # color = CalculateTransitionColor(self.colorS, self.colorE, float(value) / 100) + # if self.gradientStart > 0: + # gcolor = color.BrightenColor(color, float(self.gradientStart) / 100) + # gMid = color.BrightenColor(color, float(self.gradientStart / 2) / 100) + # else: + # gcolor = color.DarkenColor(color, float(-self.gradientStart) / 100) + # gMid = color.DarkenColor(color, float(-self.gradientStart / 2) / 100) + # + # gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) + # dc.DrawBitmap(gBmp, 0, 0) + # dc.SetFont(self.font) + # + # r = copy.copy(rect) + # r.left += 1 + # r.top += 1 + # + # formatStr = "{0:." + str(self._fractionDigits) + "f}%" + # value = (self.percE - self.percS) * value / (self.percE - self.percS) + # value = self.percS + (self.percE - self.percS) * value / 100 + # + # dc.SetTextForeground(wx.Colour(80, 80, 80)) + # dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) + # + # dc.SetTextForeground(wx.Colour(255, 255, 255)) + # dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) class PFGaugePref(PreferenceView): @@ -160,7 +161,7 @@ class PFGaugePref(PreferenceView): gSizer1 = wx.BoxSizer(wx.HORIZONTAL) - self.st0100 = wx.StaticText(panel, wx.ID_ANY, u"0 - 100", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) + self.st0100 = wx.StaticText(panel, wx.ID_ANY, "0 - 100", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) self.st0100.Wrap(-1) gSizer1.Add(self.st0100, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -185,7 +186,7 @@ class PFGaugePref(PreferenceView): gSizer2 = wx.BoxSizer(wx.HORIZONTAL) - self.st100101 = wx.StaticText(panel, wx.ID_ANY, u"100 - 101", wx.DefaultPosition, wx.DefaultSize, + self.st100101 = wx.StaticText(panel, wx.ID_ANY, "100 - 101", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) self.st100101.Wrap(-1) gSizer2.Add(self.st100101, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -211,7 +212,7 @@ class PFGaugePref(PreferenceView): gSizer3 = wx.BoxSizer(wx.HORIZONTAL) - self.st101103 = wx.StaticText(panel, wx.ID_ANY, u"101 - 103", wx.DefaultPosition, wx.DefaultSize, + self.st101103 = wx.StaticText(panel, wx.ID_ANY, "101 - 103", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) self.st101103.Wrap(-1) gSizer3.Add(self.st101103, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -237,7 +238,7 @@ class PFGaugePref(PreferenceView): gSizer4 = wx.BoxSizer(wx.HORIZONTAL) - self.st103105 = wx.StaticText(panel, wx.ID_ANY, u"103 - 105", wx.DefaultPosition, wx.DefaultSize, + self.st103105 = wx.StaticText(panel, wx.ID_ANY, "103 - 105", wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_RIGHT) self.st103105.Wrap(-1) gSizer4.Add(self.st103105, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -284,20 +285,20 @@ class PFGaugePref(PreferenceView): buttonsSizer = wx.BoxSizer(wx.HORIZONTAL) - self.cbLink = wx.CheckBox(panel, wx.ID_ANY, u"Link Colors", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbLink = wx.CheckBox(panel, wx.ID_ANY, "Link Colors", wx.DefaultPosition, wx.DefaultSize, 0) buttonsSizer.Add(self.cbLink, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.LEFT, 5) self.sliderGradientStart = wx.Slider(panel, wx.ID_ANY, self.gradientStart, -100, 100, wx.DefaultPosition, (127, -1), wx.SL_HORIZONTAL | wx.SL_LABELS) buttonsSizer.Add(self.sliderGradientStart, 1, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnRestore = wx.Button(panel, wx.ID_ANY, u"Restore Defaults", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnRestore = wx.Button(panel, wx.ID_ANY, "Restore Defaults", wx.DefaultPosition, wx.DefaultSize, 0) buttonsSizer.Add(self.btnRestore, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnDump = wx.Button(panel, wx.ID_ANY, u"Dump Colors", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnDump = wx.Button(panel, wx.ID_ANY, "Dump Colors", wx.DefaultPosition, wx.DefaultSize, 0) buttonsSizer.Add(self.btnDump, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnOk = wx.Button(panel, wx.ID_ANY, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnOk = wx.Button(panel, wx.ID_ANY, "Apply", wx.DefaultPosition, wx.DefaultSize, 0) buttonsSizer.Add(self.btnOk, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) footerSizer.Add(buttonsSizer, 1, wx.ALIGN_RIGHT, 5) @@ -432,11 +433,11 @@ class PFGaugePref(PreferenceView): event.Skip() def DumpColours(self, event): - print("Gradient start: %d" % self.sliderGradientStart.GetValue()) - print(" 0 <-> 100 Start: ", self.c0100S, " End: ", self.c0100E) - print("100 <-> 101 Start: ", self.c100101S, " End: ", self.c100101E) - print("101 <-> 103 Start: ", self.c101103S, " End: ", self.c101103E) - print("103 <-> 105 Start: ", self.c103105S, " End: ", self.c103105E) + print(("Gradient start: %d" % self.sliderGradientStart.GetValue())) + print((" 0 <-> 100 Start: ", self.c0100S, " End: ", self.c0100E)) + print(("100 <-> 101 Start: ", self.c100101S, " End: ", self.c100101E)) + print(("101 <-> 103 Start: ", self.c101103S, " End: ", self.c101103E)) + print(("103 <-> 105 Start: ", self.c103105S, " End: ", self.c103105E)) event.Skip() diff --git a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py index 6f1acaab1..412e1a988 100644 --- a/gui/builtinPreferenceViews/pyfaGeneralPreferences.py +++ b/gui/builtinPreferenceViews/pyfaGeneralPreferences.py @@ -3,7 +3,7 @@ import wx from wx.lib.intctrl import IntCtrl from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.mainFrame import gui.globalEvents as GE @@ -21,7 +21,7 @@ class PFGeneralPref(PreferenceView): self.openFitsSettings = SettingsProvider.getInstance().getSettings("pyfaPrevOpenFits", {"enabled": False, "pyfaOpenFits": []}) - helpCursor = wx.StockCursor(wx.CURSOR_QUESTION_ARROW) + helpCursor = wx.Cursor(wx.CURSOR_QUESTION_ARROW) mainSizer = wx.BoxSizer(wx.VERTICAL) @@ -34,59 +34,59 @@ class PFGeneralPref(PreferenceView): self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, u"Use global character", wx.DefaultPosition, wx.DefaultSize, + self.cbGlobalChar = wx.CheckBox(panel, wx.ID_ANY, "Use global character", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbGlobalChar, 0, wx.ALL | wx.EXPAND, 5) - self.cbGlobalDmgPattern = wx.CheckBox(panel, wx.ID_ANY, u"Use global damage pattern", wx.DefaultPosition, + self.cbGlobalDmgPattern = wx.CheckBox(panel, wx.ID_ANY, "Use global damage pattern", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbGlobalDmgPattern, 0, wx.ALL | wx.EXPAND, 5) - self.cbCompactSkills = wx.CheckBox(panel, wx.ID_ANY, u"Compact skills needed tooltip", wx.DefaultPosition, + self.cbCompactSkills = wx.CheckBox(panel, wx.ID_ANY, "Compact skills needed tooltip", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbCompactSkills, 0, wx.ALL | wx.EXPAND, 5) - self.cbFitColorSlots = wx.CheckBox(panel, wx.ID_ANY, u"Color fitting view by slot", wx.DefaultPosition, + self.cbFitColorSlots = wx.CheckBox(panel, wx.ID_ANY, "Color fitting view by slot", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbFitColorSlots, 0, wx.ALL | wx.EXPAND, 5) - self.cbReopenFits = wx.CheckBox(panel, wx.ID_ANY, u"Reopen previous fits on startup", wx.DefaultPosition, + self.cbReopenFits = wx.CheckBox(panel, wx.ID_ANY, "Reopen previous fits on startup", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbReopenFits, 0, wx.ALL | wx.EXPAND, 5) - self.cbRackSlots = wx.CheckBox(panel, wx.ID_ANY, u"Separate Racks", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbRackSlots = wx.CheckBox(panel, wx.ID_ANY, "Separate Racks", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbRackSlots, 0, wx.ALL | wx.EXPAND, 5) labelSizer = wx.BoxSizer(wx.VERTICAL) - self.cbRackLabels = wx.CheckBox(panel, wx.ID_ANY, u"Show Rack Labels", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbRackLabels = wx.CheckBox(panel, wx.ID_ANY, "Show Rack Labels", wx.DefaultPosition, wx.DefaultSize, 0) labelSizer.Add(self.cbRackLabels, 0, wx.ALL | wx.EXPAND, 5) mainSizer.Add(labelSizer, 0, wx.LEFT | wx.EXPAND, 30) - self.cbShowTooltip = wx.CheckBox(panel, wx.ID_ANY, u"Show tab tooltips", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbShowTooltip = wx.CheckBox(panel, wx.ID_ANY, "Show tab tooltips", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbShowTooltip, 0, wx.ALL | wx.EXPAND, 5) - self.cbMarketShortcuts = wx.CheckBox(panel, wx.ID_ANY, u"Show market shortcuts", wx.DefaultPosition, + self.cbMarketShortcuts = wx.CheckBox(panel, wx.ID_ANY, "Show market shortcuts", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbMarketShortcuts, 0, wx.ALL | wx.EXPAND, 5) - self.cbGaugeAnimation = wx.CheckBox(panel, wx.ID_ANY, u"Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbGaugeAnimation = wx.CheckBox(panel, wx.ID_ANY, "Animate gauges", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbGaugeAnimation, 0, wx.ALL | wx.EXPAND, 5) - self.cbExportCharges = wx.CheckBox(panel, wx.ID_ANY, u"Export loaded charges", wx.DefaultPosition, + self.cbExportCharges = wx.CheckBox(panel, wx.ID_ANY, "Export loaded charges", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbExportCharges, 0, wx.ALL | wx.EXPAND, 5) - self.cbOpenFitInNew = wx.CheckBox(panel, wx.ID_ANY, u"Open fittings in a new page by default", + self.cbOpenFitInNew = wx.CheckBox(panel, wx.ID_ANY, "Open fittings in a new page by default", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbOpenFitInNew, 0, wx.ALL | wx.EXPAND, 5) - self.cbShowShipBrowserTooltip = wx.CheckBox(panel, wx.ID_ANY, u"Show ship browser tooltip", + self.cbShowShipBrowserTooltip = wx.CheckBox(panel, wx.ID_ANY, "Show ship browser tooltip", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbShowShipBrowserTooltip, 0, wx.ALL | wx.EXPAND, 5) priceSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stDefaultSystem = wx.StaticText(panel, wx.ID_ANY, u"Default Market Prices:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stDefaultSystem = wx.StaticText(panel, wx.ID_ANY, "Default Market Prices:", wx.DefaultPosition, wx.DefaultSize, 0) self.stDefaultSystem.Wrap(-1) priceSizer.Add(self.stDefaultSystem, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.stDefaultSystem.SetCursor(helpCursor) @@ -95,7 +95,7 @@ class PFGeneralPref(PreferenceView): 'source fails. The system you choose is absolute and requests will not be made against other systems.')) self.chPriceSource = wx.Choice(panel, choices=sorted(Price.sources.keys())) - self.chPriceSystem = wx.Choice(panel, choices=Price.systemsList.keys()) + self.chPriceSystem = wx.Choice(panel, choices=list(Price.systemsList.keys())) priceSizer.Add(self.chPriceSource, 1, wx.ALL | wx.EXPAND, 5) priceSizer.Add(self.chPriceSystem, 1, wx.ALL | wx.EXPAND, 5) @@ -103,7 +103,7 @@ class PFGeneralPref(PreferenceView): delayTimer = wx.BoxSizer(wx.HORIZONTAL) - self.stMarketDelay = wx.StaticText(panel, wx.ID_ANY, u"Market Search Delay (ms):", wx.DefaultPosition, wx.DefaultSize, 0) + self.stMarketDelay = wx.StaticText(panel, wx.ID_ANY, "Market Search Delay (ms):", wx.DefaultPosition, wx.DefaultSize, 0) self.stMarketDelay.Wrap(-1) self.stMarketDelay.SetCursor(helpCursor) self.stMarketDelay.SetToolTip( diff --git a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py index a60cc24df..d73cc8e58 100644 --- a/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py +++ b/gui/builtinPreferenceViews/pyfaHTMLExportPreferences.py @@ -3,11 +3,12 @@ import wx import os from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.mainFrame from service.settings import HTMLExportSettings +import wx.lib.agw.hyperlink class PFHTMLExportPref(PreferenceView): @@ -37,10 +38,9 @@ class PFHTMLExportPref(PreferenceView): self.stDesc.Wrap(dlgWidth - 50) mainSizer.Add(self.stDesc, 0, wx.ALL, 5) - self.PathLinkCtrl = wx.HyperlinkCtrl(panel, wx.ID_ANY, self.HTMLExportSettings.getPath(), - u'file:///{}'.format(self.HTMLExportSettings.getPath()), + self.PathLinkCtrl = wx.lib.agw.hyperlink.HyperLinkCtrl(panel, wx.ID_ANY, self.HTMLExportSettings.getPath(), wx.DefaultPosition, wx.DefaultSize, - wx.HL_ALIGN_LEFT | wx.NO_BORDER | wx.HL_CONTEXTMENU) + URL='file:///{}'.format(self.HTMLExportSettings.getPath()),) mainSizer.Add(self.PathLinkCtrl, 0, wx.ALL | wx.EXPAND, 5) self.fileSelectDialog = wx.FileDialog(None, "Save Fitting As...", @@ -56,7 +56,7 @@ class PFHTMLExportPref(PreferenceView): self.stDesc4.Wrap(dlgWidth - 50) mainSizer.Add(self.stDesc4, 0, wx.ALL, 5) - self.exportMinimal = wx.CheckBox(panel, wx.ID_ANY, u"Enable minimal format", wx.DefaultPosition, + self.exportMinimal = wx.CheckBox(panel, wx.ID_ANY, "Enable minimal format", wx.DefaultPosition, wx.DefaultSize, 0) self.exportMinimal.SetValue(self.HTMLExportSettings.getMinimalEnabled()) self.exportMinimal.Bind(wx.EVT_CHECKBOX, self.OnMinimalEnabledChange) @@ -67,7 +67,7 @@ class PFHTMLExportPref(PreferenceView): def setPathLinkCtrlValues(self, path): self.PathLinkCtrl.SetLabel(self.HTMLExportSettings.getPath()) - self.PathLinkCtrl.SetURL(u'file:///{}'.format(self.HTMLExportSettings.getPath())) + self.PathLinkCtrl.SetURL('file:///{}'.format(self.HTMLExportSettings.getPath())) self.PathLinkCtrl.SetSize(wx.DefaultSize) self.PathLinkCtrl.Refresh() diff --git a/gui/builtinPreferenceViews/pyfaLoggingPreferences.py b/gui/builtinPreferenceViews/pyfaLoggingPreferences.py index 5a7ffccc1..6de36df1c 100644 --- a/gui/builtinPreferenceViews/pyfaLoggingPreferences.py +++ b/gui/builtinPreferenceViews/pyfaLoggingPreferences.py @@ -1,7 +1,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import config from logbook import Logger @@ -25,7 +25,7 @@ class PFGeneralPref(PreferenceView): self.stTitle.SetFont(wx.Font(12, 70, 90, 90, False, wx.EmptyString)) mainSizer.Add(self.stTitle, 0, wx.ALL, 5) - self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, u"(Cannot be changed while pyfa is running. Set via command line switches.)", + self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, "(Cannot be changed while pyfa is running. Set via command line switches.)", wx.DefaultPosition, wx.DefaultSize, 0) self.stSubTitle.Wrap(-1) mainSizer.Add(self.stSubTitle, 0, wx.ALL, 3) @@ -34,7 +34,7 @@ class PFGeneralPref(PreferenceView): mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) # Database path - self.stLogPath = wx.StaticText(panel, wx.ID_ANY, u"Log file location:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stLogPath = wx.StaticText(panel, wx.ID_ANY, "Log file location:", wx.DefaultPosition, wx.DefaultSize, 0) self.stLogPath.Wrap(-1) mainSizer.Add(self.stLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) self.inputLogPath = wx.TextCtrl(panel, wx.ID_ANY, config.logPath, wx.DefaultPosition, wx.DefaultSize, 0) @@ -42,14 +42,23 @@ class PFGeneralPref(PreferenceView): self.inputLogPath.SetBackgroundColour((200, 200, 200)) mainSizer.Add(self.inputLogPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) + import requests + self.certPath = wx.StaticText(panel, wx.ID_ANY, "Cert Path:", wx.DefaultPosition, wx.DefaultSize, 0) + self.certPath .Wrap(-1) + mainSizer.Add(self.certPath, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) + self.certPathCtrl = wx.TextCtrl(panel, wx.ID_ANY, requests.certs.where(), wx.DefaultPosition, wx.DefaultSize, 0) + self.certPathCtrl.SetEditable(False) + self.certPathCtrl.SetBackgroundColour((200, 200, 200)) + mainSizer.Add(self.certPathCtrl, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) + # Debug Logging - self.cbdebugLogging = wx.CheckBox(panel, wx.ID_ANY, u"Debug Logging Enabled", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbdebugLogging = wx.CheckBox(panel, wx.ID_ANY, "Debug Logging Enabled", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbdebugLogging, 0, wx.ALL | wx.EXPAND, 5) - self.stDumpLogs = wx.StaticText(panel, wx.ID_ANY, u"Pressing this button will cause all logs in memory to write to the log file:", + self.stDumpLogs = wx.StaticText(panel, wx.ID_ANY, "Pressing this button will cause all logs in memory to write to the log file:", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.stDumpLogs, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.btnDumpLogs = wx.Button(panel, wx.ID_ANY, u"Dump All Logs", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnDumpLogs = wx.Button(panel, wx.ID_ANY, "Dump All Logs", wx.DefaultPosition, wx.DefaultSize, 0) self.btnDumpLogs.Bind(wx.EVT_BUTTON, OnDumpLogs) mainSizer.Add(self.btnDumpLogs, 0, wx.ALIGN_LEFT, 5) diff --git a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py index e20f8a01d..4f9887f8a 100644 --- a/gui/builtinPreferenceViews/pyfaNetworkPreferences.py +++ b/gui/builtinPreferenceViews/pyfaNetworkPreferences.py @@ -2,7 +2,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.mainFrame from service.settings import NetworkSettings @@ -30,18 +30,18 @@ class PFNetworkPref(PreferenceView): self.m_staticline1 = wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL) mainSizer.Add(self.m_staticline1, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) - self.cbEnableNetwork = wx.CheckBox(panel, wx.ID_ANY, u"Enable Network", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbEnableNetwork = wx.CheckBox(panel, wx.ID_ANY, "Enable Network", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.cbEnableNetwork, 0, wx.ALL | wx.EXPAND, 5) subSizer = wx.BoxSizer(wx.VERTICAL) - self.cbEve = wx.CheckBox(panel, wx.ID_ANY, u"EVE Servers (API && CREST import)", wx.DefaultPosition, + self.cbEve = wx.CheckBox(panel, wx.ID_ANY, "EVE Servers (API && CREST import)", wx.DefaultPosition, wx.DefaultSize, 0) subSizer.Add(self.cbEve, 0, wx.ALL | wx.EXPAND, 5) - self.cbPricing = wx.CheckBox(panel, wx.ID_ANY, u"Pricing updates", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbPricing = wx.CheckBox(panel, wx.ID_ANY, "Pricing updates", wx.DefaultPosition, wx.DefaultSize, 0) subSizer.Add(self.cbPricing, 0, wx.ALL | wx.EXPAND, 5) - self.cbPyfaUpdate = wx.CheckBox(panel, wx.ID_ANY, u"Pyfa Update checks", wx.DefaultPosition, wx.DefaultSize, 0) + self.cbPyfaUpdate = wx.CheckBox(panel, wx.ID_ANY, "Pyfa Update checks", wx.DefaultPosition, wx.DefaultSize, 0) subSizer.Add(self.cbPyfaUpdate, 0, wx.ALL | wx.EXPAND, 5) mainSizer.Add(subSizer, 0, wx.LEFT | wx.EXPAND, 30) @@ -80,11 +80,11 @@ class PFNetworkPref(PreferenceView): ptypeSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stPType = wx.StaticText(panel, wx.ID_ANY, u"Mode:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPType = wx.StaticText(panel, wx.ID_ANY, "Mode:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPType.Wrap(-1) ptypeSizer.Add(self.stPType, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) - self.chProxyTypeChoices = [u"No proxy", u"Auto-detected proxy settings", u"Manual proxy settings"] + self.chProxyTypeChoices = ["No proxy", "Auto-detected proxy settings", "Manual proxy settings"] self.chProxyType = wx.Choice(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, self.chProxyTypeChoices, 0) self.chProxyType.SetSelection(self.nMode) @@ -98,7 +98,7 @@ class PFNetworkPref(PreferenceView): fgAddrSizer.SetFlexibleDirection(wx.BOTH) fgAddrSizer.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - self.stPSetAddr = wx.StaticText(panel, wx.ID_ANY, u"Addr:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetAddr = wx.StaticText(panel, wx.ID_ANY, "Addr:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetAddr.Wrap(-1) fgAddrSizer.Add(self.stPSetAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -106,7 +106,7 @@ class PFNetworkPref(PreferenceView): fgAddrSizer.Add(self.editProxySettingsAddr, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5) - self.stPSetPort = wx.StaticText(panel, wx.ID_ANY, u"Port:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetPort = wx.StaticText(panel, wx.ID_ANY, "Port:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetPort.Wrap(-1) fgAddrSizer.Add(self.stPSetPort, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -118,14 +118,14 @@ class PFNetworkPref(PreferenceView): mainSizer.Add(fgAddrSizer, 0, wx.EXPAND, 5) # proxy auth information: login and pass - self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, u"Username:", wx.DefaultPosition, wx.DefaultSize, 0) + self.stPSetLogin = wx.StaticText(panel, wx.ID_ANY, "Username:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetLogin.Wrap(-1) self.editProxySettingsLogin = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[0], wx.DefaultPosition, wx.DefaultSize, - 0) - self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, u"Password:", wx.DefaultPosition, wx.DefaultSize, 0) + 0) + self.stPSetPassword = wx.StaticText(panel, wx.ID_ANY, "Password:", wx.DefaultPosition, wx.DefaultSize, 0) self.stPSetPassword.Wrap(-1) self.editProxySettingsPassword = wx.TextCtrl(panel, wx.ID_ANY, self.nAuth[1], wx.DefaultPosition, - wx.DefaultSize, wx.TE_PASSWORD) + wx.DefaultSize, wx.TE_PASSWORD) pAuthSizer = wx.BoxSizer(wx.HORIZONTAL) pAuthSizer.Add(self.stPSetLogin, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) pAuthSizer.Add(self.editProxySettingsLogin, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) @@ -133,15 +133,15 @@ class PFNetworkPref(PreferenceView): pAuthSizer.Add(self.editProxySettingsPassword, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) mainSizer.Add(pAuthSizer, 0, wx.EXPAND, 5) - self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, u"Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, - 0) + self.stPSAutoDetected = wx.StaticText(panel, wx.ID_ANY, "Auto-detected: ", wx.DefaultPosition, wx.DefaultSize, + 0) self.stPSAutoDetected.Wrap(-1) mainSizer.Add(self.stPSAutoDetected, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5) btnSizer = wx.BoxSizer(wx.HORIZONTAL) - btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + btnSizer.AddStretchSpacer() - self.btnApply = wx.Button(panel, wx.ID_ANY, u"Apply Proxy Settings", wx.DefaultPosition, wx.DefaultSize, 0) + self.btnApply = wx.Button(panel, wx.ID_ANY, "Apply Proxy Settings", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnApply, 0, wx.ALL, 5) @@ -150,10 +150,10 @@ class PFNetworkPref(PreferenceView): proxy = self.settings.autodetect() if proxy is not None: - addr, port = proxy - txt = addr + ":" + str(port) + addr, port = proxy + txt = addr + ":" + str(port) else: - txt = "None" + txt = "None" self.stPSAutoDetected.SetLabel("Auto-detected: " + txt) self.stPSAutoDetected.Disable() diff --git a/gui/builtinPreferenceViews/pyfaStatViewPreferences.py b/gui/builtinPreferenceViews/pyfaStatViewPreferences.py index 800b64851..e9e0d8cb0 100644 --- a/gui/builtinPreferenceViews/pyfaStatViewPreferences.py +++ b/gui/builtinPreferenceViews/pyfaStatViewPreferences.py @@ -2,7 +2,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.settings import StatViewSettings @@ -27,7 +27,7 @@ class PFStatViewPref(PreferenceView): mainSizer.Add(self.stTitle, 0, wx.ALL, 5) self.stSubTitle = wx.StaticText(panel, wx.ID_ANY, - u"Changes require restart of pyfa to take effect.", + "Changes require restart of pyfa to take effect.", wx.DefaultPosition, wx.DefaultSize, 0) self.stSubTitle.Wrap(-1) mainSizer.Add(self.stSubTitle, 0, wx.ALL, 3) diff --git a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py index cfc1bef11..f07d0380f 100644 --- a/gui/builtinPreferenceViews/pyfaUpdatePreferences.py +++ b/gui/builtinPreferenceViews/pyfaUpdatePreferences.py @@ -2,7 +2,7 @@ import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from service.settings import UpdateSettings @@ -33,7 +33,7 @@ class PFUpdatePref(PreferenceView): self.stDesc.Wrap(dlgWidth - 50) mainSizer.Add(self.stDesc, 0, wx.ALL, 5) - self.suppressPrerelease = wx.CheckBox(panel, wx.ID_ANY, u"Allow pre-release notifications", wx.DefaultPosition, + self.suppressPrerelease = wx.CheckBox(panel, wx.ID_ANY, "Allow pre-release notifications", wx.DefaultPosition, wx.DefaultSize, 0) self.suppressPrerelease.Bind(wx.EVT_CHECKBOX, self.OnPrereleaseStateChange) self.suppressPrerelease.SetValue(not self.UpdateSettings.get('prerelease')) @@ -52,11 +52,11 @@ class PFUpdatePref(PreferenceView): "You can choose to reset notification suppression for this release, " "or download the new release from GitHub.") - self.versionSizer.AddSpacer((5, 5), 0, wx.EXPAND, 5) + self.versionSizer.AddStretchSpacer() self.versionSizer.Add(wx.StaticLine(panel, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, wx.EXPAND, 5) - self.versionSizer.AddSpacer((5, 5), 0, wx.EXPAND, 5) + self.versionSizer.AddStretchSpacer() self.versionSizer.Add(self.versionTitle, 0, wx.EXPAND, 5) self.versionDesc = wx.StaticText(panel, wx.ID_ANY, self.versionInfo, wx.DefaultPosition, wx.DefaultSize, 0) diff --git a/gui/builtinShipBrowser/categoryItem.py b/gui/builtinShipBrowser/categoryItem.py index 875454e43..b88557d22 100644 --- a/gui/builtinShipBrowser/categoryItem.py +++ b/gui/builtinShipBrowser/categoryItem.py @@ -4,11 +4,11 @@ import wx from logbook import Logger from gui.builtinShipBrowser.sfBrowserItem import SFBrowserItem -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -from gui.bitmapLoader import BitmapLoader -import events +from gui.bitmap_loader import BitmapLoader +from .events import Stage2Selected pyfalog = Logger(__name__) @@ -20,7 +20,7 @@ class CategoryItem(SFBrowserItem): if categoryID: self.shipBmp = BitmapLoader.getBitmap("ship_small", "gui") else: - self.shipBmp = wx.EmptyBitmap(16, 16) + self.shipBmp = wx.Bitmap(16, 16) self.dropShadowBitmap = drawUtils.CreateDropShadowBitmap(self.shipBmp, 0.2) @@ -77,7 +77,7 @@ class CategoryItem(SFBrowserItem): def selectCategory(self, event): categoryID = self.categoryID - wx.PostEvent(self.shipBrowser, events.Stage2Selected(categoryID=categoryID, back=False)) + wx.PostEvent(self.shipBrowser, Stage2Selected(categoryID=categoryID, back=False)) def MouseLeftUp(self, event): self.selectCategory(event) @@ -100,8 +100,8 @@ class CategoryItem(SFBrowserItem): # rect = self.GetRect() self.UpdateElementsPos(mdc) - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + textColor = colorUtils.GetSuitable(windowColor, 1) mdc.SetTextForeground(textColor) mdc.DrawBitmap(self.dropShadowBitmap, self.shipBmpx + 1, self.shipBmpy + 1) diff --git a/gui/builtinShipBrowser/fitItem.py b/gui/builtinShipBrowser/fitItem.py index ad553a106..3d782fe5f 100644 --- a/gui/builtinShipBrowser/fitItem.py +++ b/gui/builtinShipBrowser/fitItem.py @@ -2,6 +2,7 @@ import re import time +import config import wx from logbook import Logger @@ -9,11 +10,11 @@ from logbook import Logger import gui.builtinShipBrowser.sfBrowserItem as SFItem import gui.globalEvents as GE import gui.mainFrame -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -import events -from gui.bitmapLoader import BitmapLoader +from .events import ImportSelected, SearchSelected, FitSelected, BoosterListUpdated, Stage3Selected, FitRenamed, FitRemoved +from gui.bitmap_loader import BitmapLoader from gui.builtinShipBrowser.pfBitmapFrame import PFBitmapFrame from service.fit import Fit @@ -58,6 +59,9 @@ class FitItem(SFItem.SFBrowserItem): self.shipFittingInfo = shipFittingInfo self.shipName, self.shipTrait, self.fitName, self.fitBooster, self.timestamp, self.notes = shipFittingInfo + if config.debug: + self.fitName = '({}) {}'.format(self.fitID, self.fitName) + self.shipTrait = re.sub("<.*?>", " ", self.shipTrait) # see GH issue #62 @@ -72,9 +76,9 @@ class FitItem(SFItem.SFBrowserItem): self.acceptBmp = BitmapLoader.getBitmap("faccept_small", "gui") self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big", "gui") - img = wx.ImageFromBitmap(self.shipEffBk) + img = self.shipEffBk.ConvertToImage() img = img.Mirror(False) - self.shipEffBkMirrored = wx.BitmapFromImage(img) + self.shipEffBkMirrored = wx.Bitmap(img) self.dragTLFBmp = None @@ -154,8 +158,8 @@ class FitItem(SFItem.SFBrowserItem): if self.shipTrait and sFit.serviceFittingOptions["showShipBrowserTooltip"]: notes = "" if self.notes: - notes = u'─' * 20 + u"\nNotes: {}\n".format(self.notes[:197] + '...' if len(self.notes) > 200 else self.notes) - self.SetToolTip(wx.ToolTip(u'{}\n{}{}\n{}'.format(self.shipName, notes, u'─' * 20, self.shipTrait))) + notes = '─' * 20 + "\nNotes: {}\n".format(self.notes[:197] + '...' if len(self.notes) > 200 else self.notes) + self.SetToolTip(wx.ToolTip('{}\n{}{}\n{}'.format(self.shipName, notes, '─' * 20, self.shipTrait))) def OnKeyUp(self, event): if event.GetKeyCode() in (32, 13): # space and enter @@ -171,7 +175,7 @@ class FitItem(SFItem.SFBrowserItem): self.fitBooster = not self.fitBooster self.boosterBtn.Show(self.fitBooster) self.Refresh() - wx.PostEvent(self.mainFrame, events.BoosterListUpdated()) + wx.PostEvent(self.mainFrame, BoosterListUpdated()) event.Skip() def OnProjectToFit(self, event): @@ -285,6 +289,7 @@ class FitItem(SFItem.SFBrowserItem): def editLostFocus(self, event): self.RestoreEditButton() self.Refresh() + event.Skip() def editCheckEsc(self, event): if event.GetKeyCode() == wx.WXK_ESCAPE: @@ -303,8 +308,8 @@ class FitItem(SFItem.SFBrowserItem): sFit = Fit.getInstance() fitID = sFit.copyFit(self.fitID) self.shipBrowser.fitIDMustEditName = fitID - wx.PostEvent(self.shipBrowser, events.Stage3Selected(shipID=self.shipID)) - wx.PostEvent(self.mainFrame, events.FitSelected(fitID=fitID)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID)) + wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def renameBtnCB(self): if self.tcFitName.IsShown(): @@ -327,7 +332,7 @@ class FitItem(SFItem.SFBrowserItem): if fitName: self.fitName = fitName sFit.renameFit(self.fitID, self.fitName) - wx.PostEvent(self.mainFrame, events.FitRenamed(fitID=self.fitID)) + wx.PostEvent(self.mainFrame, FitRenamed(fitID=self.fitID)) else: self.tcFitName.SetValue(self.fitName) @@ -337,7 +342,7 @@ class FitItem(SFItem.SFBrowserItem): return # to prevent accidental deletion, give dialog confirmation unless shift is depressed - if wx.GetMouseState().ShiftDown() or wx.GetMouseState().MiddleDown(): + if wx.GetMouseState().ShiftDown() or wx.GetMouseState().MiddleIsDown(): self.deleteFit() else: dlg = wx.MessageDialog( @@ -369,21 +374,21 @@ class FitItem(SFItem.SFBrowserItem): sFit.deleteFit(self.fitID) # Notify other areas that a fit has been deleted - wx.PostEvent(self.mainFrame, events.FitRemoved(fitID=self.fitID)) + wx.PostEvent(self.mainFrame, FitRemoved(fitID=self.fitID)) # todo: would a simple RefreshList() work here instead of posting that a stage has been selected? if self.shipBrowser.GetActiveStage() == 5: - wx.PostEvent(self.shipBrowser, events.ImportSelected(fits=self.shipBrowser.lastdata, recent=self.shipBrowser.recentFits)) + wx.PostEvent(self.shipBrowser, ImportSelected(fits=self.shipBrowser.lastdata, recent=self.shipBrowser.recentFits)) elif self.shipBrowser.GetActiveStage() == 4: - wx.PostEvent(self.shipBrowser, events.SearchSelected(text=self.shipBrowser.navpanel.lastSearch, back=True)) + wx.PostEvent(self.shipBrowser, SearchSelected(text=self.shipBrowser.navpanel.lastSearch, back=True)) else: - wx.PostEvent(self.shipBrowser, events.Stage3Selected(shipID=self.shipID)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID)) def MouseLeftUp(self, event): if self.dragging and self.dragged: self.OnMouseCaptureLost(event) - targetWnd = wx.FindWindowAtPointer() + targetWnd, _ = wx.FindWindowAtPointer() if not targetWnd: return @@ -433,9 +438,9 @@ class FitItem(SFItem.SFBrowserItem): def selectFit(self, event=None, newTab=False): if newTab: - wx.PostEvent(self.mainFrame, events.FitSelected(fitID=self.fitID, startup=2)) + wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fitID, startup=2)) else: - wx.PostEvent(self.mainFrame, events.FitSelected(fitID=self.fitID)) + wx.PostEvent(self.mainFrame, FitSelected(fitID=self.fitID)) def RestoreEditButton(self): self.tcFitName.Show(False) @@ -480,8 +485,8 @@ class FitItem(SFItem.SFBrowserItem): def DrawItem(self, mdc): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + textColor = colorUtils.GetSuitable(windowColor, 1) mdc.SetTextForeground(textColor) @@ -521,7 +526,7 @@ class FitItem(SFItem.SFBrowserItem): self.AdjustControlSizePos(self.tcFitName, self.textStartx, self.toolbarx - self.editWidth - self.padding) tdc = wx.MemoryDC() - self.dragTLFBmp = wx.EmptyBitmap((self.toolbarx if self.toolbarx < 200 else 200), rect.height, 24) + 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) @@ -569,7 +574,7 @@ class FitItem(SFItem.SFBrowserItem): def RenderBackground(self): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) # activeFitID = self.mainFrame.getActiveFit() state = self.GetState() diff --git a/gui/builtinShipBrowser/navigationPanel.py b/gui/builtinShipBrowser/navigationPanel.py index cd80b658e..204802c45 100644 --- a/gui/builtinShipBrowser/navigationPanel.py +++ b/gui/builtinShipBrowser/navigationPanel.py @@ -5,11 +5,11 @@ from logbook import Logger import gui.builtinShipBrowser.sfBrowserItem as SFItem import gui.mainFrame -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -import events -from gui.bitmapLoader import BitmapLoader +from .events import FitSelected, SearchSelected, ImportSelected, Stage1Selected, Stage2Selected, Stage3Selected +from gui.bitmap_loader import BitmapLoader from service.fit import Fit pyfalog = Logger(__name__) @@ -29,11 +29,11 @@ class NavigationPanel(SFItem.SFBrowserItem): switchImg = BitmapLoader.getImage("fit_switch_view_mode_small", "gui") switchImg = switchImg.AdjustChannels(1, 1, 1, 0.4) - self.switchBmpD = wx.BitmapFromImage(switchImg) + self.switchBmpD = wx.Bitmap(switchImg) recentImg = BitmapLoader.getImage("frecent_small", "gui") recentImg = recentImg.AdjustChannels(1, 1, 1, 0.4) - self.recentBmpD = wx.BitmapFromImage(recentImg) + self.recentBmpD = wx.Bitmap(recentImg) self.resetBmp = self.AdjustChannels(self.resetBmpH) self.rewBmp = self.AdjustChannels(self.rewBmpH) @@ -87,7 +87,7 @@ class NavigationPanel(SFItem.SFBrowserItem): realsearch = search.replace("*", "") if len(realsearch) >= 3: self.lastSearch = search - wx.PostEvent(self.shipBrowser, events.SearchSelected(text=search, back=False)) + wx.PostEvent(self.shipBrowser, SearchSelected(text=search, back=False)) def ToggleSearchBox(self): if self.BrowserSearchBox.IsShown(): @@ -122,7 +122,7 @@ class NavigationPanel(SFItem.SFBrowserItem): self.btnRecent.normalBmp = self.recentBmpD if emitEvent: - wx.PostEvent(self.shipBrowser, events.Stage1Selected()) + wx.PostEvent(self.shipBrowser, Stage1Selected()) else: self.shipBrowser.recentFits = True self.btnRecent.label = "Hide Recent Fits" @@ -131,7 +131,7 @@ class NavigationPanel(SFItem.SFBrowserItem): if emitEvent: sFit = Fit.getInstance() fits = sFit.getRecentFits() - wx.PostEvent(self.shipBrowser, events.ImportSelected(fits=fits, back=True, recent=True)) + wx.PostEvent(self.shipBrowser, ImportSelected(fits=fits, back=True, recent=True)) def ToggleEmptyGroupsView(self): if self.shipBrowser.filterShipsWithNoFits: @@ -146,10 +146,10 @@ class NavigationPanel(SFItem.SFBrowserItem): stage = self.shipBrowser.GetActiveStage() if stage == 1: - wx.PostEvent(self.shipBrowser, events.Stage1Selected()) + wx.PostEvent(self.shipBrowser, Stage1Selected()) elif stage == 2: categoryID = self.shipBrowser.GetStageData(stage) - wx.PostEvent(self.shipBrowser, events.Stage2Selected(categoryID=categoryID, back=True)) + wx.PostEvent(self.shipBrowser, Stage2Selected(categoryID=categoryID, back=True)) def ShowNewFitButton(self, show): self.btnNew.Show(show) @@ -167,8 +167,8 @@ class NavigationPanel(SFItem.SFBrowserItem): sFit = Fit.getInstance() fitID = sFit.newFit(shipID, "%s fit" % shipName) self.shipBrowser.fitIDMustEditName = fitID - wx.PostEvent(self.Parent, events.Stage3Selected(shipID=shipID)) - wx.PostEvent(self.mainFrame, events.FitSelected(fitID=fitID)) + wx.PostEvent(self.Parent, Stage3Selected(shipID=shipID)) + wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def OnHistoryReset(self): self.ToggleRecentShips(False, False) @@ -184,9 +184,9 @@ class NavigationPanel(SFItem.SFBrowserItem): @staticmethod def AdjustChannels(bitmap): - img = wx.ImageFromBitmap(bitmap) + img = bitmap.ConvertToImage() img = img.AdjustChannels(1.05, 1.05, 1.05, 1) - return wx.BitmapFromImage(img) + return wx.Bitmap(img) def UpdateElementsPos(self, mdc): rect = self.GetRect() @@ -211,9 +211,9 @@ class NavigationPanel(SFItem.SFBrowserItem): def DrawItem(self, mdc): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) - sepColor = colorUtils.GetSuitableColor(windowColor, 0.2) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + textColor = colorUtils.GetSuitable(windowColor, 1) + sepColor = colorUtils.GetSuitable(windowColor, 0.2) mdc.SetTextForeground(textColor) @@ -230,7 +230,7 @@ class NavigationPanel(SFItem.SFBrowserItem): def RenderBackground(self): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) sFactor = 0.1 @@ -259,15 +259,15 @@ class NavigationPanel(SFItem.SFBrowserItem): def gotoStage(self, stage, data=None): self.shipBrowser.recentFits = False if stage == 1: - wx.PostEvent(self.Parent, events.Stage1Selected()) + wx.PostEvent(self.Parent, Stage1Selected()) elif stage == 2: - wx.PostEvent(self.Parent, events.Stage2Selected(categoryID=data, back=True)) + wx.PostEvent(self.Parent, Stage2Selected(categoryID=data, back=True)) elif stage == 3: - wx.PostEvent(self.Parent, events.Stage3Selected(shipID=data)) + wx.PostEvent(self.Parent, Stage3Selected(shipID=data)) elif stage == 4: self.shipBrowser._activeStage = 4 - wx.PostEvent(self.Parent, events.SearchSelected(text=data, back=True)) + wx.PostEvent(self.Parent, SearchSelected(text=data, back=True)) elif stage == 5: - wx.PostEvent(self.Parent, events.ImportSelected(fits=data)) + wx.PostEvent(self.Parent, ImportSelected(fits=data)) else: - wx.PostEvent(self.Parent, events.Stage1Selected()) + wx.PostEvent(self.Parent, Stage1Selected()) diff --git a/gui/builtinShipBrowser/pfBitmapFrame.py b/gui/builtinShipBrowser/pfBitmapFrame.py index e05e643fc..85ad487d6 100644 --- a/gui/builtinShipBrowser/pfBitmapFrame.py +++ b/gui/builtinShipBrowser/pfBitmapFrame.py @@ -7,7 +7,7 @@ class PFBitmapFrame(wx.Frame): style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) img = bitmap.ConvertToImage() img = img.ConvertToGreyscale() - bitmap = wx.BitmapFromImage(img) + bitmap = wx.Bitmap(img) self.bitmap = bitmap self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) self.Bind(wx.EVT_PAINT, self.OnWindowPaint) @@ -19,6 +19,8 @@ class PFBitmapFrame(wx.Frame): self.transp = 0 self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + self.SetTransparent(0) self.Refresh() @@ -49,8 +51,12 @@ class PFBitmapFrame(wx.Frame): pass def OnWindowPaint(self, event): + # todo: evaluate wx.DragImage, might make this class obsolete, however might also lose our customizations + # (like the sexy fade-in animation) rect = self.GetRect() - canvas = wx.EmptyBitmap(rect.width, rect.height) + canvas = wx.Bitmap(rect.width, rect.height) + # todo: convert to context manager after updating to wxPython >v4.0.1 (4.0.1 has a bug, see #1421) + # See #1418 for discussion mdc = wx.BufferedPaintDC(self) mdc.SelectObject(canvas) mdc.DrawBitmap(self.bitmap, 0, 0) diff --git a/gui/builtinShipBrowser/pfListPane.py b/gui/builtinShipBrowser/pfListPane.py index e6689ce8d..cbdebff05 100644 --- a/gui/builtinShipBrowser/pfListPane.py +++ b/gui/builtinShipBrowser/pfListPane.py @@ -23,14 +23,13 @@ import wx class PFListPane(wx.ScrolledWindow): def __init__(self, parent): - wx.ScrolledWindow.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(1, 1), - style=wx.TAB_TRAVERSAL) + wx.ScrolledWindow.__init__(self, parent, pos=wx.DefaultPosition, style=wx.TAB_TRAVERSAL) self._wList = [] self._wCount = 0 self.itemsHeight = 1 - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) self.SetVirtualSize((1, 1)) self.SetScrollRate(0, 1) @@ -127,7 +126,7 @@ class PFListPane(wx.ScrolledWindow): maxy = 0 selected = None - for i in xrange(len(self._wList)): + for i in range(len(self._wList)): iwidth, iheight = self._wList[i].GetSize() xa, ya = self.CalcScrolledPosition((0, maxy)) self._wList[i].SetPosition((xa, ya)) @@ -144,7 +143,7 @@ class PFListPane(wx.ScrolledWindow): elif doFocus: self.SetFocus() - for i in xrange(len(self._wList)): + for i in range(len(self._wList)): iwidth, iheight = self._wList[i].GetSize() self._wList[i].SetSize((cwidth, iheight)) if doRefresh is True: diff --git a/gui/builtinShipBrowser/pfStaticText.py b/gui/builtinShipBrowser/pfStaticText.py index 66a602239..f5122ecfa 100644 --- a/gui/builtinShipBrowser/pfStaticText.py +++ b/gui/builtinShipBrowser/pfStaticText.py @@ -9,7 +9,7 @@ pyfalog = Logger(__name__) class PFStaticText(wx.Panel): def __init__(self, parent, label=wx.EmptyString): wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=parent.GetSize()) - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) mainSizer = wx.BoxSizer(wx.VERTICAL) text = wx.StaticText(self, wx.ID_ANY, label, wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE) diff --git a/gui/builtinShipBrowser/pfWidgetContainer.py b/gui/builtinShipBrowser/pfWidgetContainer.py index 305bd6385..e3219530a 100644 --- a/gui/builtinShipBrowser/pfWidgetContainer.py +++ b/gui/builtinShipBrowser/pfWidgetContainer.py @@ -1,6 +1,6 @@ from gui.builtinShipBrowser.pfListPane import PFListPane import gui.mainFrame -import gui.utils.animUtils as animUtils +import gui.utils.anim as animUtils class PFWidgetsContainer(PFListPane): diff --git a/gui/builtinShipBrowser/raceSelector.py b/gui/builtinShipBrowser/raceSelector.py index 3807bdf66..809474249 100644 --- a/gui/builtinShipBrowser/raceSelector.py +++ b/gui/builtinShipBrowser/raceSelector.py @@ -3,11 +3,11 @@ import wx from logbook import Logger -import gui.utils.animEffects as animEffects -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils -import events -from gui.bitmapLoader import BitmapLoader +import gui.utils.anim_effects as animEffects +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils +from .events import Stage2Selected +from gui.bitmap_loader import BitmapLoader pyfalog = Logger(__name__) @@ -70,7 +70,7 @@ class RaceSelector(wx.Window): if layout == wx.VERTICAL: img = img.Scale(self.minWidth, 8, wx.IMAGE_QUALITY_HIGH) - self.bmpArrow = wx.BitmapFromImage(img) + self.bmpArrow = wx.Bitmap(img) self.RebuildRaces(self.shipBrowser.RACE_ORDER) @@ -85,6 +85,8 @@ class RaceSelector(wx.Window): self.Layout() + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + def OnMouseMove(self, event): mx, my = event.GetPosition() @@ -93,9 +95,9 @@ class RaceSelector(wx.Window): self.hoveredItem = location self.Refresh() if location is not None: - self.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) + self.SetCursor(wx.Cursor(wx.CURSOR_HAND)) else: - self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) def OnSizeUpdate(self, event): self.CalcButtonsBarPos() @@ -134,7 +136,7 @@ class RaceSelector(wx.Window): if stage == 2: categoryID = self.shipBrowser.GetStageData(stage) - wx.PostEvent(self.shipBrowser, events.Stage2Selected(categoryID=categoryID, back=True)) + wx.PostEvent(self.shipBrowser, Stage2Selected(categoryID=categoryID, back=True)) event.Skip() def HitTest(self, mx, my): @@ -166,11 +168,11 @@ class RaceSelector(wx.Window): def OnPaint(self, event): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - # bkColor = colorUtils.GetSuitableColor(windowColor, 0.1) - sepColor = colorUtils.GetSuitableColor(windowColor, 0.2) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + # bkColor = colorUtils.GetSuitable(windowColor, 0.1) + sepColor = colorUtils.GetSuitable(windowColor, 0.2) - mdc = wx.BufferedPaintDC(self) + mdc = wx.AutoBufferedPaintDC(self) bkBitmap = drawUtils.RenderGradientBar(windowColor, rect.width, rect.height, 0.1, 0.1, 0.2, 2) mdc.DrawBitmap(bkBitmap, 0, 0, True) @@ -184,12 +186,12 @@ class RaceSelector(wx.Window): if self.shipBrowser.GetRaceFilterState(self.raceNames[self.raceBmps.index(raceBmp)]): bmp = raceBmp else: - img = wx.ImageFromBitmap(raceBmp) + img = raceBmp.ConvertToImage() if self.hoveredItem == self.raceBmps.index(raceBmp): img = img.AdjustChannels(1, 1, 1, 0.7) else: img = img.AdjustChannels(1, 1, 1, 0.4) - bmp = wx.BitmapFromImage(img) + bmp = wx.Bitmap(img) if self.layout == wx.VERTICAL: mdc.DrawBitmap(dropShadow, rect.width - self.buttonsPadding - bmp.GetWidth() + 1, y + 1) @@ -257,7 +259,7 @@ class RaceSelector(wx.Window): def OnWindowLeave(self, event): if self.hoveredItem is not None: self.hoveredItem = None - self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) self.Refresh() if not self.animate: diff --git a/gui/builtinShipBrowser/sfBrowserItem.py b/gui/builtinShipBrowser/sfBrowserItem.py index 4f33f101f..16b7a4f24 100644 --- a/gui/builtinShipBrowser/sfBrowserItem.py +++ b/gui/builtinShipBrowser/sfBrowserItem.py @@ -1,6 +1,6 @@ # noinspection PyPackageRequirements import wx -import gui.utils.drawUtils as drawUtils +import gui.utils.draw as drawUtils SB_ITEM_NORMAL = 0 SB_ITEM_SELECTED = 1 @@ -122,7 +122,7 @@ class PFToolbar(object): if not state & BTN_HOVER: button.SetState(state | BTN_HOVER) self.hoverLabel = button.GetLabel() - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) + self.Parent.SetCursor(wx.Cursor(wx.CURSOR_HAND)) doRefresh = True else: if state & BTN_HOVER: @@ -133,7 +133,7 @@ class PFToolbar(object): bx += bwidth + self.padding if not changeCursor: - self.Parent.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self.Parent.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) return doRefresh def MouseClick(self, event): @@ -241,7 +241,7 @@ class PFToolbar(object): class SFBrowserItem(wx.Window): def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=(0, 16), style=0): wx.Window.__init__(self, parent, id, pos, size, style) - + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) self.highlighted = False self.selected = False self.bkBitmap = None @@ -280,7 +280,7 @@ class SFBrowserItem(wx.Window): wx.Window.Refresh(self) def OnPaint(self, event): - mdc = wx.BufferedPaintDC(self) + mdc = wx.AutoBufferedPaintDC(self) self.RenderBackground() @@ -399,7 +399,7 @@ class SFBrowserItem(wx.Window): def RenderBackground(self): rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) state = self.GetState() diff --git a/gui/builtinShipBrowser/shipItem.py b/gui/builtinShipBrowser/shipItem.py index 3ef8163df..4c679fb8a 100644 --- a/gui/builtinShipBrowser/shipItem.py +++ b/gui/builtinShipBrowser/shipItem.py @@ -5,11 +5,11 @@ from logbook import Logger import gui.builtinShipBrowser.sfBrowserItem as SFItem import gui.mainFrame -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils +import gui.utils.color as colorUtils +import gui.utils.draw as drawUtils import gui.utils.fonts as fonts -import events -from gui.bitmapLoader import BitmapLoader +from .events import Stage3Selected, Stage2Selected, Stage1Selected, FitSelected +from gui.bitmap_loader import BitmapLoader from gui.contextMenu import ContextMenu from service.fit import Fit from service.market import Market @@ -50,9 +50,9 @@ class ShipItem(SFItem.SFBrowserItem): self.shipEffBk = BitmapLoader.getBitmap("fshipbk_big", "gui") - img = wx.ImageFromBitmap(self.shipEffBk) + img = self.shipEffBk.ConvertToImage() img = img.Mirror(False) - self.shipEffBkMirrored = wx.BitmapFromImage(img) + self.shipEffBkMirrored = wx.Bitmap(img) self.raceBmp = BitmapLoader.getBitmap("race_%s_small" % self.shipRace, "gui") @@ -147,7 +147,7 @@ class ShipItem(SFItem.SFBrowserItem): else: shipName, shipTrait, fittings = self.shipFittingInfo if fittings > 0: - wx.PostEvent(self.shipBrowser, events.Stage3Selected(shipID=self.shipID, back=True)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID, back=True)) else: self.newBtnCB() @@ -186,8 +186,8 @@ class ShipItem(SFItem.SFBrowserItem): sFit = Fit.getInstance() fitID = sFit.newFit(self.shipID, self.tcFitName.GetValue()) - wx.PostEvent(self.shipBrowser, events.Stage3Selected(shipID=self.shipID, back=False)) - wx.PostEvent(self.mainFrame, events.FitSelected(fitID=fitID)) + wx.PostEvent(self.shipBrowser, Stage3Selected(shipID=self.shipID, back=False)) + wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def UpdateElementsPos(self, mdc): rect = self.GetRect() @@ -232,8 +232,8 @@ class ShipItem(SFItem.SFBrowserItem): def DrawItem(self, mdc): # rect = self.GetRect() - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - textColor = colorUtils.GetSuitableColor(windowColor, 1) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + textColor = colorUtils.GetSuitable(windowColor, 1) mdc.SetTextForeground(textColor) diff --git a/gui/builtinStatsViews/capacitorViewFull.py b/gui/builtinStatsViews/capacitorViewFull.py index 49bb9d59b..8ff7d6a13 100644 --- a/gui/builtinStatsViews/capacitorViewFull.py +++ b/gui/builtinStatsViews/capacitorViewFull.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount @@ -45,7 +45,7 @@ class CapacitorViewFull(StatsView): panel = "full" - sizerCapacitor = wx.GridSizer(1, 2) + sizerCapacitor = wx.GridSizer(1, 2, 0, 0) contentSizer.Add(sizerCapacitor, 0, wx.EXPAND, 0) # Capacitor capacity and time baseBox = wx.BoxSizer(wx.HORIZONTAL) @@ -91,7 +91,7 @@ class CapacitorViewFull(StatsView): baseBox.Add(bitmap, 0, wx.ALIGN_CENTER) # Recharge - chargeSizer = wx.FlexGridSizer(2, 3) + chargeSizer = wx.FlexGridSizer(2, 3, 0, 0) baseBox.Add(chargeSizer, 0, wx.ALIGN_CENTER) chargeSizer.Add(wx.StaticText(parent, wx.ID_ANY, "+ "), 0, wx.ALIGN_CENTER) @@ -124,7 +124,7 @@ class CapacitorViewFull(StatsView): label = getattr(self, labelName % panel) value = value() if fit is not None else 0 value = value if value is not None else 0 - if isinstance(value, basestring): + if isinstance(value, str): label.SetLabel(value) label.SetToolTip(wx.ToolTip(value)) else: diff --git a/gui/builtinStatsViews/firepowerViewFull.py b/gui/builtinStatsViews/firepowerViewFull.py index 618ca52de..98d54d714 100644 --- a/gui/builtinStatsViews/firepowerViewFull.py +++ b/gui/builtinStatsViews/firepowerViewFull.py @@ -21,7 +21,7 @@ import wx import gui.mainFrame from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.fit import Fit @@ -53,7 +53,7 @@ class FirepowerViewFull(StatsView): panel = "full" - sizerFirepower = wx.FlexGridSizer(1, 4) + sizerFirepower = wx.FlexGridSizer(1, 4, 0, 0) sizerFirepower.AddGrowableCol(1) contentSizer.Add(sizerFirepower, 0, wx.EXPAND, 0) @@ -128,8 +128,8 @@ class FirepowerViewFull(StatsView): # Remove effective label hsizer = self.headerPanel.GetSizer() - hsizer.Remove(self.stEff) - self.stEff.Destroy() + hsizer.Hide(self.stEff) + #self.stEff.Destroy() # Get the new view view = StatsView.getView("miningyieldViewFull")(self.parent) diff --git a/gui/builtinStatsViews/miningyieldViewFull.py b/gui/builtinStatsViews/miningyieldViewFull.py index 912bddc2c..7793a2850 100644 --- a/gui/builtinStatsViews/miningyieldViewFull.py +++ b/gui/builtinStatsViews/miningyieldViewFull.py @@ -21,7 +21,7 @@ import wx import gui.mainFrame from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.fit import Fit @@ -48,7 +48,7 @@ class MiningYieldViewFull(StatsView): panel = "full" - sizerMiningYield = wx.FlexGridSizer(1, 4) + sizerMiningYield = wx.FlexGridSizer(1, 4, 0, 0) sizerMiningYield.AddGrowableCol(1) contentSizer.Add(sizerMiningYield, 0, wx.EXPAND, 0) @@ -69,7 +69,7 @@ class MiningYieldViewFull(StatsView): hbox = wx.BoxSizer(wx.HORIZONTAL) box.Add(hbox, 1, wx.ALIGN_CENTER) - lbl = wx.StaticText(parent, wx.ID_ANY, u"0.0 m\u00B3/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0.0 m\u00B3/s") setattr(self, "label%sminingyield%s" % (panel.capitalize(), miningType.capitalize()), lbl) hbox.Add(lbl, 0, wx.ALIGN_CENTER) @@ -90,7 +90,7 @@ class MiningYieldViewFull(StatsView): hbox = wx.BoxSizer(wx.HORIZONTAL) box.Add(hbox, 1, wx.EXPAND) - lbl = wx.StaticText(parent, wx.ID_ANY, u"0.0 m\u00B3/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0.0 m\u00B3/s") setattr(self, "label%sminingyieldTotal" % panel.capitalize(), lbl) hbox.Add(lbl, 0, wx.ALIGN_LEFT) @@ -123,16 +123,16 @@ class MiningYieldViewFull(StatsView): # Get the TogglePanel tp = self.panel.GetParent() # Bind the new panel's children to allow context menu access - self.parent.applyBinding(self.parent, tp.GetContentPane()) + self.parent.applyBinding(self.parent, tp.content_panel) tp.SetLabel(view.getHeaderText(fit)) view.refreshPanel(fit) def refreshPanel(self, fit): # If we did anything intresting, we'd update our labels to reflect the new fit's stats here - stats = (("labelFullminingyieldMiner", lambda: fit.minerYield, 3, 0, 0, u"%s m\u00B3/s", None), - ("labelFullminingyieldDrone", lambda: fit.droneYield, 3, 0, 0, u"%s m\u00B3/s", None), - ("labelFullminingyieldTotal", lambda: fit.totalYield, 3, 0, 0, u"%s m\u00B3/s", None)) + stats = (("labelFullminingyieldMiner", lambda: fit.minerYield, 3, 0, 0, "%s m\u00B3/s", None), + ("labelFullminingyieldDrone", lambda: fit.droneYield, 3, 0, 0, "%s m\u00B3/s", None), + ("labelFullminingyieldTotal", lambda: fit.totalYield, 3, 0, 0, "%s m\u00B3/s", None)) counter = 0 for labelName, value, prec, lowest, highest, valueFormat, altFormat in stats: diff --git a/gui/builtinStatsViews/outgoingViewFull.py b/gui/builtinStatsViews/outgoingViewFull.py index 346aacc7a..be24ee6af 100644 --- a/gui/builtinStatsViews/outgoingViewFull.py +++ b/gui/builtinStatsViews/outgoingViewFull.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount @@ -44,7 +44,7 @@ class OutgoingViewFull(StatsView): parent = self.panel = contentPanel self.headerPanel = headerPanel - sizerOutgoing = wx.GridSizer(1, 4) + sizerOutgoing = wx.GridSizer(1, 4, 0, 0) contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0) @@ -63,9 +63,9 @@ class OutgoingViewFull(StatsView): baseBox.Add(BitmapLoader.getStaticBitmap("%s_big" % image, parent, "gui"), 0, wx.ALIGN_CENTER) if "Capacitor" in outgoingType: - lbl = wx.StaticText(parent, wx.ID_ANY, u"0 GJ/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0 GJ/s") else: - lbl = wx.StaticText(parent, wx.ID_ANY, u"0 HP/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0 HP/s") lbl.SetToolTip(wx.ToolTip(tooltip)) @@ -81,10 +81,10 @@ class OutgoingViewFull(StatsView): # If we did anything intresting, we'd update our labels to reflect the new fit's stats here stats = [ - ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, u"%s GJ/s", None), + ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, "%s GJ/s", None), ] counter = 0 diff --git a/gui/builtinStatsViews/outgoingViewMinimal.py b/gui/builtinStatsViews/outgoingViewMinimal.py index d2bf2c118..21fe9bede 100644 --- a/gui/builtinStatsViews/outgoingViewMinimal.py +++ b/gui/builtinStatsViews/outgoingViewMinimal.py @@ -43,7 +43,7 @@ class OutgoingViewMinimal(StatsView): parent = self.panel = contentPanel self.headerPanel = headerPanel - sizerOutgoing = wx.GridSizer(1, 4) + sizerOutgoing = wx.GridSizer(1, 4, 0, 0) contentSizer.Add(sizerOutgoing, 0, wx.EXPAND, 0) @@ -62,9 +62,9 @@ class OutgoingViewMinimal(StatsView): baseBox.Add(wx.StaticText(contentPanel, wx.ID_ANY, label), 0, wx.ALIGN_CENTER) if "Capacitor" in outgoingType: - lbl = wx.StaticText(parent, wx.ID_ANY, u"0 GJ/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0 GJ/s") else: - lbl = wx.StaticText(parent, wx.ID_ANY, u"0 HP/s") + lbl = wx.StaticText(parent, wx.ID_ANY, "0 HP/s") lbl.SetToolTip(wx.ToolTip(tooltip)) @@ -80,10 +80,10 @@ class OutgoingViewMinimal(StatsView): # If we did anything intresting, we'd update our labels to reflect the new fit's stats here stats = [ - ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, u"%s HP/s", None), - ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, u"%s GJ/s", None), + ("labelRemoteArmor", lambda: fit.remoteReps["Armor"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteShield", lambda: fit.remoteReps["Shield"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteHull", lambda: fit.remoteReps["Hull"], 3, 0, 0, "%s HP/s", None), + ("labelRemoteCapacitor", lambda: fit.remoteReps["Capacitor"], 3, 0, 0, "%s GJ/s", None), ] counter = 0 diff --git a/gui/builtinStatsViews/priceViewFull.py b/gui/builtinStatsViews/priceViewFull.py index b315c6161..9eb03ea22 100644 --- a/gui/builtinStatsViews/priceViewFull.py +++ b/gui/builtinStatsViews/priceViewFull.py @@ -19,9 +19,8 @@ # noinspection PyPackageRequirements import wx - -from gui.bitmapLoader import BitmapLoader from gui.statsView import StatsView +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.price import Price from service.settings import PriceMenuSettings @@ -50,7 +49,7 @@ class PriceViewFull(StatsView): headerContentSizer.Add(self.labelEMStatus) headerPanel.GetParent().AddToggleItem(self.labelEMStatus) - gridPrice = wx.GridSizer(2, 3) + gridPrice = wx.GridSizer(2, 3, 0, 0) contentSizer.Add(gridPrice, 0, wx.EXPAND | wx.ALL, 0) for _type in ("ship", "fittings", "total", "drones", "cargoBay", "character"): if _type in "ship": diff --git a/gui/builtinStatsViews/priceViewMinimal.py b/gui/builtinStatsViews/priceViewMinimal.py index 3a0af082c..ec1d5357f 100644 --- a/gui/builtinStatsViews/priceViewMinimal.py +++ b/gui/builtinStatsViews/priceViewMinimal.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.price import Price from service.settings import PriceMenuSettings @@ -49,7 +49,7 @@ class PriceViewMinimal(StatsView): headerContentSizer.Add(self.labelEMStatus) headerPanel.GetParent().AddToggleItem(self.labelEMStatus) - gridPrice = wx.GridSizer(1, 3) + gridPrice = wx.GridSizer(1, 3, 0, 0) contentSizer.Add(gridPrice, 0, wx.EXPAND | wx.ALL, 0) for _type in ("ship", "fittings", "total"): image = "%sPrice_big" % _type if _type != "ship" else "ship_big" diff --git a/gui/builtinStatsViews/rechargeViewFull.py b/gui/builtinStatsViews/rechargeViewFull.py index 18af61d73..eadcf537f 100644 --- a/gui/builtinStatsViews/rechargeViewFull.py +++ b/gui/builtinStatsViews/rechargeViewFull.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount import gui.mainFrame from gui.builtinStatsViews.resistancesViewFull import EFFECTIVE_HP_TOGGLED @@ -55,7 +55,7 @@ class RechargeViewFull(StatsView): self.panel = contentPanel self.headerPanel = headerPanel - sizerTankStats = wx.FlexGridSizer(3, 5) + sizerTankStats = wx.FlexGridSizer(3, 5, 0, 0) for i in range(4): sizerTankStats.AddGrowableCol(i + 1) diff --git a/gui/builtinStatsViews/resistancesViewFull.py b/gui/builtinStatsViews/resistancesViewFull.py index 9946310ec..684a0e617 100644 --- a/gui/builtinStatsViews/resistancesViewFull.py +++ b/gui/builtinStatsViews/resistancesViewFull.py @@ -20,11 +20,12 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader -from gui.pygauge import PyGauge +from gui.bitmap_loader import BitmapLoader +from gui.pyfa_gauge import PyGauge from gui.utils.numberFormatter import formatAmount import gui.mainFrame import gui.globalEvents as GE +from gui.utils import fonts EffectiveHpToggled, EFFECTIVE_HP_TOGGLED = wx.lib.newevent.NewEvent() @@ -93,7 +94,7 @@ class ResistancesViewFull(StatsView): self.stEHPs.Bind(wx.EVT_BUTTON, self.toggleEHP) - for i in xrange(4): + for i in range(4): sizerResistances.AddGrowableCol(i + 1) sizerResistances.Add(self.stEHPs, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) @@ -122,6 +123,8 @@ class ResistancesViewFull(StatsView): continue currGColour = 0 + font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + for damageType in ("em", "thermal", "kinetic", "explosive"): box = wx.BoxSizer(wx.HORIZONTAL) sizerResistances.Add(box, wx.GBPosition(row, col), wx.GBSpan(1, 1), wx.ALIGN_CENTER) @@ -133,7 +136,7 @@ class ResistancesViewFull(StatsView): bc = pgColour[1] currGColour += 1 - lbl = PyGauge(contentPanel, wx.ID_ANY, 100) + lbl = PyGauge(contentPanel, font, 100) lbl.SetMinSize((48, 16)) lbl.SetBackgroundColour(wx.Colour(bc[0], bc[1], bc[2])) lbl.SetBarColour(wx.Colour(fc[0], fc[1], fc[2])) diff --git a/gui/builtinStatsViews/resourcesViewFull.py b/gui/builtinStatsViews/resourcesViewFull.py index 3171aaa4b..77a1eaae5 100644 --- a/gui/builtinStatsViews/resourcesViewFull.py +++ b/gui/builtinStatsViews/resourcesViewFull.py @@ -20,10 +20,11 @@ # noinspection PyPackageRequirements import wx from gui.statsView import StatsView -from gui.bitmapLoader import BitmapLoader -from gui.pygauge import PyGauge +from gui.bitmap_loader import BitmapLoader +from gui.pyfa_gauge import PyGauge import gui.mainFrame -from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED +from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED +from gui.utils import fonts from eos.saveddata.module import Hardpoint @@ -101,7 +102,7 @@ class ResourcesViewFull(StatsView): panel = "full" base = sizerResources - sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + sizer.AddStretchSpacer() # Turrets & launcher hardslots display tooltipText = {"turret": "Turret hardpoints", "launcher": "Launcher hardpoints", "drones": "Drones active", "fighter": "Fighter squadrons active", "calibration": "Calibration"} @@ -133,7 +134,9 @@ class ResourcesViewFull(StatsView): # Hack - We add a spacer after each thing, but we are always hiding something. The spacer is stil there. # This way, we only have one space after the drones/fighters if type_ != "drones": - sizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) + sizer.AddStretchSpacer() + + gauge_font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) # PG, Cpu & drone stuff tooltipText = {"cpu": "CPU", "pg": "PowerGrid", "droneBay": "Drone bay", "fighterBay": "Fighter bay", @@ -169,14 +172,14 @@ class ResourcesViewFull(StatsView): setattr(self, "label%sTotal%s" % (panel.capitalize(), capitalizedType), lbl) absolute.Add(lbl, 0, wx.ALIGN_LEFT) - units = {"cpu": " tf", "pg": " MW", "droneBandwidth": " mbit/s", "droneBay": u" m\u00B3", - "fighterBay": u" m\u00B3", "cargoBay": u" m\u00B3"} + units = {"cpu": " tf", "pg": " MW", "droneBandwidth": " mbit/s", "droneBay": " m\u00B3", + "fighterBay": " m\u00B3", "cargoBay": " m\u00B3"} lbl = wx.StaticText(parent, wx.ID_ANY, "%s" % units[type_]) absolute.Add(lbl, 0, wx.ALIGN_LEFT) # Gauges modif. - Darriele - gauge = PyGauge(parent, wx.ID_ANY, 1) + gauge = PyGauge(parent, gauge_font, 1) gauge.SetValueRange(0, 0) gauge.SetMinSize((self.getTextExtentW("1.999M/1.99M MW"), 23)) gauge.SetFractionDigits(2) @@ -275,7 +278,7 @@ class ResourcesViewFull(StatsView): totalCalibrationPoints = value labelTCP = label - if isinstance(value, basestring): + if isinstance(value, str): label.SetLabel(value) label.SetToolTip(wx.ToolTip(value)) else: @@ -283,7 +286,7 @@ class ResourcesViewFull(StatsView): label.SetToolTip(wx.ToolTip("%.1f" % value)) colorWarn = wx.Colour(204, 51, 51) - colorNormal = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + colorNormal = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) if usedTurretHardpoints > totalTurretHardpoints: colorT = colorWarn diff --git a/gui/builtinStatsViews/targetingMiscViewFull.py b/gui/builtinStatsViews/targetingMiscViewFull.py index 0ee84b9dd..e43f04418 100644 --- a/gui/builtinStatsViews/targetingMiscViewFull.py +++ b/gui/builtinStatsViews/targetingMiscViewFull.py @@ -21,11 +21,7 @@ import wx from gui.statsView import StatsView from gui.utils.numberFormatter import formatAmount - -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +from collections import OrderedDict class TargetingMiscViewFull(StatsView): @@ -48,13 +44,13 @@ class TargetingMiscViewFull(StatsView): self.panel = contentPanel self.headerPanel = headerPanel - gridTargetingMisc = wx.FlexGridSizer(1, 3) + gridTargetingMisc = wx.FlexGridSizer(1, 3, 0, 0) contentSizer.Add(gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0) gridTargetingMisc.AddGrowableCol(0) gridTargetingMisc.AddGrowableCol(2) # Targeting - gridTargeting = wx.FlexGridSizer(5, 2) + gridTargeting = wx.FlexGridSizer(5, 2, 0, 0) gridTargeting.AddGrowableCol(1) gridTargetingMisc.Add(gridTargeting, 0, wx.ALIGN_LEFT | wx.ALL, 5) @@ -79,7 +75,7 @@ class TargetingMiscViewFull(StatsView): # Misc gridTargetingMisc.Add(wx.StaticLine(contentPanel, wx.ID_ANY, style=wx.VERTICAL), 0, wx.EXPAND, 3) - gridMisc = wx.FlexGridSizer(5, 2) + gridMisc = wx.FlexGridSizer(5, 2, 0, 0) gridMisc.AddGrowableCol(1) gridTargetingMisc.Add(gridMisc, 0, wx.ALIGN_LEFT | wx.ALL, 5) @@ -87,7 +83,7 @@ class TargetingMiscViewFull(StatsView): ("Align time", "AlignTime", "s"), ("Signature", "SigRadius", "m"), ("Warp Speed", "WarpSpeed", "AU/s"), - ("Cargo", "Cargo", u"m\u00B3")) + ("Cargo", "Cargo", "m\u00B3")) for header, labelShort, unit in labels: gridMisc.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT) @@ -157,7 +153,7 @@ class TargetingMiscViewFull(StatsView): ("labelFullAlignTime", {"main": lambda: fit.alignTime}, 3, 0, 0, "s"), ("labelFullSigRadius", {"main": lambda: fit.ship.getModifiedItemAttr("signatureRadius")}, 3, 0, 9, ""), ("labelFullWarpSpeed", {"main": lambda: fit.warpSpeed}, 3, 0, 0, "AU/s"), - ("labelFullCargo", cargoValues, 4, 0, 9, u"m\u00B3")) + ("labelFullCargo", cargoValues, 4, 0, 9, "m\u00B3")) counter = 0 RADII = [("Pod", 25), ("Interceptor", 33), ("Frigate", 38), @@ -167,13 +163,13 @@ class TargetingMiscViewFull(StatsView): for labelName, valueDict, prec, lowest, highest, unit in stats: label = getattr(self, labelName) newValues = {} - for valueAlias, value in valueDict.items(): + for valueAlias, value in list(valueDict.items()): value = value() if fit is not None else 0 value = value if value is not None else 0 newValues[valueAlias] = value if self._cachedValues[counter] != newValues: mainValue = newValues["main"] - otherValues = dict((k, newValues[k]) for k in filter(lambda k: k != "main", newValues)) + otherValues = dict((k, newValues[k]) for k in [k for k in newValues if k != "main"]) if labelName == "labelFullCargo": # Get sum of all cargoholds except for maintenance bay additionalCargo = sum(otherValues.values()) @@ -215,11 +211,11 @@ class TargetingMiscViewFull(StatsView): agility = "Agility:\t%.3fx" % (fit.ship.getModifiedItemAttr("agility") or 0) label.SetToolTip(wx.ToolTip("%s\n%s\n%s" % (alignTime, mass, agility))) elif labelName == "labelFullCargo": - tipLines = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])] - for attrName, tipAlias in cargoNamesOrder.items(): + tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])] + for attrName, tipAlias in list(cargoNamesOrder.items()): if newValues[attrName] > 0: - tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) - label.SetToolTip(wx.ToolTip(u"\n".join(tipLines))) + tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) + label.SetToolTip(wx.ToolTip("\n".join(tipLines))) else: label.SetToolTip(wx.ToolTip("%.1f" % mainValue)) else: @@ -246,11 +242,11 @@ class TargetingMiscViewFull(StatsView): cachedCargo = self._cachedValues[counter] # if you add stuff to cargo, the capacity doesn't change and thus it is still cached # This assures us that we force refresh of cargo tooltip - tipLines = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])] - for attrName, tipAlias in cargoNamesOrder.items(): + tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])] + for attrName, tipAlias in list(cargoNamesOrder.items()): if cachedCargo[attrName] > 0: - tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) - label.SetToolTip(wx.ToolTip(u"\n".join(tipLines))) + tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) + label.SetToolTip(wx.ToolTip("\n".join(tipLines))) else: label.SetToolTip(wx.ToolTip("")) diff --git a/gui/builtinStatsViews/targetingMiscViewMinimal.py b/gui/builtinStatsViews/targetingMiscViewMinimal.py index 489aacf52..9d4761bf3 100644 --- a/gui/builtinStatsViews/targetingMiscViewMinimal.py +++ b/gui/builtinStatsViews/targetingMiscViewMinimal.py @@ -21,11 +21,7 @@ import wx from gui.statsView import StatsView from gui.utils.numberFormatter import formatAmount - -try: - from collections import OrderedDict -except ImportError: - from utils.compat import OrderedDict +from collections import OrderedDict class TargetingMiscViewMinimal(StatsView): @@ -48,13 +44,13 @@ class TargetingMiscViewMinimal(StatsView): self.panel = contentPanel self.headerPanel = headerPanel - gridTargetingMisc = wx.FlexGridSizer(1, 3) + gridTargetingMisc = wx.FlexGridSizer(1, 3, 0, 0) contentSizer.Add(gridTargetingMisc, 0, wx.EXPAND | wx.ALL, 0) gridTargetingMisc.AddGrowableCol(0) gridTargetingMisc.AddGrowableCol(2) # Targeting - gridTargeting = wx.FlexGridSizer(5, 2) + gridTargeting = wx.FlexGridSizer(5, 2, 0, 0) gridTargeting.AddGrowableCol(1) gridTargetingMisc.Add(gridTargeting, 0, wx.ALIGN_LEFT | wx.ALL, 5) @@ -79,7 +75,7 @@ class TargetingMiscViewMinimal(StatsView): # Misc gridTargetingMisc.Add(wx.StaticLine(contentPanel, wx.ID_ANY, style=wx.VERTICAL), 0, wx.EXPAND, 3) - gridMisc = wx.FlexGridSizer(5, 2) + gridMisc = wx.FlexGridSizer(5, 2, 0, 0) gridMisc.AddGrowableCol(1) gridTargetingMisc.Add(gridMisc, 0, wx.ALIGN_LEFT | wx.ALL, 5) @@ -87,7 +83,7 @@ class TargetingMiscViewMinimal(StatsView): ("Align time", "AlignTime", "s"), ("Signature", "SigRadius", "m"), ("Warp Speed", "WarpSpeed", "AU/s"), - ("Cargo", "Cargo", u"m\u00B3")) + ("Cargo", "Cargo", "m\u00B3")) for header, labelShort, unit in labels: gridMisc.Add(wx.StaticText(contentPanel, wx.ID_ANY, "%s: " % header), 0, wx.ALIGN_LEFT) @@ -154,7 +150,7 @@ class TargetingMiscViewMinimal(StatsView): ("labelFullAlignTime", {"main": lambda: fit.alignTime}, 3, 0, 0, "s"), ("labelFullSigRadius", {"main": lambda: fit.ship.getModifiedItemAttr("signatureRadius")}, 3, 0, 9, ""), ("labelFullWarpSpeed", {"main": lambda: fit.warpSpeed}, 3, 0, 0, "AU/s"), - ("labelFullCargo", cargoValues, 4, 0, 9, u"m\u00B3")) + ("labelFullCargo", cargoValues, 4, 0, 9, "m\u00B3")) counter = 0 RADII = [("Pod", 25), ("Interceptor", 33), ("Frigate", 38), @@ -164,13 +160,13 @@ class TargetingMiscViewMinimal(StatsView): for labelName, valueDict, prec, lowest, highest, unit in stats: label = getattr(self, labelName) newValues = {} - for valueAlias, value in valueDict.items(): + for valueAlias, value in list(valueDict.items()): value = value() if fit is not None else 0 value = value if value is not None else 0 newValues[valueAlias] = value if self._cachedValues[counter] != newValues: mainValue = newValues["main"] - otherValues = dict((k, newValues[k]) for k in filter(lambda k: k != "main", newValues)) + otherValues = dict((k, newValues[k]) for k in [k for k in newValues if k != "main"]) if labelName == "labelFullCargo": # Get sum of all cargoholds except for maintenance bay additionalCargo = sum(otherValues.values()) @@ -209,11 +205,11 @@ class TargetingMiscViewMinimal(StatsView): agility = "Agility:\t%.3fx" % (fit.ship.getModifiedItemAttr("agility") or 0) label.SetToolTip(wx.ToolTip("%s\n%s\n%s" % (alignTime, mass, agility))) elif labelName == "labelFullCargo": - tipLines = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])] - for attrName, tipAlias in cargoNamesOrder.items(): + tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, newValues["main"])] + for attrName, tipAlias in list(cargoNamesOrder.items()): if newValues[attrName] > 0: - tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) - label.SetToolTip(wx.ToolTip(u"\n".join(tipLines))) + tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, newValues[attrName])) + label.SetToolTip(wx.ToolTip("\n".join(tipLines))) else: label.SetToolTip(wx.ToolTip("%.1f" % mainValue)) else: @@ -242,11 +238,11 @@ class TargetingMiscViewMinimal(StatsView): cachedCargo = self._cachedValues[counter] # if you add stuff to cargo, the capacity doesn't change and thus it is still cached # This assures us that we force refresh of cargo tooltip - tipLines = [u"Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])] - for attrName, tipAlias in cargoNamesOrder.items(): + tipLines = ["Cargohold: {:,.2f}m\u00B3 / {:,.2f}m\u00B3".format(fit.cargoBayUsed, cachedCargo["main"])] + for attrName, tipAlias in list(cargoNamesOrder.items()): if cachedCargo[attrName] > 0: - tipLines.append(u"{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) - label.SetToolTip(wx.ToolTip(u"\n".join(tipLines))) + tipLines.append("{}: {:,.2f}m\u00B3".format(tipAlias, cachedCargo[attrName])) + label.SetToolTip(wx.ToolTip("\n".join(tipLines))) else: label.SetToolTip(wx.ToolTip("")) diff --git a/gui/builtinViewColumns/ammo.py b/gui/builtinViewColumns/ammo.py index 069328a32..862148285 100644 --- a/gui/builtinViewColumns/ammo.py +++ b/gui/builtinViewColumns/ammo.py @@ -21,7 +21,7 @@ import wx from eos.saveddata.fighter import Fighter from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class Ammo(ViewColumn): diff --git a/gui/builtinViewColumns/attributeDisplay.py b/gui/builtinViewColumns/attributeDisplay.py index 855bfe2a1..7ad8743d7 100644 --- a/gui/builtinViewColumns/attributeDisplay.py +++ b/gui/builtinViewColumns/attributeDisplay.py @@ -21,7 +21,7 @@ import wx from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from service.attribute import Attribute @@ -72,7 +72,7 @@ class AttributeDisplay(ViewColumn): def getText(self, mod): if hasattr(mod, "item"): - attr = mod.getModifiedItemAttr(self.info.name) + attr = mod.getModifiedItemAttr(self.info.name, None) else: if self.direct: info = self.directInfo @@ -80,16 +80,19 @@ class AttributeDisplay(ViewColumn): else: attr = mod.getAttribute(self.info.name) + if attr is None: + return "" + if self.info.name == "volume": str_ = (formatAmount(attr, 3, 0, 3)) if hasattr(mod, "amount"): - str_ += u"m\u00B3 (%s m\u00B3)" % (formatAmount(attr * mod.amount, 3, 0, 3)) + str_ += "m\u00B3 (%s m\u00B3)" % (formatAmount(attr * mod.amount, 3, 0, 3)) attr = str_ if isinstance(attr, (float, int)): attr = (formatAmount(attr, 3, 0, 3)) - return attr if attr is not None else "" + return attr def getImageId(self, mod): return -1 diff --git a/gui/builtinViewColumns/baseName.py b/gui/builtinViewColumns/baseName.py index 3a8bc6fa7..8ed1466ac 100644 --- a/gui/builtinViewColumns/baseName.py +++ b/gui/builtinViewColumns/baseName.py @@ -71,9 +71,9 @@ class BaseName(ViewColumn): elif isinstance(stuff, Rack): if FitSvc.getInstance().serviceFittingOptions["rackLabels"]: if stuff.slot == Slot.MODE: - return u'─ Tactical Mode ─' + return '─ Tactical Mode ─' else: - return u'─ {} Slots ─'.format(Slot.getName(stuff.slot).capitalize()) + return '─ {} Slots ─'.format(Slot.getName(stuff.slot).capitalize()) else: return "" elif isinstance(stuff, Module): @@ -91,7 +91,7 @@ class BaseName(ViewColumn): if marketShortcut: # use unicode subscript to display shortcut value - shortcut = unichr(marketShortcut + 8320) + u" " + shortcut = chr(marketShortcut + 8320) + " " del item.marketShortcut return shortcut + item.name diff --git a/gui/builtinViewColumns/capacitorUse.py b/gui/builtinViewColumns/capacitorUse.py index 5d0287375..8770a9d4c 100644 --- a/gui/builtinViewColumns/capacitorUse.py +++ b/gui/builtinViewColumns/capacitorUse.py @@ -24,7 +24,7 @@ from eos.saveddata.mode import Mode from service.attribute import Attribute from gui.utils.numberFormatter import formatAmount from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class CapacitorUse(ViewColumn): diff --git a/gui/builtinViewColumns/maxRange.py b/gui/builtinViewColumns/maxRange.py index d4b3367da..ef420c344 100644 --- a/gui/builtinViewColumns/maxRange.py +++ b/gui/builtinViewColumns/maxRange.py @@ -23,7 +23,7 @@ import wx from eos.saveddata.mode import Mode from service.attribute import Attribute from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount @@ -58,7 +58,7 @@ class MaxRange(ViewColumn): if isinstance(stuff, Mode): return "" - maxRange = stuff.maxRange if hasattr(stuff, "maxRange") else stuff.getModifiedItemAttr("maxRange") + maxRange = stuff.maxRange if hasattr(stuff, "maxRange") else stuff.getModifiedItemAttr("maxRange", None) falloff = stuff.falloff if falloff: falloff = "+%sm" % formatAmount(falloff, 3, 0, 3) diff --git a/gui/builtinViewColumns/misc.py b/gui/builtinViewColumns/misc.py index a3c84b4a7..668979c1f 100644 --- a/gui/builtinViewColumns/misc.py +++ b/gui/builtinViewColumns/misc.py @@ -24,7 +24,7 @@ from service.fit import Fit from service.market import Market import gui.mainFrame from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount from gui.utils.listFormatter import formatList from eos.saveddata.drone import Drone @@ -201,7 +201,7 @@ class Miscellanea(ViewColumn): "falloff range": falloffRangeBonus, "tracking speed": trackingSpeedBonus} - isTrackingDisruptor = any(map(lambda x: x is not None and x != 0, trackingDisruptorAttributes.values())) + isTrackingDisruptor = any([x is not None and x != 0 for x in list(trackingDisruptorAttributes.values())]) # Then get the attributes for guidance disruptors explosionVelocityBonus = stuff.getModifiedItemAttr("aoeVelocityBonus") @@ -216,7 +216,7 @@ class Miscellanea(ViewColumn): "flight time": flightTimeBonus, "missile velocity": missileVelocityBonus} - isGuidanceDisruptor = any(map(lambda x: x is not None and x != 0, guidanceDisruptorAttributes.values())) + isGuidanceDisruptor = any([x is not None and x != 0 for x in list(guidanceDisruptorAttributes.values())]) if isTrackingDisruptor: attributes = trackingDisruptorAttributes @@ -225,12 +225,12 @@ class Miscellanea(ViewColumn): else: return "", None - display = max(attributes.values(), key=lambda x: abs(x)) + display = max(list(attributes.values()), key=lambda x: abs(x)) text = "{0}%".format(formatAmount(display, 3, 0, 3, forceSign=True)) ttEntries = [] - for attributeName, attributeValue in attributes.items(): + for attributeName, attributeValue in list(attributes.items()): if attributeValue == display: ttEntries.append(attributeName) diff --git a/gui/builtinViewColumns/price.py b/gui/builtinViewColumns/price.py index 3797052a2..728376bef 100644 --- a/gui/builtinViewColumns/price.py +++ b/gui/builtinViewColumns/price.py @@ -24,7 +24,7 @@ from eos.saveddata.cargo import Cargo from eos.saveddata.drone import Drone from service.price import Price as ServicePrice from gui.viewColumn import ViewColumn -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.numberFormatter import formatAmount diff --git a/gui/builtinViews/emptyView.py b/gui/builtinViews/emptyView.py index ebf667726..3e6d2b467 100644 --- a/gui/builtinViews/emptyView.py +++ b/gui/builtinViews/emptyView.py @@ -1,7 +1,7 @@ # noinspection PyPackageRequirements import wx import gui.globalEvents as GE -from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED +from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED import gui.mainFrame @@ -13,12 +13,13 @@ class BlankPage(wx.Panel): self.parent = parent self.parent.Bind(EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=None)) def Destroy(self): - self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED, handler=self.pageChanged) + # todo: This unbind caused fits to not recalc when switching to their tabs; find out why + # self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED) wx.Panel.Destroy(self) def pageChanged(self, event): diff --git a/gui/builtinViews/entityEditor.py b/gui/builtinViews/entityEditor.py index 6bde57a11..b706c4a76 100644 --- a/gui/builtinViews/entityEditor.py +++ b/gui/builtinViews/entityEditor.py @@ -1,11 +1,11 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader -class BaseValidator(wx.PyValidator): +class BaseValidator(wx.Validator): def __init__(self): - wx.PyValidator.__init__(self) + wx.Validator.__init__(self) def Validate(self, win): raise NotImplementedError() @@ -22,7 +22,9 @@ class TextEntryValidatedDialog(wx.TextEntryDialog): wx.TextEntryDialog.__init__(self, parent, *args, **kargs) self.parent = parent - self.txtctrl = self.FindWindowById(3000) + # See https://github.com/wxWidgets/Phoenix/issues/611 + self.txtctrl = self.FindWindowById(3000, self) + if validator: self.txtctrl.SetValidator(validator()) @@ -42,7 +44,7 @@ class EntityEditor(wx.Panel): self.choices = [] self.choices.sort(key=lambda p: p.name) - self.entityChoices = wx.Choice(self, choices=map(lambda p: p.name, self.choices)) + self.entityChoices = wx.Choice(self, choices=[p.name for p in self.choices]) self.navSizer.Add(self.entityChoices, 1, wx.ALL, 5) buttons = (("new", wx.ART_NEW, self.OnNew), @@ -60,7 +62,7 @@ class EntityEditor(wx.Panel): btn.SetMinSize(size) btn.SetMaxSize(size) - btn.SetToolTipString("{} {}".format(name.capitalize(), self.entityName)) + btn.SetToolTip("{} {}".format(name.capitalize(), self.entityName)) btn.Bind(wx.EVT_BUTTON, func) setattr(self, "btn%s" % name.capitalize(), btn) self.navSizer.Add(btn, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 2) @@ -96,8 +98,8 @@ class EntityEditor(wx.Panel): def OnNew(self, event): dlg = TextEntryValidatedDialog(self, self.validator, - u"Enter a name for your new {}:".format(self.entityName), - u"New {}".format(self.entityName)) + "Enter a name for your new {}:".format(self.entityName), + "New {}".format(self.entityName)) dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: @@ -110,10 +112,10 @@ class EntityEditor(wx.Panel): def OnCopy(self, event): dlg = TextEntryValidatedDialog(self, self.validator, - u"Enter a name for your {} copy:".format(self.entityName), - u"Copy {}".format(self.entityName)) + "Enter a name for your {} copy:".format(self.entityName), + "Copy {}".format(self.entityName)) active = self.getActiveEntity() - dlg.SetValue(u"{} Copy".format(active.name)) + dlg.SetValue("{} Copy".format(active.name)) dlg.txtctrl.SetInsertionPointEnd() dlg.CenterOnParent() @@ -124,8 +126,8 @@ class EntityEditor(wx.Panel): def OnRename(self, event): dlg = TextEntryValidatedDialog(self, self.validator, - u"Enter a new name for your {}:".format(self.entityName), - u"Rename {}".format(self.entityName)) + "Enter a new name for your {}:".format(self.entityName), + "Rename {}".format(self.entityName)) active = self.getActiveEntity() dlg.SetValue(active.name) dlg.txtctrl.SetInsertionPointEnd() @@ -138,9 +140,9 @@ class EntityEditor(wx.Panel): def OnDelete(self, event): dlg = wx.MessageDialog(self, - u"Do you really want to delete the {} {}?".format(self.getActiveEntity().name, + "Do you really want to delete the {} {}?".format(self.getActiveEntity().name, self.entityName), - u"Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) + "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_YES: @@ -152,7 +154,7 @@ class EntityEditor(wx.Panel): self.choices = self.getEntitiesFromContext() self.entityChoices.Clear() - self.entityChoices.AppendItems(map(lambda p: p.name, self.choices)) + self.entityChoices.AppendItems([p.name for p in self.choices]) if selected: idx = self.choices.index(selected) self.entityChoices.SetSelection(idx) diff --git a/gui/builtinViews/fittingView.py b/gui/builtinViews/fittingView.py index 5702503f3..362de074b 100644 --- a/gui/builtinViews/fittingView.py +++ b/gui/builtinViews/fittingView.py @@ -25,15 +25,15 @@ import gui.mainFrame from gui.builtinMarketBrowser.events import ItemSelected, ITEM_SELECTED import gui.display as d from gui.contextMenu import ContextMenu -import gui.builtinShipBrowser.events as sbEvents +from gui.builtinShipBrowser.events import EVT_FIT_RENAMED, EVT_FIT_REMOVED, FitSelected, EVT_FIT_SELECTED import gui.multiSwitch from eos.saveddata.mode import Mode from eos.saveddata.module import Module, Slot, Rack from gui.builtinViewColumns.state import State -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import gui.builtinViews.emptyView from logbook import Logger -from gui.chromeTabs import EVT_NOTEBOOK_PAGE_CHANGED +from gui.chrome_tabs import EVT_NOTEBOOK_PAGE_CHANGED from service.fit import Fit from service.market import Market @@ -50,12 +50,13 @@ class FitSpawner(gui.multiSwitch.TabSpawner): def __init__(self, multiSwitch): self.multiSwitch = multiSwitch self.mainFrame = mainFrame = gui.mainFrame.MainFrame.getInstance() - mainFrame.Bind(sbEvents.EVT_FIT_SELECTED, self.fitSelected) - self.multiSwitch.tabsContainer.handleDrag = self.handleDrag + mainFrame.Bind(EVT_FIT_SELECTED, self.fitSelected) + self.multiSwitch.tabs_container.handleDrag = self.handleDrag def fitSelected(self, event): count = -1 - for index, page in enumerate(self.multiSwitch.pages): + # @todo pheonix: _pages is supposed to be private? + for index, page in enumerate(self.multiSwitch._pages): if not isinstance(page, gui.builtinViews.emptyView.BlankPage): # Don't try and process it if it's a blank page. try: if page.activeFitID == event.fitID: @@ -76,8 +77,13 @@ class FitSpawner(gui.multiSwitch.TabSpawner): if from_import or (not openFitInNew and mstate.CmdDown()) or startup or (openFitInNew and not mstate.CmdDown()): self.multiSwitch.AddPage() - view = FittingView(self.multiSwitch) - self.multiSwitch.ReplaceActivePage(view) + view = self.multiSwitch.GetSelectedPage() + + if not isinstance(view, FittingView): + view = FittingView(self.multiSwitch) + print("###################### Created new view:" + repr(view)) + self.multiSwitch.ReplaceActivePage(view) + view.fitSelected(event) def handleDrag(self, type, fitID): @@ -103,12 +109,12 @@ FitSpawner.register() # Drag'n'drop handler -class FittingViewDrop(wx.PyDropTarget): +class FittingViewDrop(wx.DropTarget): def __init__(self, dropFn, *args, **kwargs): super(FittingViewDrop, self).__init__(*args, **kwargs) self.dropFn = dropFn # this is really transferring an EVE itemID - self.dropData = wx.PyTextDataObject() + self.dropData = wx.TextDataObject() self.SetDataObject(self.dropData) def OnData(self, x, y, t): @@ -139,8 +145,8 @@ class FittingView(d.Display): self.Show(False) self.parent = parent self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) - self.mainFrame.Bind(sbEvents.EVT_FIT_RENAMED, self.fitRenamed) - self.mainFrame.Bind(sbEvents.EVT_FIT_REMOVED, self.fitRemoved) + self.mainFrame.Bind(EVT_FIT_RENAMED, self.fitRenamed) + self.mainFrame.Bind(EVT_FIT_REMOVED, self.fitRemoved) self.mainFrame.Bind(ITEM_SELECTED, self.appendItem) self.Bind(wx.EVT_LEFT_DCLICK, self.removeItem) @@ -166,6 +172,8 @@ class FittingView(d.Display): self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) self.parent.Bind(EVT_NOTEBOOK_PAGE_CHANGED, self.pageChanged) + print("------------------ new fitting view -------------------") + print(self) def OnLeaveWindow(self, event): self.SetToolTip(None) @@ -185,7 +193,7 @@ class FittingView(d.Display): mod = self.mods[self.GetItemData(row)] tooltip = self.activeColumns[col].getToolTip(mod) if tooltip is not None: - self.SetToolTipString(tooltip) + self.SetToolTip(tooltip) else: self.SetToolTip(None) else: @@ -211,14 +219,15 @@ class FittingView(d.Display): def handleDrag(self, type, fitID): # Those are drags coming from pyfa sources, NOT builtin wx drags if type == "fit": - wx.PostEvent(self.mainFrame, sbEvents.FitSelected(fitID=fitID)) + wx.PostEvent(self.mainFrame, FitSelected(fitID=fitID)) def Destroy(self): - self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED, handler=self.pageChanged) - self.mainFrame.Unbind(GE.FIT_CHANGED, handler=self.fitChanged) - self.mainFrame.Unbind(sbEvents.EVT_FIT_RENAMED, handler=self.fitRenamed) - self.mainFrame.Unbind(sbEvents.EVT_FIT_REMOVED, handler=self.fitRemoved) - self.mainFrame.Unbind(ITEM_SELECTED, handler=self.appendItem) + print("+++++ Destroy " + repr(self)) + print(self.parent.Unbind(EVT_NOTEBOOK_PAGE_CHANGED)) + print(self.mainFrame.Unbind(GE.FIT_CHANGED)) + print(self.mainFrame.Unbind(EVT_FIT_RENAMED)) + print(self.mainFrame.Unbind(EVT_FIT_REMOVED)) + print(self.mainFrame.Unbind(ITEM_SELECTED)) d.Display.Destroy(self) @@ -238,7 +247,7 @@ class FittingView(d.Display): row = event.GetIndex() if row != -1 and row not in self.blanks and isinstance(self.mods[row], Module) and not self.mods[row].isEmpty: - data = wx.PyTextDataObject() + data = wx.TextDataObject() dataStr = "fitting:" + str(self.mods[row].modPosition) data.SetText(dataStr) @@ -278,20 +287,22 @@ class FittingView(d.Display): If fit is removed and active, the page is deleted. We also refresh the fit of the new current page in case delete fit caused change in stats (projected) + todo: move this to the notebook, not the page. We don't want the page being responsible for deleting itself """ + print('_+_+_+_+_+_ Fit Removed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID)) pyfalog.debug("FittingView::fitRemoved") if event.fitID == self.getActiveFit(): pyfalog.debug(" Deleted fit is currently active") self.parent.DeletePage(self.parent.GetPageIndex(self)) - try: - # Sometimes there is no active page after deletion, hence the try block - sFit = Fit.getInstance() - sFit.refreshFit(self.getActiveFit()) - wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) - except wx._core.PyDeadObjectError: - pyfalog.warning("Caught dead object") - pass + try: + # Sometimes there is no active page after deletion, hence the try block + sFit = Fit.getInstance() + sFit.refreshFit(self.getActiveFit()) + wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.activeFitID)) + except RuntimeError: + pyfalog.warning("Caught dead object") + pass event.Skip() @@ -303,6 +314,8 @@ class FittingView(d.Display): event.Skip() def fitSelected(self, event): + print('====== Fit Selected: ' + repr(self) + str(bool(self))) + if self.parent.IsActive(self): fitID = event.fitID startup = getattr(event, "startup", False) @@ -313,6 +326,7 @@ class FittingView(d.Display): self.Show(fitID is not None) self.slotsChanged() sFit.switchFit(fitID) + # @todo pheonix: had to disable this as it was causing a crash at the wxWidgets level. Dunno why, investigate wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) event.Skip() @@ -322,7 +336,7 @@ class FittingView(d.Display): fit = sFit.getFit(self.getActiveFit(), basic=True) bitmap = BitmapLoader.getImage("race_%s_small" % fit.ship.item.race, "gui") - text = u"%s: %s" % (fit.ship.item.name, fit.name) + text = "%s: %s" % (fit.ship.item.name, fit.name) pageIndex = self.parent.GetPageIndex(self) if pageIndex is not None: @@ -509,6 +523,8 @@ class FittingView(d.Display): self.populate(self.mods) def fitChanged(self, event): + print('====== Fit Changed: {} {} activeFitID: {}, eventFitID: {}'.format(repr(self), str(bool(self)), self.activeFitID, event.fitID)) + try: if self.activeFitID is not None and self.activeFitID == event.fitID: self.generateMods() @@ -519,7 +535,7 @@ class FittingView(d.Display): self.Refresh() self.Show(self.activeFitID is not None and self.activeFitID == event.fitID) - except wx._core.PyDeadObjectError: + except RuntimeError: pyfalog.error("Caught dead object") finally: event.Skip() @@ -604,14 +620,14 @@ class FittingView(d.Display): sFit = Fit.getInstance() fitID = self.mainFrame.getActiveFit() - ctrl = wx.GetMouseState().CmdDown() or wx.GetMouseState().MiddleDown() + ctrl = event.cmdDown or event.middleIsDown click = "ctrl" if ctrl is True else "right" if event.GetButton() == 3 else "left" sFit.toggleModulesState(fitID, self.mods[self.GetItemData(row)], mods, click) # update state tooltip tooltip = self.activeColumns[col].getToolTip(self.mods[self.GetItemData(row)]) if tooltip: - self.SetToolTipString(tooltip) + self.SetToolTip(tooltip) wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=self.mainFrame.getActiveFit())) else: @@ -645,7 +661,7 @@ class FittingView(d.Display): slot = Slot.getValue(slotType) slotMap[slot] = fit.getSlotsFree(slot) < 0 - font = (self.GetClassDefaultAttributes()).font + font = wx.Font(self.GetClassDefaultAttributes().font) for i, mod in enumerate(self.mods): self.SetItemBackgroundColour(i, self.GetBackgroundColour()) @@ -670,15 +686,15 @@ class FittingView(d.Display): self.Thaw() self.itemCount = self.GetItemCount() - if 'wxMac' in wx.PlatformInfo: - try: - self.MakeSnapshot() - except Exception as e: - pyfalog.critical("Failed to make snapshot") - pyfalog.critical(e) + # if 'wxMac' in wx.PlatformInfo: + # try: + # self.MakeSnapshot() + # except Exception as e: + # pyfalog.critical("Failed to make snapshot") + # pyfalog.critical(e) def OnShow(self, event): - if event.GetShow(): + if self and not self.IsShown(): try: self.MakeSnapshot() except Exception as e: @@ -691,14 +707,13 @@ class FittingView(d.Display): # noinspection PyPropertyAccess def MakeSnapshot(self, maxColumns=1337): - if self.FVsnapshot: del self.FVsnapshot - tbmp = wx.EmptyBitmap(16, 16) + tbmp = wx.Bitmap(16, 16) tdc = wx.MemoryDC() tdc.SelectObject(tbmp) - font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) tdc.SetFont(font) columnsWidths = [] @@ -711,6 +726,7 @@ class FittingView(d.Display): except Exception as e: pyfalog.critical("Failed to get fit") pyfalog.critical(e) + return if fit is None: return @@ -732,7 +748,7 @@ class FittingView(d.Display): break name = col.getText(st) - if not isinstance(name, basestring): + if not isinstance(name, str): name = "" nx, ny = tdc.GetTextExtent(name) @@ -762,7 +778,7 @@ class FittingView(d.Display): name = col.columnText imgId = col.imageId - if not isinstance(name, basestring): + if not isinstance(name, str): name = "" opts = wx.HeaderButtonParams() @@ -771,7 +787,7 @@ class FittingView(d.Display): opts.m_labelText = name if imgId != -1: - opts.m_labelBitmap = wx.EmptyBitmap(isize, isize) + opts.m_labelBitmap = wx.Bitmap(isize, isize) width = render.DrawHeaderButton(self, tdc, (0, 0, 16, 16), sortArrow=wx.HDR_SORT_ICON_NONE, params=opts) @@ -787,15 +803,15 @@ class FittingView(d.Display): maxWidth += columnsWidths[i] mdc = wx.MemoryDC() - mbmp = wx.EmptyBitmap(maxWidth, maxRowHeight * rows + padding * 4 + headerSize) + mbmp = wx.Bitmap(maxWidth, maxRowHeight * rows + padding * 4 + headerSize) mdc.SelectObject(mbmp) - mdc.SetBackground(wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))) + mdc.SetBackground(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))) mdc.Clear() mdc.SetFont(font) - mdc.SetTextForeground(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT)) + mdc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)) cx = padding for i, col in enumerate(self.activeColumns): @@ -805,7 +821,7 @@ class FittingView(d.Display): name = col.columnText imgId = col.imageId - if not isinstance(name, basestring): + if not isinstance(name, str): name = "" opts = wx.HeaderButtonParams() @@ -839,7 +855,7 @@ class FittingView(d.Display): break name = col.getText(st) - if not isinstance(name, basestring): + if not isinstance(name, str): name = "" imgId = col.getImageId(st) diff --git a/gui/builtinViews/implantEditor.py b/gui/builtinViews/implantEditor.py index 827d514a2..b391c1812 100644 --- a/gui/builtinViews/implantEditor.py +++ b/gui/builtinViews/implantEditor.py @@ -5,7 +5,7 @@ from wx.lib.buttons import GenBitmapButton import gui.builtinMarketBrowser.pfSearchBox as SBox import gui.display as d -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.marketBrowser import SearchBox from service.market import Market @@ -23,7 +23,7 @@ class BaseImplantEditorView(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -54,7 +54,7 @@ class BaseImplantEditorView(wx.Panel): pmainSizer.Add(availableSizer, 1, wx.ALL | wx.EXPAND, 5) buttonSizer = wx.BoxSizer(wx.VERTICAL) - buttonSizer.AddSpacer((0, 0), 1) + buttonSizer.AddStretchSpacer() self.btnAdd = GenBitmapButton(self, wx.ID_ADD, BitmapLoader.getBitmap("fit_add_small", "gui"), style=wx.BORDER_NONE) @@ -64,7 +64,7 @@ class BaseImplantEditorView(wx.Panel): style=wx.BORDER_NONE) buttonSizer.Add(self.btnRemove, 0) - buttonSizer.AddSpacer((0, 0), 1) + buttonSizer.AddStretchSpacer() pmainSizer.Add(buttonSizer, 0, wx.EXPAND, 0) characterImplantSizer = wx.BoxSizer(wx.VERTICAL) @@ -79,7 +79,7 @@ class BaseImplantEditorView(wx.Panel): sMkt = Market.getInstance() for mktGrp in sMkt.getImplantTree(): iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(mktGrp)) - childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=wx.TreeItemData(mktGrp.ID)) + childId = self.availableImplantsTree.AppendItem(root, mktGrp.name, iconId, data=mktGrp.ID) if sMkt.marketGroupHasTypesCheck(mktGrp) is False: self.availableImplantsTree.AppendItem(childId, "dummy") @@ -142,10 +142,10 @@ class BaseImplantEditorView(wx.Panel): # if the dummy item is a market group, replace with actual market groups if text == "dummy": # Add 'real stoof!' instead - currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent), eager="children") + currentMktGrp = sMkt.getMarketGroup(tree.GetItemData(parent), eager="children") for childMktGrp in sMkt.getMarketGroupChildren(currentMktGrp): iconId = self.addMarketViewImage(sMkt.getIconByMarketGroup(childMktGrp)) - childId = tree.AppendItem(parent, childMktGrp.name, iconId, data=wx.TreeItemData(childMktGrp.ID)) + childId = tree.AppendItem(parent, childMktGrp.name, iconId, data=childMktGrp.ID) if sMkt.marketGroupHasTypesCheck(childMktGrp) is False: tree.AppendItem(childId, "dummy") else: @@ -153,11 +153,11 @@ class BaseImplantEditorView(wx.Panel): # replace dummy with actual items if text == "itemdummy": - currentMktGrp = sMkt.getMarketGroup(tree.GetPyData(parent)) + currentMktGrp = sMkt.getMarketGroup(tree.GetItemData(parent)) items = sMkt.getItemsByMarketGroup(currentMktGrp) for item in items: iconId = self.addMarketViewImage(item.icon.iconFile) - tree.AppendItem(parent, item.name, iconId, data=wx.TreeItemData(item)) + tree.AppendItem(parent, item.name, iconId, data=item) tree.SortChildren(parent) @@ -185,7 +185,7 @@ class BaseImplantEditorView(wx.Panel): nchilds = self.availableImplantsTree.GetChildrenCount(root) if nchilds == 0: - item = self.availableImplantsTree.GetPyData(root) + item = self.availableImplantsTree.GetItemData(root) self.addImplantToContext(item) else: event.Skip() diff --git a/gui/cachingImageList.py b/gui/cachingImageList.py index 4bbef1e23..578da97c7 100644 --- a/gui/cachingImageList.py +++ b/gui/cachingImageList.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class CachingImageList(wx.ImageList): diff --git a/gui/characterEditor.py b/gui/characterEditor.py index 3ef4b1ba8..c7a2fef8c 100644 --- a/gui/characterEditor.py +++ b/gui/characterEditor.py @@ -19,27 +19,33 @@ # noinspection PyPackageRequirements import wx +import wx.dataview +import wx.lib.agw.hyperlink -from utils.floatspin import FloatSpin # noinspection PyPackageRequirements import wx.lib.newevent # noinspection PyPackageRequirements -import wx.gizmos -from gui.bitmapLoader import BitmapLoader +from wx.dataview import TreeListCtrl +from gui.bitmap_loader import BitmapLoader from gui.contextMenu import ContextMenu import gui.globalEvents as GE from gui.builtinViews.implantEditor import BaseImplantEditorView from gui.builtinViews.entityEditor import EntityEditor, BaseValidator, TextEntryValidatedDialog from service.fit import Fit from service.character import Character +from service.esi import Esi from service.network import AuthenticationError, TimeoutError from service.market import Market from logbook import Logger +from wx.lib.agw.floatspin import FloatSpin + + from gui.utils.clipboard import toClipboard, fromClipboard -import utils.roman as roman +import roman import re +import webbrowser pyfalog = Logger(__name__) @@ -72,9 +78,9 @@ class CharacterTextValidor(BaseValidator): raise ValueError("Character name already in use, please choose another.") return True - except ValueError, e: + except ValueError as e: pyfalog.error(e) - wx.MessageBox(u"{}".format(e), "Error") + wx.MessageBox("{}".format(e), "Error") textCtrl.SetFocus() return False @@ -143,10 +149,10 @@ class CharacterEntityEditor(EntityEditor): class CharacterEditor(wx.Frame): def __init__(self, parent): - wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=u"pyfa: Character Editor", pos=wx.DefaultPosition, + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="pyfa: Character Editor", pos=wx.DefaultPosition, size=wx.Size(640, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER) - i = wx.IconFromBitmap(BitmapLoader.getBitmap("character_small", "gui")) + i = wx.Icon(BitmapLoader.getBitmap("character_small", "gui")) self.SetIcon(i) self.mainFrame = parent @@ -170,7 +176,7 @@ class CharacterEditor(wx.Frame): self.viewsNBContainer.AddPage(self.sview, "Skills") self.viewsNBContainer.AddPage(self.iview, "Implants") - self.viewsNBContainer.AddPage(self.aview, "API") + self.viewsNBContainer.AddPage(self.aview, "EVE SSO") mainSizer.Add(self.viewsNBContainer, 1, wx.EXPAND | wx.ALL, 5) @@ -303,7 +309,7 @@ class SkillTreeView(wx.Panel): wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.TAB_TRAVERSAL) self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.VERTICAL) @@ -338,35 +344,38 @@ class SkillTreeView(wx.Panel): self.searchTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.populateSkillTreeSkillSearch, self.searchTimer) - tree = self.skillTreeListCtrl = wx.gizmos.TreeListCtrl(self, wx.ID_ANY, style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT) + tree = self.skillTreeListCtrl = TreeListCtrl(self, wx.ID_ANY, style=wx.dataview.TL_DEFAULT_STYLE) pmainSizer.Add(tree, 1, wx.EXPAND | wx.ALL, 5) self.imageList = wx.ImageList(16, 16) tree.SetImageList(self.imageList) - self.skillBookImageId = self.imageList.Add(BitmapLoader.getBitmap("skill_small", "gui")) + self.skillBookImageId = self.imageList.Add(wx.Icon(BitmapLoader.getBitmap("skill_small", "gui"))) + self.skillBookDirtyImageId = self.imageList.Add(wx.Icon(BitmapLoader.getBitmap("skill_small_red", "gui"))) - tree.AddColumn("Skill") - tree.AddColumn("Level") - tree.SetMainColumn(0) + tree.AppendColumn("Skill") + tree.AppendColumn("Level") + # tree.SetMainColumn(0) - self.root = tree.AddRoot("Skills") - tree.SetItemText(self.root, "Levels", 1) + self.root = tree.GetRootItem() + # self.root = tree.AppendItem(root, "Skills") + # + # tree.SetItemText(self.root, 1, "Levels") - tree.SetColumnWidth(0, 500) + # tree.SetColumnWidth(0, 300) self.btnSecStatus = wx.Button(self, wx.ID_ANY, "Sec Status: {0:.2f}".format(char.secStatus or 0.0)) self.btnSecStatus.Bind(wx.EVT_BUTTON, self.onSecStatus) self.populateSkillTree() - tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) - tree.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.scheduleMenu) + tree.Bind(wx.dataview.EVT_TREELIST_ITEM_EXPANDING, self.expandLookup) + tree.Bind(wx.dataview.EVT_TREELIST_ITEM_CONTEXT_MENU, self.scheduleMenu) bSizerButtons = wx.BoxSizer(wx.HORIZONTAL) bSizerButtons.Add(self.btnSecStatus, 0, wx.ALL, 5) - bSizerButtons.AddSpacer((0, 0), 1, wx.EXPAND, 5) + bSizerButtons.AddStretchSpacer() importExport = (("Import", wx.ART_FILE_OPEN, "from"), ("Export", wx.ART_FILE_SAVE_AS, "to")) @@ -381,7 +390,7 @@ class SkillTreeView(wx.Panel): btn.Layout() setattr(self, "{}Btn".format(name.lower()), btn) btn.Enable(True) - btn.SetToolTipString("%s skills %s clipboard" % (name, direction)) + btn.SetToolTip("%s skills %s clipboard" % (name, direction)) bSizerButtons.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT | wx.ALL, 5) btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Skills".format(name.lower()))) @@ -403,7 +412,7 @@ class SkillTreeView(wx.Panel): self.levelIds[idUnlearned] = "Not learned" self.levelChangeMenu.Append(idUnlearned, "Unlearn") - for level in xrange(6): + for level in range(6): id = wx.NewId() self.levelIds[id] = level self.levelChangeMenu.Append(id, "Level %d" % level) @@ -505,15 +514,17 @@ class SkillTreeView(wx.Panel): root = self.root tree = self.skillTreeListCtrl - tree.DeleteChildren(root) + tree.DeleteAllItems() for id, name in sChar.getSkillsByName(search): iconId = self.skillBookImageId - childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(('skill', id))) level, dirty = sChar.getSkillLevel(char.ID, id) - tree.SetItemText(childId, "Level %d" % int(level) if isinstance(level, float) else level, 1) + if dirty: - tree.SetItemTextColour(childId, wx.BLUE) + iconId = self.skillBookDirtyImageId + + childId = tree.AppendItem(root, name, iconId, data=('skill', id)) + tree.SetItemText(childId, 1, "Level %d" % int(level) if isinstance(level, float) else level) def populateSkillTree(self, event=None): sChar = Character.getInstance() @@ -528,56 +539,56 @@ class SkillTreeView(wx.Panel): self.btnSecStatus.Enable() groups = sChar.getSkillGroups() - imageId = self.skillBookImageId root = self.root tree = self.skillTreeListCtrl - tree.DeleteChildren(root) + tree.DeleteAllItems() for id, name in groups: - childId = tree.AppendItem(root, name, imageId) - tree.SetPyData(childId, ('group', id)) - tree.AppendItem(childId, "dummy") + imageId = self.skillBookImageId if id in dirtyGroups: - tree.SetItemTextColour(childId, wx.BLUE) + imageId = self.skillBookDirtyImageId - tree.SortChildren(root) + childId = tree.AppendItem(root, name, imageId, data=('group', id)) + tree.AppendItem(childId, "dummy") if event: event.Skip() def expandLookup(self, event): - root = event.Item + root = event.GetItem() tree = self.skillTreeListCtrl - child, cookie = tree.GetFirstChild(root) + child = tree.GetFirstChild(root) if tree.GetItemText(child) == "dummy": - tree.Delete(child) + tree.DeleteItem(child) # Get the real intrestin' stuff sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() - data = tree.GetPyData(root) + data = tree.GetItemData(root) for id, name in sChar.getSkills(data[1]): iconId = self.skillBookImageId - childId = tree.AppendItem(root, name, iconId, data=wx.TreeItemData(('skill', id))) level, dirty = sChar.getSkillLevel(char.ID, id) - tree.SetItemText(childId, "Level %d" % int(level) if isinstance(level, float) else level, 1) - if dirty: - tree.SetItemTextColour(childId, wx.BLUE) - tree.SortChildren(root) + if dirty: + iconId = self.skillBookDirtyImageId + + childId = tree.AppendItem(root, name, iconId, data=('skill', id)) + + tree.SetItemText(childId, 1, "Level %d" % int(level) if isinstance(level, float) else level) def scheduleMenu(self, event): event.Skip() - wx.CallAfter(self.spawnMenu, event.Item) + wx.CallAfter(self.spawnMenu, event.GetItem()) def spawnMenu(self, item): - self.skillTreeListCtrl.SelectItem(item) - if self.skillTreeListCtrl.GetChildrenCount(item) > 0: + self.skillTreeListCtrl.Select(item) + thing = self.skillTreeListCtrl.GetFirstChild(item).IsOk() + if thing: return char = self.charEditor.entityEditor.getActiveEntity() sMkt = Market.getInstance() - id = self.skillTreeListCtrl.GetPyData(item)[1] + id = self.skillTreeListCtrl.GetItemData(item)[1] if char.name not in ("All 0", "All 5"): self.levelChangeMenu.selection = sMkt.getItem(id) self.PopupMenu(self.levelChangeMenu) @@ -591,7 +602,7 @@ class SkillTreeView(wx.Panel): sChar = Character.getInstance() char = self.charEditor.entityEditor.getActiveEntity() selection = self.skillTreeListCtrl.GetSelection() - dataType, skillID = self.skillTreeListCtrl.GetPyData(selection) + dataType, skillID = self.skillTreeListCtrl.GetItemData(selection) if level is not None: sChar.changeLevel(char.ID, skillID, level, persist=True) @@ -604,41 +615,43 @@ class SkillTreeView(wx.Panel): # level setting. We don't want to refresh tree, as that will lose all expanded categories and users location # within the tree. Thus, we loop through the tree and refresh the info. # @todo: when collapsing branch, remove the data. This will make this loop more performant - child, cookie = self.skillTreeListCtrl.GetFirstChild(self.root) + + child = self.skillTreeListCtrl.GetFirstChild(self.root) def _setTreeSkillLevel(treeItem, skillID): lvl, dirty = sChar.getSkillLevel(char.ID, skillID) self.skillTreeListCtrl.SetItemText(treeItem, - "Level {}".format(int(lvl)) if not isinstance(lvl, basestring) else lvl, - 1) + 1, + "Level {}".format(int(lvl)) if not isinstance(lvl, str) else lvl) + if not dirty: - self.skillTreeListCtrl.SetItemTextColour(treeItem, None) + self.skillTreeListCtrl.SetItemImage(treeItem, self.skillBookImageId) while child.IsOk(): # child = Skill category - dataType, id = self.skillTreeListCtrl.GetPyData(child) + dataType, id = self.skillTreeListCtrl.GetItemData(child) + if dataType == 'skill': _setTreeSkillLevel(child, id) else: - grand, cookie2 = self.skillTreeListCtrl.GetFirstChild(child) - + grand = self.skillTreeListCtrl.GetFirstChild(child) while grand.IsOk(): if self.skillTreeListCtrl.GetItemText(grand) != "dummy": - _, skillID = self.skillTreeListCtrl.GetPyData(grand) + _, skillID = self.skillTreeListCtrl.GetItemData(grand) _setTreeSkillLevel(grand, skillID) - grand, cookie2 = self.skillTreeListCtrl.GetNextChild(child, cookie2) + grand = self.skillTreeListCtrl.GetNextSibling(grand) - child, cookie = self.skillTreeListCtrl.GetNextChild(self.root, cookie) + child = self.skillTreeListCtrl.GetNextSibling(child) dirtySkills = sChar.getDirtySkills(char.ID) dirtyGroups = set([skill.item.group.ID for skill in dirtySkills]) parentID = self.skillTreeListCtrl.GetItemParent(selection) - parent = self.skillTreeListCtrl.GetPyData(parentID) + parent = self.skillTreeListCtrl.GetItemData(parentID) if parent: if parent[1] in dirtyGroups: - self.skillTreeListCtrl.SetItemTextColour(parentID, None) + self.skillTreeListCtrl.SetItemImage(parentID, self.skillBookImageId) event.Skip() @@ -709,20 +722,24 @@ class APIView(wx.Panel): wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(500, 300), style=wx.TAB_TRAVERSAL) self.charEditor = self.Parent.Parent # first parent is Notebook, second is Character Editor - self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - - self.apiUrlCreatePredefined = u"https://community.eveonline.com/support/api-key/CreatePredefined?accessMask=8" - self.apiUrlKeyList = u"https://community.eveonline.com/support/api-key/" + self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) pmainSizer = wx.BoxSizer(wx.VERTICAL) hintSizer = wx.BoxSizer(wx.HORIZONTAL) hintSizer.AddStretchSpacer() self.stDisabledTip = wx.StaticText(self, wx.ID_ANY, - u"You cannot add API Details for All 0 and All 5 characters.\n" - u"Please select another character or make a new one.", style=wx.ALIGN_CENTER) + "You cannot link All 0 or All 5 characters to an EVE character.\n" + "Please select another character or make a new one.", style=wx.ALIGN_CENTER) self.stDisabledTip.Wrap(-1) hintSizer.Add(self.stDisabledTip, 0, wx.TOP | wx.BOTTOM, 10) + + self.noCharactersTip = wx.StaticText(self, wx.ID_ANY, + "You haven't logging into EVE SSO with any characters yet. Please use the " + "button below to log into EVE.", style=wx.ALIGN_CENTER) + self.noCharactersTip.Wrap(-1) + hintSizer.Add(self.noCharactersTip, 0, wx.TOP | wx.BOTTOM, 10) + self.stDisabledTip.Hide() hintSizer.AddStretchSpacer() pmainSizer.Add(hintSizer, 0, wx.EXPAND, 5) @@ -732,99 +749,95 @@ class APIView(wx.Panel): fgSizerInput.SetFlexibleDirection(wx.BOTH) fgSizerInput.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED) - self.m_staticIDText = wx.StaticText(self, wx.ID_ANY, u"keyID:", wx.DefaultPosition, wx.DefaultSize, 0) - self.m_staticIDText.Wrap(-1) - fgSizerInput.Add(self.m_staticIDText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5) - - self.inputID = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) - fgSizerInput.Add(self.inputID, 1, wx.ALL | wx.EXPAND, 5) - - self.m_staticKeyText = wx.StaticText(self, wx.ID_ANY, u"vCode:", wx.DefaultPosition, wx.DefaultSize, 0) - self.m_staticKeyText.Wrap(-1) - fgSizerInput.Add(self.m_staticKeyText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5) - - self.inputKey = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0) - fgSizerInput.Add(self.inputKey, 0, wx.ALL | wx.EXPAND, 5) - - self.m_staticCharText = wx.StaticText(self, wx.ID_ANY, u"Character:", wx.DefaultPosition, wx.DefaultSize, 0) + self.m_staticCharText = wx.StaticText(self, wx.ID_ANY, "Character:", wx.DefaultPosition, wx.DefaultSize, 0) self.m_staticCharText.Wrap(-1) - fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 5) + fgSizerInput.Add(self.m_staticCharText, 0, wx.ALL | wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL, 10) self.charChoice = wx.Choice(self, wx.ID_ANY, style=0) - self.charChoice.Append("No Selection", 0) - fgSizerInput.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 5) - - self.charChoice.Enable(False) + fgSizerInput.Add(self.charChoice, 1, wx.ALL | wx.EXPAND, 10) pmainSizer.Add(fgSizerInput, 0, wx.EXPAND, 5) - btnSizer = wx.BoxSizer(wx.HORIZONTAL) - btnSizer.AddStretchSpacer() - - self.btnFetchCharList = wx.Button(self, wx.ID_ANY, u"Get Characters") - btnSizer.Add(self.btnFetchCharList, 0, wx.ALL, 2) - self.btnFetchCharList.Bind(wx.EVT_BUTTON, self.fetchCharList) - - self.btnFetchSkills = wx.Button(self, wx.ID_ANY, u"Fetch Skills") - btnSizer.Add(self.btnFetchSkills, 0, wx.ALL, 2) - self.btnFetchSkills.Bind(wx.EVT_BUTTON, self.fetchSkills) - self.btnFetchSkills.Enable(False) - - btnSizer.AddStretchSpacer() - pmainSizer.Add(btnSizer, 0, wx.EXPAND, 5) - + self.addButton = wx.Button(self, wx.ID_ANY, "Log In with EVE SSO", wx.DefaultPosition, wx.DefaultSize, 0) + self.addButton.Bind(wx.EVT_BUTTON, self.addCharacter) + pmainSizer.Add(self.addButton, 0, wx.ALL | wx.ALIGN_CENTER, 5) self.stStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString) pmainSizer.Add(self.stStatus, 0, wx.ALL, 5) - - pmainSizer.AddStretchSpacer() - self.stAPITip = wx.StaticText(self, wx.ID_ANY, - u"You can create a pre-defined key here (only CharacterSheet is required):", - wx.DefaultPosition, wx.DefaultSize, 0) - self.stAPITip.Wrap(-1) - - pmainSizer.Add(self.stAPITip, 0, wx.ALL, 2) - - self.hlEveAPI = wx.HyperlinkCtrl(self, wx.ID_ANY, self.apiUrlCreatePredefined, self.apiUrlCreatePredefined, - wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE) - pmainSizer.Add(self.hlEveAPI, 0, wx.ALL, 2) - - self.stAPITip2 = wx.StaticText(self, wx.ID_ANY, u"Or, you can choose an existing key from:", wx.DefaultPosition, - wx.DefaultSize, 0) - self.stAPITip2.Wrap(-1) - pmainSizer.Add(self.stAPITip2, 0, wx.ALL, 2) - - self.hlEveAPI2 = wx.HyperlinkCtrl(self, wx.ID_ANY, self.apiUrlKeyList, self.apiUrlKeyList, wx.DefaultPosition, - wx.DefaultSize, wx.HL_DEFAULT_STYLE) - pmainSizer.Add(self.hlEveAPI2, 0, wx.ALL, 2) - + self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGOUT, self.ssoListChanged) + self.charEditor.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoListChanged) self.charEditor.entityEditor.Bind(wx.EVT_CHOICE, self.charChanged) + self.charChoice.Bind(wx.EVT_CHOICE, self.ssoCharChanged) + self.SetSizer(pmainSizer) self.Layout() - self.charChanged(None) + self.ssoListChanged(None) + + def ssoCharChanged(self, event): + sChar = Character.getInstance() + activeChar = self.charEditor.entityEditor.getActiveEntity() + sChar.setSsoCharacter(activeChar.ID, self.getActiveCharacter()) + event.Skip() + + def addCharacter(self, event): + sEsi = Esi.getInstance() + sEsi.login() + + def getActiveCharacter(self): + selection = self.charChoice.GetCurrentSelection() + return self.charChoice.GetClientData(selection) if selection is not -1 else None + + def ssoListChanged(self, event): + sEsi = Esi.getInstance() + ssoChars = sEsi.getSsoCharacters() + + if len(ssoChars) == 0: + self.charChoice.Hide() + self.m_staticCharText.Hide() + self.noCharactersTip.Show() + else: + self.noCharactersTip.Hide() + self.m_staticCharText.Show() + self.charChoice.Show() + + self.charChanged(event) def charChanged(self, event): sChar = Character.getInstance() + sEsi = Esi.getInstance() + activeChar = self.charEditor.entityEditor.getActiveEntity() - ID, key, char, chars = sChar.getApiDetails(activeChar.ID) - self.inputID.SetValue(str(ID)) - self.inputKey.SetValue(key) + if event and event.EventType == GE.EVT_SSO_LOGIN.typeId and hasattr(event, 'character'): + # Automatically assign the character that was just logged into + sChar.setSsoCharacter(activeChar.ID, event.character.ID) + + sso = sChar.getSsoCharacter(activeChar.ID) + + ssoChars = sEsi.getSsoCharacters() self.charChoice.Clear() - if chars: - for charName in chars: - self.charChoice.Append(charName) - self.charChoice.SetStringSelection(char) - self.charChoice.Enable(True) - self.btnFetchSkills.Enable(True) - else: - self.charChoice.Append("No characters...", 0) - self.charChoice.SetSelection(0) - self.charChoice.Enable(False) - self.btnFetchSkills.Enable(False) + noneID = self.charChoice.Append("None", None) + for char in ssoChars: + currId = self.charChoice.Append(char.characterName, char.ID) + + if sso is not None and char.ID == sso.ID: + self.charChoice.SetSelection(currId) + if sso is None: + self.charChoice.SetSelection(noneID) + + + # + # if chars: + # for charName in chars: + # self.charChoice.Append(charName) + # self.charChoice.SetStringSelection(char) + # else: + # self.charChoice.Append("No characters...", 0) + # self.charChoice.SetSelection(0) + # if activeChar.name in ("All 0", "All 5"): self.Enable(False) self.stDisabledTip.Show() @@ -837,47 +850,6 @@ class APIView(wx.Panel): if event is not None: event.Skip() - def fetchCharList(self, event): - self.stStatus.SetLabel("") - if self.inputID.GetLineText(0) == "" or self.inputKey.GetLineText(0) == "": - self.stStatus.SetLabel("Invalid keyID or vCode!") - return - - sChar = Character.getInstance() - try: - activeChar = self.charEditor.entityEditor.getActiveEntity() - list = sChar.apiCharList(activeChar.ID, self.inputID.GetLineText(0), self.inputKey.GetLineText(0)) - except AuthenticationError, e: - msg = "Authentication failure. Please check keyID and vCode combination." - pyfalog.info(msg) - self.stStatus.SetLabel(msg) - except TimeoutError, e: - msg = "Request timed out. Please check network connectivity and/or proxy settings." - pyfalog.info(msg) - self.stStatus.SetLabel(msg) - except Exception, e: - pyfalog.error(e) - self.stStatus.SetLabel("Error:\n%s" % e.message) - else: - self.charChoice.Clear() - for charName in list: - self.charChoice.Append(charName) - - self.btnFetchSkills.Enable(True) - self.charChoice.Enable(True) - - self.Layout() - - self.charChoice.SetSelection(0) - - def fetchSkills(self, event): - charName = self.charChoice.GetString(self.charChoice.GetSelection()) - if charName: - sChar = Character.getInstance() - activeChar = self.charEditor.entityEditor.getActiveEntity() - sChar.apiFetch(activeChar.ID, charName, self.__fetchCallback) - self.stStatus.SetLabel("Getting skills for {}".format(charName)) - def __fetchCallback(self, e=None): charName = self.charChoice.GetString(self.charChoice.GetSelection()) if e is None: @@ -893,12 +865,12 @@ class SecStatusDialog(wx.Dialog): def __init__(self, parent, sec): wx.Dialog.__init__(self, parent, title="Set Security Status", size=(275, 175)) - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) bSizer1 = wx.BoxSizer(wx.VERTICAL) self.m_staticText1 = wx.StaticText(self, wx.ID_ANY, - u"Security Status is used in some CONCORD hull calculations; you can set the characters security status here", + "Security Status is used in some CONCORD hull calculations; you can set the characters security status here", wx.DefaultPosition, wx.DefaultSize, 0) self.m_staticText1.Wrap(-1) bSizer1.Add(self.m_staticText1, 1, wx.ALL | wx.EXPAND, 5) diff --git a/gui/characterSelection.py b/gui/characterSelection.py index 6fc901f1c..cc63a1f82 100644 --- a/gui/characterSelection.py +++ b/gui/characterSelection.py @@ -19,15 +19,15 @@ # noinspection PyPackageRequirements import wx +from gui.bitmap_loader import BitmapLoader from logbook import Logger import gui.globalEvents as GE import gui.mainFrame -from gui.bitmapLoader import BitmapLoader -from gui.utils.clipboard import toClipboard from service.character import Character from service.fit import Fit +from gui.utils.clipboard import toClipboard pyfalog = Logger(__name__) @@ -61,7 +61,7 @@ class CharacterSelection(wx.Panel): self.btnRefresh.SetMinSize(size) self.btnRefresh.SetMaxSize(size) - self.btnRefresh.SetToolTipString("Refresh API") + self.btnRefresh.SetToolTip("Refresh API") self.btnRefresh.Bind(wx.EVT_BUTTON, self.refreshApi) self.btnRefresh.Enable(False) @@ -79,6 +79,7 @@ class CharacterSelection(wx.Panel): self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) self.SetMinSize(wx.Size(25, -1)) + self.toggleRefreshButton() self.charChoice.Enable(False) @@ -142,7 +143,7 @@ class CharacterSelection(wx.Panel): sFit = Fit.getInstance() sFit.changeChar(fitID, charID) - choice.Append(u"\u2015 Open Character Editor \u2015", -1) + choice.Append("\u2015 Open Character Editor \u2015", -1) self.charCache = self.charChoice.GetCurrentSelection() if event is not None: @@ -151,9 +152,7 @@ class CharacterSelection(wx.Panel): def refreshApi(self, event): self.btnRefresh.Enable(False) sChar = Character.getInstance() - ID, key, charName, chars = sChar.getApiDetails(self.getActiveCharacter()) - if charName: - sChar.apiFetch(self.getActiveCharacter(), charName, self.refreshAPICallback) + sChar.apiFetch(self.getActiveCharacter(), self.refreshAPICallback) def refreshAPICallback(self, e=None): self.btnRefresh.Enable(True) @@ -161,11 +160,11 @@ class CharacterSelection(wx.Panel): self.refreshCharacterList() else: exc_type, exc_obj, exc_trace = e - pyfalog.warn("Error fetching API information for character") + pyfalog.warn("Error fetching skill information for character") pyfalog.warn(exc_obj) wx.MessageBox( - "Error fetching API information, please check your API details in the character editor and try again later", + "Error fetching skill information", "Error", wx.ICON_ERROR | wx.STAY_ON_TOP) def charChanged(self, event): @@ -178,16 +177,23 @@ class CharacterSelection(wx.Panel): self.charChoice.SetSelection(self.charCache) self.mainFrame.showCharacterEditor(event) return - if sChar.getCharName(charID) not in ("All 0", "All 5") and sChar.apiEnabled(charID): - self.btnRefresh.Enable(True) - else: - self.btnRefresh.Enable(False) + + self.toggleRefreshButton() sFit = Fit.getInstance() sFit.changeChar(fitID, charID) self.charCache = self.charChoice.GetCurrentSelection() wx.PostEvent(self.mainFrame, GE.FitChanged(fitID=fitID)) + def toggleRefreshButton(self): + charID = self.getActiveCharacter() + sChar = Character.getInstance() + char = sChar.getCharacter(charID) + if sChar.getCharName(charID) not in ("All 0", "All 5") and sChar.getSsoCharacter(char.ID) is not None: + self.btnRefresh.Enable(True) + else: + self.btnRefresh.Enable(False) + def selectChar(self, charID): choice = self.charChoice numItems = len(choice.GetItems()) @@ -212,7 +218,7 @@ class CharacterSelection(wx.Panel): if event.fitID is None: self.skillReqsStaticBitmap.SetBitmap(self.cleanSkills) - self.skillReqsStaticBitmap.SetToolTipString("No active fit") + self.skillReqsStaticBitmap.SetToolTip("No active fit") else: sCharacter = Character.getInstance() self.reqs = sCharacter.checkRequirements(fit) @@ -233,7 +239,7 @@ class CharacterSelection(wx.Panel): else: tip += self._buildSkillsTooltip(self.reqs) self.skillReqsStaticBitmap.SetBitmap(self.redSkills) - self.skillReqsStaticBitmap.SetToolTipString(tip.strip()) + self.skillReqsStaticBitmap.SetToolTip(tip.strip()) if newCharID is None: sChar = Character.getInstance() @@ -244,6 +250,8 @@ class CharacterSelection(wx.Panel): if not fit.calculated: self.charChanged(None) + self.toggleRefreshButton() + event.Skip() def exportSkills(self, evt): @@ -260,11 +268,11 @@ class CharacterSelection(wx.Panel): sCharacter = Character.getInstance() if tabulationLevel == 0: - for item, subReqs in reqs.iteritems(): + for item, subReqs in reqs.items(): tip += "%s:\n" % item.name tip += self._buildSkillsTooltip(subReqs, item.name, 1) else: - for name, info in reqs.iteritems(): + for name, info in reqs.items(): level, ID, more = info sCharacter.skillReqsDict['skills'].append({ 'item': currItem, @@ -286,11 +294,11 @@ class CharacterSelection(wx.Panel): sCharacter = Character.getInstance() if tabulationLevel == 0: - for item, subReqs in reqs.iteritems(): + for item, subReqs in reqs.items(): skillsMap = self._buildSkillsTooltipCondensed(subReqs, item.name, 1, skillsMap) sorted(skillsMap, key=skillsMap.get) else: - for name, info in reqs.iteritems(): + for name, info in reqs.items(): level, ID, more = info sCharacter.skillReqsDict['skills'].append({ 'item': currItem, diff --git a/gui/chromeTabs.py b/gui/chromeTabs.py deleted file mode 100644 index f978e9dd3..000000000 --- a/gui/chromeTabs.py +++ /dev/null @@ -1,1439 +0,0 @@ -# ============================================================================= -# Copyright (C) 2010 Darriele -# -# This file is part of pyfa. -# -# pyfa is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# pyfa is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyfa. If not, see . -# ============================================================================= - -# noinspection PyPackageRequirements -import wx -# noinspection PyPackageRequirements -import wx.lib.newevent -import gui.utils.colorUtils as colorUtils -import gui.utils.drawUtils as drawUtils -import gui.utils.fonts as fonts -from gui.bitmapLoader import BitmapLoader -from logbook import Logger -from service.fit import Fit - -pyfalog = Logger(__name__) - -_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() -_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() -_PageAdding, EVT_NOTEBOOK_PAGE_ADDING = wx.lib.newevent.NewEvent() -_PageClosing, EVT_NOTEBOOK_PAGE_CLOSING = wx.lib.newevent.NewEvent() -PageAdded, EVT_NOTEBOOK_PAGE_ADDED = wx.lib.newevent.NewEvent() -PageClosed, EVT_NOTEBOOK_PAGE_CLOSED = wx.lib.newevent.NewEvent() - - -class VetoAble(object): - def __init__(self): - self.__vetoed = False - - def Veto(self): - self.__vetoed = True - - def isVetoed(self): - return self.__vetoed - - -class NotebookTabChangeEvent(object): - def __init__(self, old, new): - self.__old = old - self.__new = new - - def GetOldSelection(self): - return self.__old - - def GetSelection(self): - return self.__new - - OldSelection = property(GetOldSelection) - Selection = property(GetSelection) - - -class PageChanging(_PageChanging, NotebookTabChangeEvent, VetoAble): - def __init__(self, old, new): - NotebookTabChangeEvent.__init__(self, old, new) - _PageChanging.__init__(self) - VetoAble.__init__(self) - - -class PageChanged(_PageChanged, NotebookTabChangeEvent): - def __init__(self, old, new): - NotebookTabChangeEvent.__init__(self, old, new) - _PageChanged.__init__(self) - - -class PageClosing(_PageClosing, VetoAble): - def __init__(self, i): - self.__index = i - _PageClosing.__init__(self) - VetoAble.__init__(self) - self.Selection = property(self.GetSelection) - - def GetSelection(self): - return self.__index - - -class PageAdding(_PageAdding, VetoAble): - def __init__(self): - _PageAdding.__init__(self) - VetoAble.__init__(self) - - -class PFNotebook(wx.Panel): - def __init__(self, parent, canAdd=True): - """ - Instance of Pyfa Notebook. Initializes general layout, includes methods - for setting current page, replacing pages, etc - - parent - wx parent element - canAdd - True if tabs be deleted and added, passed directly to - PFTabsContainer - """ - - wx.Panel.__init__(self, parent, wx.ID_ANY, size=(-1, -1)) - - self.pages = [] - self.activePage = None - - mainSizer = wx.BoxSizer(wx.VERTICAL) - - tabsSizer = wx.BoxSizer(wx.VERTICAL) - self.tabsContainer = PFTabsContainer(self, canAdd=canAdd) - tabsSizer.Add(self.tabsContainer, 0, wx.EXPAND) - - style = wx.DOUBLE_BORDER if 'wxMSW' in wx.PlatformInfo else wx.SIMPLE_BORDER - - contentSizer = wx.BoxSizer(wx.VERTICAL) - self.pageContainer = wx.Panel(self, style=style) - self.pageContainer.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - contentSizer.Add(self.pageContainer, 1, wx.EXPAND, 5) - - mainSizer.Add(tabsSizer, 0, wx.EXPAND, 5) - mainSizer.Add(contentSizer, 1, wx.EXPAND | wx.BOTTOM, 2) - - self.SetSizer(mainSizer) - self.Bind(wx.EVT_SIZE, self.OnSize) - self.Layout() - - def GetPage(self, i): - return self.pages[i] - - def SetPage(self, i, page): - if i >= len(self.pages) or i is None or page is None: - return - - oldPage = self.pages[i] - self.pages[i] = page - if oldPage == self.activePage: - oldPage.Destroy() - self.activePage = page - else: - oldPage.Destroy() - - page.Reparent(self.pageContainer) - - if self.activePage == page: - self.ShowActive() - - @staticmethod - def GetBorders(): - """Gets border widths to better determine page size in ShowActive()""" - - bx = wx.SystemSettings_GetMetric(wx.SYS_BORDER_X) - by = wx.SystemSettings_GetMetric(wx.SYS_BORDER_Y) - - if bx < 0: - bx = 1 - if by < 0: - by = 1 - - return bx, by - - def ReplaceActivePage(self, page): - self.SetPage(self.GetSelection(), page) - - def GetSelectedPage(self): - return self.activePage - - def GetPageIndex(self, page): - return self.pages.index(page) if page in self.pages else None - - def GetSelection(self): - return self.GetPageIndex(self.activePage) - - def GetCurrentPage(self): - return self.activePage - - def GetPageCount(self): - return len(self.pages) - - def NextPage(self): - """Used with keyboard shortcut for next page navigation""" - cpage = self.GetSelection() - - if cpage is None: - return - - if cpage < self.GetPageCount() - 1: - self.SetSelection(cpage + 1) - npage = cpage + 1 - else: - self.SetSelection(0) - npage = 0 - - wx.PostEvent(self, PageChanged(cpage, npage)) - - def PrevPage(self): - """Used with keyboard shortcut for previous page navigation""" - cpage = self.GetSelection() - - if cpage is None: - return - - if cpage > 0: - self.SetSelection(cpage - 1) - npage = cpage - 1 - else: - self.SetSelection(self.GetPageCount() - 1) - npage = self.GetPageCount() - 1 - - wx.PostEvent(self, PageChanged(cpage, npage)) - - def AddPage(self, tabWnd=None, tabTitle="Empty Tab", tabImage=None, showClose=True): - if self.activePage: - self.activePage.Hide() - - if not tabWnd: - tabWnd = wx.Panel(self) - - tabWnd.Reparent(self.pageContainer) - - self.pageContainer.Layout() - - self.pages.append(tabWnd) - self.tabsContainer.AddTab(tabTitle, tabImage, showClose) - - self.activePage = tabWnd - self.ShowActive(True) - - def DisablePage(self, page, toggle): - idx = self.GetPageIndex(page) - - if toggle and page == self.activePage: - try: - # Set page to the first non-disabled page - self.SetSelection(next(i for i, _ in enumerate(self.pages) if not self.tabsContainer.tabs[i].disabled)) - except StopIteration: - self.SetSelection(0) - - self.tabsContainer.DisableTab(idx, toggle) - - def SetSelection(self, page): - oldsel = self.GetSelection() - if oldsel != page: - self.activePage.Hide() - self.activePage = self.pages[page] - self.tabsContainer.SetSelected(page) - self.ShowActive() - - def DeletePage(self, n, internal=False): - """ - Deletes page. - - n -- index of page to be deleted - internal -- True if we're deleting the page from the PFTabsContainer - """ - page = self.pages[n] - self.pages.remove(page) - page.Destroy() - - if not internal: - # If we're not deleting from the tab, delete the tab - # (deleting from the tab automatically deletes itself) - self.tabsContainer.DeleteTab(n, True) - - sel = self.tabsContainer.GetSelected() - if sel is not None: - self.activePage = self.pages[sel] - self.ShowActive() - wx.PostEvent(self, PageChanged(-1, sel)) - else: - self.activePage = None - - def SwitchPages(self, src, dest): - self.pages[src], self.pages[dest] = self.pages[dest], self.pages[src] - - def ShowActive(self, resizeOnly=False): - """ - Sets the size of the page and shows. The sizing logic adjusts for some - minor sizing errors (scrollbars going beyond bounds) - - resizeOnly -- if we are not interested in showing the page, only setting - the size - - @todo: is resizeOnly still needed? Was introduced with 8b8b97 in mid 2011 - to fix a resizing bug with blank pages, cannot reproduce 13Sept2014 - """ - - ww, wh = self.pageContainer.GetSize() - bx, by = self.GetBorders() - ww -= bx * 4 - wh -= by * 4 - self.activePage.SetSize((max(ww, -1), max(wh, -1))) - self.activePage.SetPosition((0, 0)) - - if not resizeOnly: - self.activePage.Show() - - self.Layout() - - def IsActive(self, page): - return self.activePage == page - - def SetPageTitle(self, i, text, refresh=True): - tab = self.tabsContainer.tabs[i] - tab.text = text - if refresh: - self.tabsContainer.AdjustTabsSize() - self.Refresh() - - def SetPageIcon(self, i, icon, refresh=True): - tab = self.tabsContainer.tabs[i] - tab.tabImg = icon - if refresh: - self.tabsContainer.AdjustTabsSize() - self.Refresh() - - def SetPageTextIcon(self, i, text=wx.EmptyString, icon=None): - self.SetPageTitle(i, text, False) - self.SetPageIcon(i, icon, False) - self.tabsContainer.AdjustTabsSize() - self.Refresh() - - def Refresh(self): - self.tabsContainer.Refresh() - - def OnSize(self, event): - w, h = self.GetSize() - self.tabsContainer.SetSize((w, -1)) - self.tabsContainer.UpdateSize() - self.tabsContainer.Refresh() - self.Layout() - - if self.activePage: - self.ShowActive() - event.Skip() - - -class PFTabRenderer(object): - def __init__(self, size=(36, 24), text=wx.EmptyString, img=None, inclination=6, closeButton=True): - """ - Renders a new tab - - text -- tab label - img -- wxImage of tab icon - inclination -- does not seem to affect class, maybe used to be a variable - for custom drawn tab inclinations before there were bitmaps? - closeButton -- True if tab can be closed - """ - # tab left/right zones inclination - self.ctabLeft = BitmapLoader.getImage("ctableft", "gui") - self.ctabMiddle = BitmapLoader.getImage("ctabmiddle", "gui") - self.ctabRight = BitmapLoader.getImage("ctabright", "gui") - self.ctabClose = BitmapLoader.getImage("ctabclose", "gui") - - self.leftWidth = self.ctabLeft.GetWidth() - self.rightWidth = self.ctabRight.GetWidth() - self.middleWidth = self.ctabMiddle.GetWidth() - self.closeBtnWidth = self.ctabClose.GetWidth() - - width, height = size - if width < self.leftWidth + self.rightWidth + self.middleWidth: - width = self.leftWidth + self.rightWidth + self.middleWidth - if height < self.ctabMiddle.GetHeight(): - height = self.ctabMiddle.GetHeight() - - self.inclination = inclination - self.text = text - self.disabled = False - self.tabSize = (width, height) - self.closeButton = closeButton - self.selected = False - self.closeBtnHovering = False - self.tabBitmap = None - self.tabBackBitmap = None - self.cbSize = 5 - self.padding = 4 - self.font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) - - self.tabImg = img - self.position = (0, 0) # Not used internally for rendering - helper for tab container - self.InitTab() - - def SetPosition(self, position): - self.position = position - - def GetPosition(self): - return self.position - - def GetSize(self): - return self.tabSize - - def SetSize(self, size): - w, h = size - if w < self.leftWidth + self.rightWidth + self.middleWidth: - w = self.leftWidth + self.rightWidth + self.middleWidth - if h < self.ctabMiddle.GetHeight(): - h = self.ctabMiddle.GetHeight() - - self.tabSize = (w, h) - self.InitTab() - - def SetSelected(self, sel=True): - self.selected = sel - self.InitTab() - - def GetSelected(self): - return self.selected - - def IsSelected(self): - return self.selected - - def ShowCloseButtonHovering(self, hover=True): - if self.closeBtnHovering != hover: - self.closeBtnHovering = hover - self._Render() - - def GetCloseButtonHoverStatus(self): - return self.closeBtnHovering - - def GetTabRegion(self): - nregion = self.CopyRegion(self.tabRegion) - nregion.SubtractRegion(self.closeBtnRegion) if self.closeButton else self.tabRegion - return nregion - - def GetCloseButtonRegion(self): - return self.CopyRegion(self.closeBtnRegion) - - def GetMinSize(self): - ebmp = wx.EmptyBitmap(1, 1) - mdc = wx.MemoryDC() - mdc.SelectObject(ebmp) - mdc.SetFont(self.font) - textSizeX, textSizeY = mdc.GetTextExtent(self.text) - totalSize = self.leftWidth + self.rightWidth + textSizeX + self.closeBtnWidth / 2 + 16 + self.padding * 2 - mdc.SelectObject(wx.NullBitmap) - return totalSize, self.tabHeight - - def SetTabImage(self, img): - self.tabImg = img - - @staticmethod - def CopyRegion(region): - rect = region.GetBox() - - newRegion = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) - newRegion.IntersectRegion(region) - - return newRegion - - def InitTab(self): - self.tabWidth, self.tabHeight = self.tabSize - - self.contentWidth = self.tabWidth - self.leftWidth - self.rightWidth - self.tabRegion = None - self.closeBtnRegion = None - - self.InitColors() - self.InitBitmaps() - - self.ComposeTabBack() - self.InitTabRegions() - self._Render() - - def InitBitmaps(self): - """ - Creates bitmap for tab - - Takes the bitmaps already set and replaces a known color (black) with - the needed color, while also considering selected state. Color dependant - on platform -- see InitColors(). - """ - if self.selected: - tr, tg, tb = self.selectedColor - else: - tr, tg, tb = self.inactiveColor - - ctabLeft = self.ctabLeft.Copy() - ctabRight = self.ctabRight.Copy() - ctabMiddle = self.ctabMiddle.Copy() - - ctabLeft.Replace(0, 0, 0, tr, tg, tb) - ctabRight.Replace(0, 0, 0, tr, tg, tb) - ctabMiddle.Replace(0, 0, 0, tr, tg, tb) - - self.ctabLeftBmp = wx.BitmapFromImage(ctabLeft) - self.ctabRightBmp = wx.BitmapFromImage(ctabRight) - self.ctabMiddleBmp = wx.BitmapFromImage(ctabMiddle) - self.ctabCloseBmp = wx.BitmapFromImage(self.ctabClose) - - def ComposeTabBack(self): - """ - Creates the tab background bitmap based upon calculated dimension values - and modified bitmaps via InitBitmaps() - """ - bkbmp = wx.EmptyBitmap(self.tabWidth, self.tabHeight) - - mdc = wx.MemoryDC() - mdc.SelectObject(bkbmp) - - # mdc.SetBackground(wx.Brush((0x12, 0x23, 0x32))) - mdc.Clear() - - mdc.DrawBitmap(self.ctabLeftBmp, 0, 0) # set the left bitmap - - # convert middle bitmap and scale to tab width - cm = self.ctabMiddleBmp.ConvertToImage() - mimg = cm.Scale(self.contentWidth, self.ctabMiddle.GetHeight(), wx.IMAGE_QUALITY_NORMAL) - mbmp = wx.BitmapFromImage(mimg) - mdc.DrawBitmap(mbmp, self.leftWidth, 0) # set middle bitmap, offset by left - - # set right bitmap offset by left + middle - mdc.DrawBitmap(self.ctabRightBmp, self.contentWidth + self.leftWidth, 0) - - mdc.SelectObject(wx.NullBitmap) - - # bkbmp.SetMaskColour((0x12, 0x23, 0x32)) - - if self.tabBackBitmap: - del self.tabBackBitmap - - self.tabBackBitmap = bkbmp - - def InitTabRegions(self): - """ - Initializes regions for tab, which makes it easier to determine if - given coordinates are incluced in a region - """ - self.tabRegion = wx.RegionFromBitmap(self.tabBackBitmap) - self.closeBtnRegion = wx.RegionFromBitmap(self.ctabCloseBmp) - self.closeBtnRegion.Offset( - self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth() / 2, - (self.tabHeight - self.ctabCloseBmp.GetHeight()) / 2 - ) - - def InitColors(self): - """Determines colors used for tab, based on system settings""" - self.tabColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) - self.inactiveColor = colorUtils.GetSuitableColor(self.tabColor, 0.25) - self.selectedColor = colorUtils.GetSuitableColor(self.tabColor, 0.10) - - def Render(self): - return self.tabBitmap - - def _Render(self): - """Renders the tab, complete with the icon, text, and close button""" - if self.tabBitmap: - del self.tabBitmap - - height = self.tabHeight - - # rect = wx.Rect(0, 0, self.tabWidth, self.tabHeight) - - canvas = wx.EmptyBitmap(self.tabWidth, self.tabHeight, 24) - - mdc = wx.MemoryDC() - - mdc.SelectObject(canvas) - # mdc.SetBackground(wx.Brush ((0x12,0x23,0x32))) - mdc.Clear() - - # r = copy.copy(rect) - # r.top = r.left = 0 - # r.height = height - mdc.DrawBitmap(self.tabBackBitmap, 0, 0, True) - - if self.tabImg: - bmp = wx.BitmapFromImage(self.tabImg.ConvertToGreyscale() if self.disabled else self.tabImg) - if self.contentWidth > 16: # @todo: is this conditional relevant anymore? - # Draw tab icon - mdc.DrawBitmap(bmp, self.leftWidth + self.padding - bmp.GetWidth() / 2, (height - bmp.GetHeight()) / 2) - textStart = self.leftWidth + self.padding + bmp.GetWidth() / 2 - else: - textStart = self.leftWidth - - mdc.SetFont(self.font) - - maxsize = self.tabWidth - textStart - self.rightWidth - self.padding * 4 - color = self.selectedColor if self.selected else self.inactiveColor - - mdc.SetTextForeground(colorUtils.GetSuitableColor(color, 1)) - - text = drawUtils.GetPartialText(mdc, self.text, maxsize, "") - tx, ty = mdc.GetTextExtent(text) - mdc.DrawText(text, textStart + self.padding, height / 2 - ty / 2) - - if self.closeButton: - if self.closeBtnHovering: - cbmp = self.ctabCloseBmp - else: - cimg = self.ctabCloseBmp.ConvertToImage() - cimg = cimg.AdjustChannels(0.7, 0.7, 0.7, 0.3) - cbmp = wx.BitmapFromImage(cimg) - - mdc.DrawBitmap( - cbmp, - self.contentWidth + self.leftWidth - self.ctabCloseBmp.GetWidth() / 2, - (height - self.ctabCloseBmp.GetHeight()) / 2, - ) - - mdc.SelectObject(wx.NullBitmap) - - canvas.SetMaskColour((0x12, 0x23, 0x32)) - img = canvas.ConvertToImage() - - if not img.HasAlpha(): - img.InitAlpha() - - bmp = wx.BitmapFromImage(img) - self.tabBitmap = bmp - - def __repr__(self): - return "PFTabRenderer(text={}, disabled={}) at {}".format( - self.text, self.disabled, hex(id(self)) - ) - - -class PFAddRenderer(object): - def __init__(self): - """Renders the add tab button""" - self.addImg = BitmapLoader.getImage("ctabadd", "gui") - self.width = self.addImg.GetWidth() - self.height = self.addImg.GetHeight() - - self.region = None - self.tbmp = wx.BitmapFromImage(self.addImg) - self.addBitmap = None - - self.position = (0, 0) - self.highlighted = False - - self.InitRenderer() - - def GetPosition(self): - return self.position - - def SetPosition(self, pos): - self.position = pos - - def GetSize(self): - return self.width, self.height - - def GetHeight(self): - return self.height - - def GetWidth(self): - return self.width - - def InitRenderer(self): - self.region = self.CreateRegion() - self._Render() - - def CreateRegion(self): - region = wx.RegionFromBitmap(self.tbmp) - return region - - @staticmethod - def CopyRegion(region): - rect = region.GetBox() - - newRegion = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) - newRegion.IntersectRegion(region) - - return newRegion - - def GetRegion(self): - return self.CopyRegion(self.region) - - def Highlight(self, highlight=False): - self.highlighted = highlight - self._Render() - - def IsHighlighted(self): - return self.highlighted - - def Render(self): - return self.addBitmap - - def _Render(self): - if self.addBitmap: - del self.addBitmap - - alpha = 1 if self.highlighted else 0.3 - - img = self.addImg.AdjustChannels(1, 1, 1, alpha) - bbmp = wx.BitmapFromImage(img) - self.addBitmap = bbmp - - -class PFTabsContainer(wx.Panel): - def __init__(self, parent, pos=(0, 0), size=(100, 22), id=wx.ID_ANY, canAdd=True): - """ - Defines the tab container. Handles functions such as tab selection and - dragging, and defines minimum width of tabs (all tabs are of equal width, - which is determined via widest tab). Also handles the tab preview, if any. - """ - - wx.Panel.__init__(self, parent, id, pos, size) - if wx.VERSION >= (3, 0): - self.SetBackgroundStyle(wx.BG_STYLE_PAINT) - - self.tabs = [] - width, height = size - self.width = width - self.height = height - self.containerHeight = height - self.startDrag = False - self.dragging = False - self.sFit = Fit.getInstance() - self.efxBmp = None - - self.inclination = 7 - if canAdd: - self.reserved = 48 - else: - self.reserved = self.inclination * 4 - - self.dragTrail = 3 # pixel distance to drag before we actually start dragging - self.dragx = 0 - self.dragy = 0 - self.draggedTab = None - self.dragTrigger = self.dragTrail - - self.showAddButton = canAdd - - self.tabContainerWidth = width - self.reserved - self.tabMinWidth = width - self.tabShadow = None - - self.addButton = PFAddRenderer() - self.addBitmap = self.addButton.Render() - - self.previewTimer = None - self.previewTimerID = wx.ID_ANY - self.previewWnd = None - self.previewBmp = None - self.previewPos = None - self.previewTab = None - - self.Bind(wx.EVT_TIMER, self.OnTimer) - self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) - - self.Bind(wx.EVT_PAINT, self.OnPaint) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase) - self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) - self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) - self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp) - self.Bind(wx.EVT_MOTION, self.OnMotion) - self.Bind(wx.EVT_SIZE, self.OnSize) - self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged) - self.tabShadow = PFTabRenderer((self.tabMinWidth, self.height + 1), inclination=self.inclination) - - def OnSysColourChanged(self, event): - for tab in self.tabs: - tab.InitTab() - self.Refresh() - - def OnSize(self, event): - self.UpdateSize() - event.Skip() - - def UpdateSize(self): - width, _ = self.GetSize() - if width != self.width: - self.width = width - self.tabContainerWidth = self.width - self.reserved - self.AdjustTabsSize() - - def OnLeftDown(self, event): - """Determines what happens when user left clicks (down)""" - mposx, mposy = event.GetPosition() - if not self.startDrag: - tab = self.FindTabAtPos(mposx, mposy) - if tab: - self.CheckTabSelected(tab, mposx, mposy) - if self.showAddButton: - # If we can add tabs, we can drag them. Set flag - self.startDrag = True - tx, ty = tab.GetPosition() - self.dragx = mposx - tx - self.dragy = self.containerHeight - self.height - self.Refresh() - - self.draggedTab = tab - - def OnLeftUp(self, event): - """Determines what happens when user left clicks (up)""" - mposx, mposy = event.GetPosition() - if self.startDrag and self.dragging: - self.dragging = False - self.startDrag = False - self.draggedTab = None - self.dragTrigger = self.dragTrail - self.UpdateTabsPosition() - self.Refresh() - if self.HasCapture(): - self.ReleaseMouse() - - return - - if self.startDrag: - self.startDrag = False - self.dragTrigger = self.dragTrail - - # Checks if we selected the add button and, if True, returns - if self.CheckAddButton(mposx, mposy): - return - - # If there are no tabs, don't waste time - if self.GetTabsCount() == 0: - return - - # Gets selected tab (was set when user down clicked) - selTab = self.GetSelectedTab() - - # Check if we selected close button for selected tab - if self.CheckTabClose(selTab, mposx, mposy): - return - - # Check if we selected close button for all others - for tab in self.tabs: - if self.CheckTabClose(tab, mposx, mposy): - return - - def OnMiddleUp(self, event): - mposx, mposy = event.GetPosition() - - tab = self.FindTabAtPos(mposx, mposy) - - if tab is None or not tab.closeButton: # if not able to close, return False - return False - - index = self.tabs.index(tab) - ev = PageClosing(index) - wx.PostEvent(self.Parent, ev) - if ev.isVetoed(): - return False - - index = self.GetTabIndex(tab) - self.DeleteTab(index) - wx.PostEvent(self.Parent, PageClosed(index=index)) - sel = self.GetSelected() - - if sel is not None: - wx.PostEvent(self.Parent, PageChanged(-1, sel)) - - def GetSelectedTab(self): - for tab in self.tabs: - if tab.GetSelected(): - return tab - return None - - def GetSelected(self): - for tab in self.tabs: - if tab.GetSelected(): - return self.tabs.index(tab) - return None - - def SetSelected(self, tabIndex): - oldSelTab = self.GetSelectedTab() - oldSelTab.SetSelected(False) - self.tabs[tabIndex].SetSelected(True) - self.Refresh() - - def CheckTabSelected(self, tab, x, y): - """ - Selects the tab at x, y. If the tab at x, y is already selected, simply - return true. Otherwise, perform TabHitTest and set tab at position to - selected - """ - oldSelTab = self.GetSelectedTab() - if oldSelTab == tab: - return True - - if self.TabHitTest(tab, x, y): - if tab.disabled: - return - tab.SetSelected(True) - oldSelTab.SetSelected(False) - - ev = PageChanging(self.tabs.index(oldSelTab), self.tabs.index(tab)) - wx.PostEvent(self.Parent, ev) - - if ev.isVetoed(): - return False - - self.Refresh() - selTab = self.tabs.index(tab) - self.Parent.SetSelection(selTab) - - wx.PostEvent(self.Parent, PageChanged(self.tabs.index(oldSelTab), self.tabs.index(tab))) - - return True - - return False - - def CheckTabClose(self, tab, x, y): - """Determines if close button was selected for the given tab.""" - if not tab.closeButton: # if not able to close, return False - return False - - closeBtnReg = tab.GetCloseButtonRegion() - tabPosX, tabPosY = tab.GetPosition() - closeBtnReg.Offset(tabPosX, tabPosY) - - if closeBtnReg.Contains(x, y): - index = self.tabs.index(tab) - ev = PageClosing(index) - wx.PostEvent(self.Parent, ev) - if ev.isVetoed(): - return False - - index = self.GetTabIndex(tab) - self.DeleteTab(index) - wx.PostEvent(self.Parent, PageClosed(index=index)) - sel = self.GetSelected() - - if sel is not None: - wx.PostEvent(self.Parent, PageChanged(-1, sel)) - - return True - return False - - def CheckAddButton(self, x, y): - """Determines if add button was selected.""" - if not self.showAddButton: # if not able to add, return False - return - - reg = self.addButton.GetRegion() - ax, ay = self.addButton.GetPosition() - reg.Offset(ax, ay) - - if reg.Contains(x, y): - ev = PageAdding() - wx.PostEvent(self.Parent, ev) - if ev.isVetoed(): - return False - - self.Parent.AddPage() - wx.PostEvent(self.Parent, PageAdded()) - return True - - def CheckCloseButtons(self, x, y): - """ - Checks if mouse pos at x, y is over a close button. If so, set the - close hovering flag for that tab - """ - dirty = False - # @todo: maybe change to for...else - for tab in self.tabs: - closeBtnReg = tab.GetCloseButtonRegion() - tabPos = tab.GetPosition() - tabPosX, tabPosY = tabPos - closeBtnReg.Offset(tabPosX, tabPosY) - - if closeBtnReg.Contains(x, y): - if not tab.GetCloseButtonHoverStatus(): - tab.ShowCloseButtonHovering(True) - dirty = True - else: - if tab.GetCloseButtonHoverStatus(): - tab.ShowCloseButtonHovering(False) - dirty = True - if dirty: - self.Refresh() - - def FindTabAtPos(self, x, y): - if self.GetTabsCount() == 0: - return None - - selTab = self.GetSelectedTab() - if self.TabHitTest(selTab, x, y): - return selTab - - for tab in self.tabs: - if self.TabHitTest(tab, x, y): - return tab - return None - - @staticmethod - def TabHitTest(tab, x, y): - tabRegion = tab.GetTabRegion() - tabPos = tab.GetPosition() - tabPosX, tabPosY = tabPos - tabRegion.Offset(tabPosX, tabPosY) - - if tabRegion.Contains(x, y): - return True - - return False - - def GetTabAtLeft(self, tabIndex): - return self.tabs[tabIndex - 1] if tabIndex > 0 else None - - def GetTabAtRight(self, tabIndex): - return self.tabs[tabIndex + 1] if tabIndex < self.GetTabsCount() - 1 else None - - def SwitchTabs(self, src, dest, draggedTab=None): - self.tabs[src], self.tabs[dest] = self.tabs[dest], self.tabs[src] - self.UpdateTabsPosition(draggedTab) - self.Parent.SwitchPages(src, dest) - self.Refresh() - - def GetTabIndex(self, tab): - return self.tabs.index(tab) - - def OnMotion(self, event): - """ - Determines what happens when the mouse moves. This handles primarily - dragging (region tab can be dragged) as well as checking if we are over - an actionable button. - """ - mposx, mposy = event.GetPosition() - - if self.startDrag: - if not self.dragging: - if self.dragTrigger < 0: - self.dragging = True - self.dragTrigger = self.dragTrail - self.CaptureMouse() - else: - self.dragTrigger -= 1 - if self.dragging: - dtx = mposx - self.dragx - w, h = self.draggedTab.GetSize() - - if dtx < 0: - dtx = 0 - if dtx + w > self.tabContainerWidth + self.inclination * 2: - dtx = self.tabContainerWidth - w + self.inclination * 2 - self.draggedTab.SetPosition((dtx, self.dragy)) - - index = self.GetTabIndex(self.draggedTab) - - leftTab = self.GetTabAtLeft(index) - rightTab = self.GetTabAtRight(index) - - if leftTab: - lw, lh = leftTab.GetSize() - lx, ly = leftTab.GetPosition() - - if lx + lw / 2 - self.inclination * 2 > dtx: - self.SwitchTabs(index - 1, index, self.draggedTab) - return - - if rightTab: - rw, rh = rightTab.GetSize() - rx, ry = rightTab.GetPosition() - - if rx + rw / 2 + self.inclination * 2 < dtx + w: - self.SwitchTabs(index + 1, index, self.draggedTab) - return - self.UpdateTabsPosition(self.draggedTab) - self.Refresh() - return - return - - self.CheckCloseButtons(mposx, mposy) - self.CheckAddHighlighted(mposx, mposy) - self.CheckTabPreview(mposx, mposy) - - event.Skip() - - def CheckTabPreview(self, mposx, mposy): - """ - Checks to see if we have a tab preview and sets up the timer for it - to display - """ - if not self.sFit.serviceFittingOptions["showTooltip"] or False: - return - - if self.previewTimer: - if self.previewTimer.IsRunning(): - if self.previewWnd: - self.previewTimer.Stop() - return - - if self.previewWnd: - self.previewWnd.Show(False) - del self.previewWnd - self.previewWnd = None - - for tab in self.tabs: - if not tab.GetSelected(): - if self.TabHitTest(tab, mposx, mposy): - try: - page = self.Parent.GetPage(self.GetTabIndex(tab)) - if hasattr(page, "Snapshot"): - if not self.previewTimer: - self.previewTimer = wx.Timer(self, self.previewTimerID) - - self.previewTab = tab - self.previewTimer.Start(500, True) - break - except Exception as e: - pyfalog.critical("Exception caught in CheckTabPreview.") - pyfalog.critical(e) - - def CheckAddHighlighted(self, x, y): - """ - Checks to see if x, y are in add button region, and sets the highlight - flag - """ - if not self.showAddButton: - return - - reg = self.addButton.GetRegion() - ax, ay = self.addButton.GetPosition() - reg.Offset(ax, ay) - - if reg.Contains(x, y): - if not self.addButton.IsHighlighted(): - self.addButton.Highlight(True) - self.Refresh() - else: - if self.addButton.IsHighlighted(): - self.addButton.Highlight(False) - self.Refresh() - - def OnPaint(self, event): - if "wxGTK" in wx.PlatformInfo: - mdc = wx.AutoBufferedPaintDC(self) - else: - mdc = wx.BufferedPaintDC(self) - - if 'wxMac' in wx.PlatformInfo and wx.VERSION < (3, 0): - color = wx.Colour(0, 0, 0) - brush = wx.Brush(color) - - # noinspection PyPackageRequirements,PyUnresolvedReferences,PyUnresolvedReferences,PyUnresolvedReferences - from Carbon.Appearance import kThemeBrushDialogBackgroundActive - # noinspection PyUnresolvedReferences - brush.MacSetTheme(kThemeBrushDialogBackgroundActive) - else: - color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) - brush = wx.Brush(color) - - if "wxGTK" not in wx.PlatformInfo: - mdc.SetBackground(brush) - mdc.Clear() - - selected = None - - tabsWidth = 0 - - for tab in self.tabs: - tabsWidth += tab.tabWidth - self.inclination * 2 - - if self.showAddButton: - ax, ay = self.addButton.GetPosition() - mdc.DrawBitmap(self.addButton.Render(), ax, ay, True) - - for i in range(len(self.tabs) - 1, -1, -1): - tab = self.tabs[i] - posx, posy = tab.GetPosition() - - if not tab.IsSelected(): - mdc.DrawBitmap(self.efxBmp, posx, posy, True) - bmp = tab.Render() - img = bmp.ConvertToImage() - img = img.AdjustChannels(1, 1, 1, 0.85) - bmp = wx.BitmapFromImage(img) - mdc.DrawBitmap(bmp, posx, posy, True) - - else: - selected = tab - - if selected: - posx, posy = selected.GetPosition() - mdc.DrawBitmap(self.efxBmp, posx, posy, True) - - bmp = selected.Render() - - if self.dragging: - img = bmp.ConvertToImage() - img = img.AdjustChannels(1.2, 1.2, 1.2, 0.7) - bmp = wx.BitmapFromImage(img) - - mdc.DrawBitmap(bmp, posx, posy, True) - - def OnErase(self, event): - pass - - def UpdateTabFX(self): - w, h = self.tabShadow.GetSize() - if self.efxBmp is None or w != self.tabMinWidth: - self.tabShadow.SetSize((self.tabMinWidth, self.height + 1)) - fxBmp = self.tabShadow.Render() - - simg = fxBmp.ConvertToImage() - if not simg.HasAlpha(): - simg.InitAlpha() - simg = simg.Blur(2) - simg = simg.AdjustChannels(0.3, 0.3, 0.3, 0.35) - - self.efxBmp = wx.BitmapFromImage(simg) - - def AddTab(self, title=wx.EmptyString, img=None, showClose=False): - self.ClearTabsSelected() - - tabRenderer = PFTabRenderer((120, self.height), title, img, self.inclination, closeButton=showClose) - tabRenderer.SetSelected(True) - - self.tabs.append(tabRenderer) - self.AdjustTabsSize() - self.Refresh() - - def ClearTabsSelected(self): - for tab in self.tabs: - tab.SetSelected(False) - - def DisableTab(self, tab, disabled=True): - tabRenderer = self.tabs[tab] - tabRenderer.disabled = disabled - - self.AdjustTabsSize() - self.Refresh() - - def DeleteTab(self, tab, external=False): - tabRenderer = self.tabs[tab] - wasSelected = tabRenderer.GetSelected() - self.tabs.remove(tabRenderer) - - if tabRenderer: - del tabRenderer - - if wasSelected and self.GetTabsCount() > 0: - if tab > self.GetTabsCount() - 1: - self.tabs[self.GetTabsCount() - 1].SetSelected(True) - else: - self.tabs[tab].SetSelected(True) - - if not external: - self.Parent.DeletePage(tab, True) - - self.AdjustTabsSize() - self.Refresh() - - def GetTabsCount(self): - return len(self.tabs) - - def AdjustTabsSize(self): - tabMinWidth = 9000000 # Really, it should be over 9000 - tabMaxWidth = 0 - for tab in self.tabs: - mw, mh = tab.GetMinSize() - if tabMinWidth > mw: - tabMinWidth = mw - if tabMaxWidth < mw: - tabMaxWidth = mw - if tabMaxWidth < 100: - tabMaxWidth = 100 - if self.GetTabsCount() > 0: - if (self.GetTabsCount()) * (tabMaxWidth - self.inclination * 2) > self.tabContainerWidth: - self.tabMinWidth = float(self.tabContainerWidth) / float(self.GetTabsCount()) + self.inclination * 2 - else: - self.tabMinWidth = tabMaxWidth - - if self.tabMinWidth < 1: - self.tabMinWidth = 1 - for tab in self.tabs: - tab.GetSize() - tab.SetSize((self.tabMinWidth, self.height)) - - if self.GetTabsCount() > 0: - self.UpdateTabFX() - - self.UpdateTabsPosition() - - def UpdateTabsPosition(self, skipTab=None): - tabsWidth = 0 - for tab in self.tabs: - tabsWidth += tab.tabWidth - self.inclination * 2 - - pos = tabsWidth - selected = None - selpos = None - for i in range(len(self.tabs) - 1, -1, -1): - tab = self.tabs[i] - width = tab.tabWidth - self.inclination * 2 - pos -= width - if not tab.IsSelected(): - tab.SetPosition((pos, self.containerHeight - self.height)) - else: - selected = tab - selpos = pos - if selected is not skipTab: - selected.SetPosition((selpos, self.containerHeight - self.height)) - self.addButton.SetPosition( - ( - round(tabsWidth) + self.inclination * 2, - self.containerHeight - self.height / 2 - self.addButton.GetHeight() / 3 - ) - ) - - def OnLeaveWindow(self, event): - - if self.startDrag and not self.dragging: - self.dragging = False - self.startDrag = False - self.draggedTab = None - self.dragTrigger = self.dragTrail - if self.HasCapture(): - self.ReleaseMouse() - - if self.previewWnd: - self.previewWnd.Show(False) - del self.previewWnd - self.previewWnd = None - event.Skip() - - def OnTimer(self, event): - mposx, mposy = wx.GetMousePosition() - cposx, cposy = self.ScreenToClient((mposx, mposy)) - if self.FindTabAtPos(cposx, cposy) == self.previewTab: - if not self.previewTab.GetSelected(): - page = self.Parent.GetPage(self.GetTabIndex(self.previewTab)) - if page.Snapshot(): - self.previewWnd = PFNotebookPagePreview( - self, - (mposx + 3, mposy + 3), - page.Snapshot(), - self.previewTab.text - ) - self.previewWnd.Show() - - event.Skip() - - -class PFNotebookPagePreview(wx.Frame): - def __init__(self, parent, pos, bitmap, title): - wx.Frame.__init__( - self, - parent, - id=wx.ID_ANY, - title=wx.EmptyString, - pos=pos, - size=wx.DefaultSize, - style=wx.NO_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP - ) - - self.title = title - self.bitmap = bitmap - self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) - self.Bind(wx.EVT_PAINT, self.OnWindowPaint) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnWindowEraseBk) - self.Bind(wx.EVT_TIMER, self.OnTimer) - - self.timer = wx.Timer(self, wx.ID_ANY) - self.timerSleep = None - self.timerSleepId = wx.NewId() - self.direction = 1 - self.padding = 15 - self.transp = 0 - - hfont = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) - self.SetFont(hfont) - - tx, ty = self.GetTextExtent(self.title) - tx += self.padding * 2 - - if bitmap.GetWidth() < tx: - width = tx - else: - width = bitmap.GetWidth() - - self.SetSize((width, bitmap.GetHeight() + 16)) - - self.SetTransparent(0) - self.Refresh() - - def OnTimer(self, event): - self.transp += 20 * self.direction - - if self.transp > 220: - self.transp = 220 - self.timer.Stop() - - if self.transp < 0: - self.transp = 0 - self.timer.Stop() - wx.Frame.Show(self, False) - self.Destroy() - return - self.SetTransparent(self.transp) - - def RaiseParent(self): - wnd = self - lastwnd = None - while wnd is not None: - lastwnd = wnd - wnd = wnd.Parent - if lastwnd: - lastwnd.Raise() - - def Show(self, showWnd=True): - if showWnd: - wx.Frame.Show(self, showWnd) - self.RaiseParent() - self.direction = 1 - self.timer.Start(10) - else: - self.direction = -1 - self.timer.Start(10) - - def OnWindowEraseBk(self, event): - pass - - def OnWindowPaint(self, event): - rect = self.GetRect() - canvas = wx.EmptyBitmap(rect.width, rect.height) - mdc = wx.BufferedPaintDC(self) - mdc.SelectObject(canvas) - color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) - mdc.SetBackground(wx.Brush(color)) - mdc.Clear() - - font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) - mdc.SetFont(font) - - x, y = mdc.GetTextExtent(self.title) - - mdc.SetBrush(wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT))) - mdc.DrawRectangle(0, 0, rect.width, 16) - - mdc.SetTextForeground(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) - - mdc.DrawText(self.title, (rect.width - x) / 2, (16 - y) / 2) - - mdc.DrawBitmap(self.bitmap, 0, 16) - - mdc.SetPen(wx.Pen("#000000", width=1)) - mdc.SetBrush(wx.TRANSPARENT_BRUSH) - - mdc.DrawRectangle(0, 16, rect.width, rect.height - 16) diff --git a/gui/chrome_tabs.py b/gui/chrome_tabs.py new file mode 100644 index 000000000..c03edacab --- /dev/null +++ b/gui/chrome_tabs.py @@ -0,0 +1,1481 @@ +#=============================================================================== +# +# ToDo: Bug - when selecting close on a tab, sometimes the tab to the right is +# selected, most likely due to determination of mouse position +# ToDo: Tab Selection seems overly complicated. OnLeftDown finds tab at +# position, and then call's CheckTabSelected which calls TabHitTest (when +# we are already aware it will return due to FindTabAtPos) +# ToDo: Perhaps a better way of finding tabs at position instead of looping +# through them and getting their regions. Perhaps some smart trickery with +# mouse pos x (all tabs have same width, so we divide x by width to find +# tab index?). This will also help with finding close buttons. +# ToDo: Fix page preview code (PFNotebookPagePreview) +# +#= ============================================================================== + +import wx +import wx.lib.newevent + +from gui.bitmap_loader import BitmapLoader +from gui.utils import draw +from gui.utils import color as color_utils +from service.fit import Fit + +_PageChanging, EVT_NOTEBOOK_PAGE_CHANGING = wx.lib.newevent.NewEvent() +_PageChanged, EVT_NOTEBOOK_PAGE_CHANGED = wx.lib.newevent.NewEvent() +_PageAdding, EVT_NOTEBOOK_PAGE_ADDING = wx.lib.newevent.NewEvent() +_PageClosing, EVT_NOTEBOOK_PAGE_CLOSING = wx.lib.newevent.NewEvent() +PageAdded, EVT_NOTEBOOK_PAGE_ADDED = wx.lib.newevent.NewEvent() +PageClosed, EVT_NOTEBOOK_PAGE_CLOSED = wx.lib.newevent.NewEvent() + + +class VetoAble(): + def __init__(self): + self.__vetoed = False + + def Veto(self): + self.__vetoed = True + + def isVetoed(self): + return self.__vetoed + + +class NotebookTabChangeEvent(): + def __init__(self, old, new): + self.__old = old + self.__new = new + + def GetOldSelection(self): + return self.__old + + def GetSelection(self): + return self.__new + + OldSelection = property(GetOldSelection) + Selection = property(GetSelection) + + +class PageChanging(_PageChanging, NotebookTabChangeEvent, VetoAble): + def __init__(self, old, new): + _PageChanging.__init__(self) + NotebookTabChangeEvent.__init__(self, old, new) + VetoAble.__init__(self) + + +class PageChanged(_PageChanged, NotebookTabChangeEvent): + def __init__(self, old, new): + _PageChanged.__init__(self) + NotebookTabChangeEvent.__init__(self, old, new) + + +class PageClosing(_PageClosing, VetoAble): + def __init__(self, i): + _PageClosing.__init__(self) + self.__index = i + VetoAble.__init__(self) + self.Selection = property(self.GetSelection) + + def GetSelection(self): + return self.__index + + +class PageAdding(_PageAdding, VetoAble): + def __init__(self): + _PageAdding.__init__(self) + VetoAble.__init__(self) + + +class ChromeNotebook(wx.Panel): + + def __init__(self, parent, can_add=True): + """ + Instance of Notebook. Initializes general layout, includes methods + for setting current page, replacing pages, any public function for the + notebook + """ + super().__init__(parent, wx.ID_ANY, size=(-1, -1)) + + self._pages = [] + self._active_page = None + + main_sizer = wx.BoxSizer(wx.VERTICAL) + + tabs_sizer = wx.BoxSizer(wx.VERTICAL) + self.tabs_container = _TabsContainer(self, can_add=can_add) + tabs_sizer.Add(self.tabs_container, 0, wx.EXPAND) + + if 'wxMSW' in wx.PlatformInfo: + style = wx.DOUBLE_BORDER + else: + style = wx.SIMPLE_BORDER + + back_color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + + content_sizer = wx.BoxSizer(wx.VERTICAL) + self.page_container = wx.Panel(self, style=style) + self.page_container.SetBackgroundColour(back_color) + content_sizer.Add(self.page_container, 1, wx.EXPAND, 5) + + main_sizer.Add(tabs_sizer, 0, wx.EXPAND, 5) + main_sizer.Add(content_sizer, 1, wx.EXPAND | wx.BOTTOM, 2) + + self.SetSizer(main_sizer) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Layout() + + def GetPage(self, i): + return self._pages[i] + + def SetPage(self, i, page): + if i >= len(self._pages) or i is None or page is None: + return + + old_page = self._pages[i] + self._pages[i] = page + if old_page == self._active_page: + old_page.Destroy() + self._active_page = page + else: + old_page.Destroy() + + page.Reparent(self.page_container) + + if self._active_page == page: + self.ShowActive() + + def GetBorders(self): + """Gets border widths to better determine page size in ShowActive()""" + + bx = wx.SystemSettings.GetMetric(wx.SYS_BORDER_X) + by = wx.SystemSettings.GetMetric(wx.SYS_BORDER_Y) + + if bx < 0: + bx = 1 + if by < 0: + by = 1 + + return bx, by + + def ReplaceActivePage(self, page): + self.SetPage(self.GetSelection(), page) + + def GetSelectedPage(self): + return self._active_page + + def GetPageIndex(self, page): + return self._pages.index(page) if page in self._pages else None + + def GetSelection(self): + return self.GetPageIndex(self._active_page) + + def GetCurrentPage(self): + return self._active_page + + def GetPageCount(self): + return len(self._pages) + + def NextPage(self): + """Used with keyboard shortcut for next page navigation""" + current_page = self.GetSelection() + + if current_page is None: + return + + if current_page < self.GetPageCount() - 1: + self.SetSelection(current_page + 1) + new_page = current_page + 1 + else: + self.SetSelection(0) + new_page = 0 + + wx.PostEvent(self, PageChanged(current_page, new_page)) + + def PrevPage(self): + """Used with keyboard shortcut for previous page navigation""" + current_page = self.GetSelection() + + if current_page is None: + return + + if current_page > 0: + self.SetSelection(current_page - 1) + new_page = current_page - 1 + else: + self.SetSelection(self.GetPageCount() - 1) + new_page = self.GetPageCount() - 1 + + wx.PostEvent(self, PageChanged(current_page, new_page)) + + def AddPage(self, win=None, title="Empty Tab", image: wx.Image=None, closeable=True): + if self._active_page: + self._active_page.Hide() + + if not win: + win = wx.Panel(self) + + win.Reparent(self.page_container) + + self.page_container.Layout() + + self._pages.append(win) + self.tabs_container.AddTab(title, image, closeable) + + self._active_page = win + self.ShowActive(True) + + def DisablePage(self, page, toggle): + idx = self.GetPageIndex(page) + + if toggle and page == self._active_page: + try: + # Set page to the first non-disabled page + self.SetSelection(next(i for i, _ in enumerate(self._pages) if not self.tabs_container.tabs[i].disabled)) + except StopIteration: + self.SetSelection(0) + + self.tabs_container.DisableTab(idx, toggle) + + def SetSelection(self, page): + old_selection = self.GetSelection() + if old_selection != page: + self._active_page.Hide() + self._active_page = self._pages[page] + self.tabs_container.SetSelected(page) + self.ShowActive() + + def DeletePage(self, n): + page = self._pages[n] + self._pages.remove(page) + page.Destroy() + + self.tabs_container.DeleteTab(n) + + selection = self.tabs_container.GetSelected() + if selection is not None: + self._active_page = self._pages[selection] + self.ShowActive() + wx.PostEvent(self, PageChanged(-1, selection)) + else: + self._active_page = None + + def SwitchPages(self, src, dst): + self._pages[src], self._pages[dst] = self._pages[dst], self._pages[src] + + def ShowActive(self, resize_only=False): + """ + Sets the size of the page and shows. The sizing logic adjusts for some + minor sizing errors (scrollbars going beyond bounds) + + resize_only + if we are not interested in showing the page, only setting the size + + @todo: is resize_only still needed? Was introduced with 8b8b97 in mid + 2011 to fix a resizing bug with blank _pages, cannot reproduce + 13Sept2014 + """ + + ww, wh = self.page_container.GetSize() + bx, by = self.GetBorders() + ww -= bx * 4 + wh -= by * 4 + self._active_page.SetSize((max(ww, -1), max(wh, -1))) + self._active_page.SetPosition((0, 0)) + + if not resize_only: + self._active_page.Show() + + self.Layout() + + def IsActive(self, page): + return self._active_page == page + + def SetPageTitle(self, i, text, refresh=True): + tab = self.tabs_container.tabs[i] + tab.text = text + if refresh: + self.tabs_container.AdjustTabsSize() + self.Refresh() + + def SetPageIcon(self, i, icon, refresh=True): + tab = self.tabs_container.tabs[i] + tab.tab_img = icon + if refresh: + self.tabs_container.AdjustTabsSize() + self.Refresh() + + def SetPageTextIcon(self, i, text=wx.EmptyString, icon=None): + self.SetPageTitle(i, text, False) + self.SetPageIcon(i, icon, False) + self.tabs_container.AdjustTabsSize() + self.Refresh() + + def Refresh(self): + self.tabs_container.Refresh() + + def OnSize(self, event): + w, h = self.GetSize() + self.tabs_container.SetSize((w, -1)) + self.tabs_container.UpdateSize() + self.tabs_container.Refresh() + self.Layout() + + if self._active_page: + self.ShowActive() + event.Skip() + + +class _TabRenderer: + def __init__(self, size=(36, 24), text=wx.EmptyString, img: wx.Image=None, + closeable=True): + + # tab left/right zones inclination + self.ctab_left = BitmapLoader.getImage("ctableft", "gui") + self.ctab_middle = BitmapLoader.getImage("ctabmiddle", "gui") + self.ctab_right = BitmapLoader.getImage("ctabright", "gui") + self.ctab_close = BitmapLoader.getImage("ctabclose", "gui") + + self.left_width = self.ctab_left.GetWidth() + self.right_width = self.ctab_right.GetWidth() + self.middle_width = self.ctab_middle.GetWidth() + self.close_btn_width = self.ctab_close.GetWidth() + + width, height = size + + self.min_width = self.left_width + self.right_width + self.middle_width + self.min_height = self.ctab_middle.GetHeight() + + # set minimum width and height to what is allotted to images + width = max(width, self.min_width) + height = max(height, self.min_height) + + self.disabled = False + self.text = text + self.tab_size = (width, height) + self.closeable = closeable + self.selected = False + self.close_btn_hovering = False + self.tab_bitmap = None + self.tab_back_bitmap = None + self.padding = 4 + self.font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + + self.tab_img = img + self.position = (0, 0) # Not used internally for rendering - helper for tab container + self.InitTab() + + def SetPosition(self, position): + self.position = position + + def GetPosition(self): + return self.position + + def GetSize(self): + return self.tab_size + + def SetSize(self, size): + width, height = size + + width = max(width, self.min_width) + height = max(height, self.min_height) + + self.tab_size = (width, height) + self.InitTab() + + def SetSelected(self, sel=True): + self.selected = sel + self.InitTab() + + def GetSelected(self): + return self.selected + + def IsSelected(self): + return self.selected + + def ShowCloseButtonHovering(self, hover=True): + if self.close_btn_hovering != hover: + self.close_btn_hovering = hover + self._Render() + + def GetCloseButtonHoverStatus(self): + return self.close_btn_hovering + + def GetTabRegion(self): + new_region = self.CopyRegion(self.tab_region) + new_region.Subtract(self.close_region) if self.closeable else self.tab_region + return new_region + + def GetCloseButtonRegion(self): + return self.CopyRegion(self.close_region) + + def GetMinSize(self): + ebmp = wx.Bitmap(1, 1) + mdc = wx.MemoryDC() + mdc.SelectObject(ebmp) + mdc.SetFont(self.font) + textSizeX, textSizeY = mdc.GetTextExtent(self.text) + totalSize = self.left_width + self.right_width + textSizeX + self.close_btn_width / 2 + 16 + self.padding* 2 + mdc.SelectObject(wx.NullBitmap) + return totalSize, self.tab_height + + def SetTabImage(self, img): + self.tab_img = img + + def CopyRegion(self, region): + rect = region.GetBox() + + newRegion = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) + newRegion.Intersect(region) + + return newRegion + + def InitTab(self): + self.tab_width, self.tab_height = self.tab_size + + self.content_width = self.tab_width - self.left_width - self.right_width + self.tab_region = None + self.close_region = None + + self.InitColors() + self.InitBitmaps() + + self.ComposeTabBack() + self.InitTabRegions() + self._Render() + + def InitBitmaps(self): + """ + Creates bitmap for tab + + Takes the bitmaps already set and replaces a known color (black) with + the needed color, while also considering selected state. Color dependant + on platform -- see InitColors(). + """ + if self.selected: + tr, tg, tb, ta = self.selected_color + else: + tr, tg, tb, ta = self.inactive_color + + ctab_left = self.ctab_left.Copy() + ctab_right = self.ctab_right.Copy() + ctab_middle = self.ctab_middle.Copy() + + ctab_left.Replace(0, 0, 0, tr, tg, tb) + ctab_right.Replace(0, 0, 0, tr, tg, tb) + ctab_middle.Replace(0, 0, 0, tr, tg, tb) + + self.ctab_left_bmp = wx.Bitmap(ctab_left) + self.ctab_right_bmp = wx.Bitmap(ctab_right) + self.ctab_middle_bmp = wx.Bitmap(ctab_middle) + self.ctab_close_bmp = wx.Bitmap(self.ctab_close) + + def ComposeTabBack(self): + """ + Creates the tab background bitmap based upon calculated dimension values + and modified bitmaps via InitBitmaps() + """ + bk_bmp = wx.Bitmap(self.tab_width, self.tab_height) + + mdc = wx.MemoryDC() + mdc.SelectObject(bk_bmp) + mdc.Clear() + + # draw the left bitmap + mdc.DrawBitmap(self.ctab_left_bmp, 0, 0) + + # convert middle bitmap and scale to tab width + cm = self.ctab_middle_bmp.ConvertToImage() + mimg = cm.Scale(self.content_width, self.ctab_middle.GetHeight(), + wx.IMAGE_QUALITY_NORMAL) + mbmp = wx.Bitmap(mimg) + + # draw middle bitmap, offset by left + mdc.DrawBitmap(mbmp, self.left_width, 0) + + # draw right bitmap offset by left + middle + mdc.DrawBitmap(self.ctab_right_bmp, + self.content_width + self.left_width, 0) + + mdc.SelectObject(wx.NullBitmap) + + if self.tab_back_bitmap: + del self.tab_back_bitmap + + self.tab_back_bitmap = bk_bmp + + def InitTabRegions(self): + """ + Initializes regions for tab, which makes it easier to determine if + given coordinates are included in a region + """ + self.tab_region = wx.Region(self.tab_back_bitmap) + self.close_region = wx.Region(self.ctab_close_bmp) + + x_offset = self.content_width \ + + self.left_width \ + - self.ctab_close_bmp.GetWidth() / 2 + y_offset = (self.tab_height - self.ctab_close_bmp.GetHeight()) / 2 + self.close_region.Offset(x_offset, y_offset) + + def InitColors(self): + """Determines colors used for tab, based on system settings""" + self.tab_color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE) + self.inactive_color = color_utils.GetSuitable(self.tab_color, 0.25) + self.selected_color = color_utils.GetSuitable(self.tab_color, 0.10) + + def Render(self): + return self.tab_bitmap + + def _Render(self): + """Renders the tab, complete with the icon, text, and close button""" + if self.tab_bitmap: + del self.tab_bitmap + + height = self.tab_height + + canvas = wx.Bitmap(self.tab_width, self.tab_height, 24) + + mdc = wx.MemoryDC() + + mdc.SelectObject(canvas) + mdc.Clear() + + mdc.DrawBitmap(self.tab_back_bitmap, 0, 0, True) + + # draw the tab icon + if self.tab_img: + bmp = wx.Bitmap(self.tab_img.ConvertToGreyscale() if self.disabled else self.tab_img) + # @todo: is this conditional relevant anymore? + if self.content_width > 16: + # Draw tab icon + mdc.DrawBitmap( + bmp, + self.left_width + self.padding - bmp.GetWidth() / 2, + (height - bmp.GetHeight()) / 2) + text_start = self.left_width + self.padding + bmp.GetWidth() / 2 + else: + text_start = self.left_width + + mdc.SetFont(self.font) + + maxsize = self.tab_width \ + - text_start \ + - self.right_width \ + - self.padding * 4 + color = self.selected_color if self.selected else self.inactive_color + + mdc.SetTextForeground(color_utils.GetSuitable(color, 1)) + + # draw text (with no ellipses) + text = draw.GetPartialText(mdc, self.text, maxsize, "") + tx, ty = mdc.GetTextExtent(text) + mdc.DrawText(text, text_start + self.padding, height / 2 - ty / 2) + + # draw close button + if self.closeable: + if self.close_btn_hovering: + cbmp = self.ctab_close_bmp + else: + cimg = self.ctab_close_bmp.ConvertToImage() + cimg = cimg.AdjustChannels(0.7, 0.7, 0.7, 0.3) + cbmp = wx.Bitmap(cimg) + + mdc.DrawBitmap( + cbmp, + self.content_width + self.left_width - cbmp.GetWidth() / 2, + (height - cbmp.GetHeight()) / 2) + + mdc.SelectObject(wx.NullBitmap) + + canvas.SetMaskColour((0x12, 0x23, 0x32)) + img = canvas.ConvertToImage() + + if not img.HasAlpha(): + img.InitAlpha() + + bmp = wx.Bitmap(img) + self.tab_bitmap = bmp + + def __repr__(self): + return "_TabRenderer(text={}, disabled={}) at {}".format( + self.text, self.disabled, hex(id(self)) + ) + + +class _AddRenderer: + def __init__(self): + """Renders the add tab button""" + self.add_img = BitmapLoader.getImage("ctabadd", "gui") + self.width = self.add_img.GetWidth() + self.height = self.add_img.GetHeight() + + self.region = None + self.tbmp = wx.Bitmap(self.add_img) + self.add_bitmap = None + + self.position = (0, 0) + self.highlighted = False + + self.InitRenderer() + + def GetPosition(self): + return self.position + + def SetPosition(self, pos): + self.position = pos + + def GetSize(self): + return self.width, self.height + + def GetHeight(self): + return self.height + + def GetWidth(self): + return self.width + + def InitRenderer(self): + self.region = self.CreateRegion() + self._Render() + + def CreateRegion(self): + region = wx.Region(self.tbmp) + return region + + def CopyRegion(self, region): + rect = region.GetBox() + + new_region = wx.Region(rect.X, rect.Y, rect.Width, rect.Height) + new_region.Intersect(region) + + return new_region + + def GetRegion(self): + return self.CopyRegion(self.region) + + def Highlight(self, highlight=False): + self.highlighted = highlight + self._Render() + + def IsHighlighted(self): + return self.highlighted + + def Render(self): + return self.add_bitmap + + def _Render(self): + if self.add_bitmap: + del self.add_bitmap + + alpha = 1 if self.highlighted else 0.3 + + img = self.add_img.AdjustChannels(1, 1, 1, alpha) + bmp = wx.Bitmap(img) + self.add_bitmap = bmp + + +class _TabsContainer(wx.Panel): + def __init__(self, parent, pos=(50, 0), size=(100, 22), id=wx.ID_ANY, + can_add=True): + """ + Defines the tab container. Handles functions such as tab selection and + dragging, and defines minimum width of tabs (all tabs are of equal + width, which is determined via widest tab). Also handles the tab + preview, if any. + """ + + super().__init__(parent, id, pos, size) + + self.tabs = [] + self.width, self.height = size + self.container_height = self.height + self.start_drag = False + self.dragging = False + + # amount of overlap of tabs? + self.inclination = 7 + + if can_add: + self.reserved = 48 + else: + self.reserved = self.inclination * 4 + + # pixel distance to drag before we actually start dragging + self.drag_trail = 5 + + self.dragx = 0 + self.dragy = 0 + self.dragged_tab = None + self.drag_trigger = self.drag_trail + + self.show_add_button = can_add + + self.tab_container_width = self.width - self.reserved + self.tab_min_width = self.width + self.tab_shadow = _TabRenderer((self.tab_min_width, self.height + 1)) + + self.add_button = _AddRenderer() + self.add_bitmap = self.add_button.Render() + + self.preview_timer = None + self.preview_timer_id = wx.ID_ANY + self.preview_wnd = None + self.preview_bmp = None + self.preview_pos = None + self.preview_tab = None + + self.Bind(wx.EVT_TIMER, self.OnTimer) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow) + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase) + self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) + self.Bind(wx.EVT_MOTION, self.OnMotion) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChanged) + + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + + def OnSysColourChanged(self, event): + for tab in self.tabs: + tab.InitTab() + self.Refresh() + + def OnSize(self, event): + self.UpdateSize() + event.Skip() + + def UpdateSize(self): + """Update tab sizes based on new tab container size""" + width, _ = self.GetSize() + if width != self.width: + self.width = width + self.tab_container_width = self.width - self.reserved + self.AdjustTabsSize() + + def OnLeftDown(self, event): + """ Select tab on mouse down and start dragging logic """ + mposx, mposy = event.GetPosition() + if not self.start_drag: + tab = self.FindTabAtPos(mposx, mposy) + if tab: + self.CheckTabSelected(tab, mposx, mposy) + if self.show_add_button: + # If we can add tabs, we can drag them. Set flag + self.start_drag = True + tx, ty = tab.GetPosition() + self.dragx = mposx - tx + self.dragy = self.container_height - self.height + self.Refresh() + + self.dragged_tab = tab + + def OnMotion(self, event): + """ + Determines what happens when the mouse moves. This handles primarily + dragging (region tab can be dragged) as well as checking if we are over + an actionable button. + """ + mposx, mposy = event.GetPosition() + + if self.start_drag: + if not self.dragging: + if self.drag_trigger < 0: + self.dragging = True + self.drag_trigger = self.drag_trail + self.CaptureMouse() + else: + self.drag_trigger -= 1 + if self.dragging: + # we wish to keep tab within tab container boundaries. To do + # this, we must detect when mouse moves outside of boundaries. + # Set hard limits to 0 and the size of tab container. + dtx = mposx - self.dragx + w, h = self.dragged_tab.GetSize() + + dtx = max(dtx, 0) + + if dtx + w > self.tab_container_width + self.inclination * 2: + dtx = self.tab_container_width - w + self.inclination * 2 + + self.dragged_tab.SetPosition((dtx, self.dragy)) + + # we must modify the surrounding tabs + index = self.GetTabIndex(self.dragged_tab) + + left_tab = self.GetTabAtLeft(index) + right_tab = self.GetTabAtRight(index) + + if left_tab: + lw, lh = left_tab.GetSize() + lx, ly = left_tab.GetPosition() + + if lx + lw / 2 - self.inclination * 2 > dtx: + self.SwitchTabs(index - 1, index, self.dragged_tab) + + if right_tab: + rw, rh = right_tab.GetSize() + rx, ry = right_tab.GetPosition() + + if rx + rw / 2 + self.inclination * 2 < dtx + w: + self.SwitchTabs(index + 1, index, self.dragged_tab) + + self.UpdateTabsPosition(self.dragged_tab) + self.Refresh() + return + + # If we aren't dragging, check for actionable buttons under mouse + self.CheckCloseHighlighted(mposx, mposy) + self.CheckAddHighlighted(mposx, mposy) + self.CheckTabPreview(mposx, mposy) + + event.Skip() + + def OnLeftUp(self, event): + """Determines what happens when user left clicks (up)""" + mposx, mposy = event.GetPosition() + if self.start_drag and self.dragging: + self.dragging = False + self.start_drag = False + self.dragged_tab = None + self.drag_trigger = self.drag_trail + self.UpdateTabsPosition() + self.Refresh() + + if self.HasCapture(): + self.ReleaseMouse() + + return + + if self.start_drag: + self.start_drag = False + self.drag_trigger = self.drag_trail + + # Checks if we selected the add button and, if True, returns + if self.CheckAddButton(mposx, mposy): + return + + # If there are no tabs, don't waste time + if self.GetTabsCount() == 0: + return + + # Gets selected tab (was set when user down clicked) + sel_tab = self.GetSelectedTab() + + # Check if we selected close button for selected tab + if self.CheckTabClose(sel_tab, mposx, mposy): + return + + # Check if we selected close button for all others + for tab in self.tabs: + if self.CheckTabClose(tab, mposx, mposy): + return + + def DisableTab(self, tab, disabled=True): + tb_renderer = self.tabs[tab] + tb_renderer.disabled = disabled + + self.AdjustTabsSize() + self.Refresh() + + def GetSelectedTab(self): + for tab in self.tabs: + if tab.GetSelected(): + return tab + return None + + def GetSelected(self): + for tab in self.tabs: + if tab.GetSelected(): + return self.tabs.index(tab) + return None + + def SetSelected(self, tabIndex): + """Set tab as selected given its index""" + old_sel_tab = self.GetSelectedTab() + old_sel_tab.SetSelected(False) + self.tabs[tabIndex].SetSelected(True) + self.Refresh() + + def CheckTabSelected(self, tab, x, y): + """ + Selects the tab at x, y. If the tab at x, y is already selected, simply + return true. Otherwise, perform TabHitTest and set tab at position to + selected + """ + old_sel_tab = self.GetSelectedTab() + if old_sel_tab == tab: + return True + + if self.TabHitTest(tab, x, y): + if tab.disabled: + return + tab.SetSelected(True) + old_sel_tab.SetSelected(False) + + ev = PageChanging(self.tabs.index(old_sel_tab), self.tabs.index(tab)) + wx.PostEvent(self.Parent, ev) + + if ev.isVetoed(): + return False + + self.Refresh() + sel_tab = self.tabs.index(tab) + self.Parent.SetSelection(sel_tab) + + wx.PostEvent(self.Parent, PageChanged(self.tabs.index(old_sel_tab), + self.tabs.index(tab))) + + return True + + return False + + def CheckTabClose(self, tab, x, y): + """ + Determines if close button was selected for the given tab by comparing + x and y position with known close button region + """ + if not tab.closeable: # if not able to close, return False + return False + + region = tab.GetCloseButtonRegion() + posx, posy = tab.GetPosition() + region.Offset(posx, posy) + + if region.Contains(x, y): + index = self.tabs.index(tab) + ev = PageClosing(index) + wx.PostEvent(self.Parent, ev) + + if ev.isVetoed(): + return False + + index = self.GetTabIndex(tab) + self.Parent.DeletePage(index) + wx.PostEvent(self.Parent, PageClosed(index=index)) + + sel = self.GetSelected() + if sel is not None: + wx.PostEvent(self.Parent, PageChanged(-1, sel)) + + return True + return False + + def CheckAddButton(self, x, y): + """ + Determines if add button was selected by comparing x and y position with + add button region + """ + if not self.show_add_button: # if not able to add, return False + return + + region = self.add_button.GetRegion() + ax, ay = self.add_button.GetPosition() + region.Offset(ax, ay) + + if region.Contains(x, y): + ev = PageAdding() + wx.PostEvent(self.Parent, ev) + if ev.isVetoed(): + return False + + self.Parent.AddPage() + wx.PostEvent(self.Parent, PageAdded()) + return True + + def CheckCloseHighlighted(self, x, y): + """ + Checks if mouse pos at x, y is over a close button. If so, set the + close hovering flag for that tab + """ + dirty = False + + for tab in self.tabs: + region = tab.GetCloseButtonRegion() + posx, posy = tab.GetPosition() + region.Offset(posx, posy) + + if region.Contains(x, y): + if not tab.GetCloseButtonHoverStatus(): + tab.ShowCloseButtonHovering(True) + dirty = True + else: + if tab.GetCloseButtonHoverStatus(): + tab.ShowCloseButtonHovering(False) + dirty = True + if dirty: + self.Refresh() + break + + def FindTabAtPos(self, x, y): + if self.GetTabsCount() == 0: + return None + + # test current tab first + sel_tab = self.GetSelectedTab() + if self.TabHitTest(sel_tab, x, y): + return sel_tab + + # test all other tabs next + for tab in self.tabs: + if self.TabHitTest(tab, x, y): + return tab + + return None + + def TabHitTest(self, tab, x, y): + """ Test if x and y are contained within a tabs region """ + tabRegion = tab.GetTabRegion() + tabPos = tab.GetPosition() + tabPosX, tabPosY = tabPos + tabRegion.Offset(tabPosX, tabPosY) + + if tabRegion.Contains(x, y): + return True + + return False + + def GetTabAtLeft(self, tab_index): + return self.tabs[tab_index - 1] if tab_index > 0 else None + + def GetTabAtRight(self, tab_index): + if tab_index < self.GetTabsCount() - 1: + return self.tabs[tab_index + 1] + else: + return None + + def SwitchTabs(self, src, dst, dragged_tab=None): + self.tabs[src], self.tabs[dst] = self.tabs[dst], self.tabs[src] + self.UpdateTabsPosition(dragged_tab) + self.Parent.SwitchPages(src, dst) + self.Refresh() + + def GetTabIndex(self, tab): + return self.tabs.index(tab) + + def CheckTabPreview(self, mposx, mposy): + """ + Checks to see if we have a tab preview and sets up the timer for it + to display + """ + sFit = Fit.getInstance() + if not sFit.serviceFittingOptions["showTooltip"] or False: + return + + if self.preview_timer: + if self.preview_timer.IsRunning(): + if self.preview_wnd: + self.preview_timer.Stop() + return + + if self.preview_wnd: + self.preview_wnd.Show(False) + del self.preview_wnd + self.preview_wnd = None + + for tab in self.tabs: + if not tab.GetSelected(): + if self.TabHitTest(tab, mposx, mposy): + try: + page = self.Parent.GetPage(self.GetTabIndex(tab)) + if hasattr(page, "Snapshot"): + if not self.preview_timer: + self.preview_timer = wx.Timer( + self, self.preview_timer_id) + + self.preview_tab = tab + self.preview_timer.Start(500, True) + break + except: + pass + + def CheckAddHighlighted(self, x, y): + """ + Checks to see if x, y are in add button region, and sets the highlight + flag + """ + if not self.show_add_button: + return + + region = self.add_button.GetRegion() + ax, ay = self.add_button.GetPosition() + region.Offset(ax, ay) + + if region.Contains(x, y): + if not self.add_button.IsHighlighted(): + self.add_button.Highlight(True) + self.Refresh() + else: + if self.add_button.IsHighlighted(): + self.add_button.Highlight(False) + self.Refresh() + + def OnPaint(self, event): + mdc = wx.AutoBufferedPaintDC(self) + + # if 'wxMac' in wx.PlatformInfo: + # color = wx.Colour(0, 0, 0) + # brush = wx.Brush(color) + # # @todo: what needs to be changed with wxPheonix? + # from Carbon.Appearance import kThemeBrushDialogBackgroundActive + # brush.MacSetTheme(kThemeBrushDialogBackgroundActive) + # else: + color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE) + brush = wx.Brush(color) + + if "wxGTK" not in wx.PlatformInfo: + mdc.SetBackground(brush) + mdc.Clear() + + selected = None + + if self.show_add_button: + ax, ay = self.add_button.GetPosition() + mdc.DrawBitmap(self.add_button.Render(), ax, ay, True) + + for i in range(len(self.tabs) - 1, -1, -1): + tab = self.tabs[i] + posx, posy = tab.GetPosition() + + if not tab.IsSelected(): + # drop shadow first + mdc.DrawBitmap(self.fx_bmp, posx, posy, True) + bmp = tab.Render() + img = bmp.ConvertToImage() + img = img.AdjustChannels(1, 1, 1, 0.85) + bmp = wx.Bitmap(img) + mdc.DrawBitmap(bmp, posx, posy, True) + else: + selected = tab + + # we draw selected tab separately (instead of in else) so as to not hide + # the front bit behind the preceding tab + if selected: + posx, posy = selected.GetPosition() + # drop shadow first + mdc.DrawBitmap(self.fx_bmp, posx, posy, True) + + bmp = selected.Render() + + if self.dragging: + img = bmp.ConvertToImage() + img = img.AdjustChannels(1.2, 1.2, 1.2, 0.7) + bmp = wx.Bitmap(img) + + mdc.DrawBitmap(bmp, posx, posy, True) + + def OnErase(self, event): + pass + + def UpdateTabFX(self): + """ Updates tab drop shadow bitmap """ + self.tab_shadow.SetSize((self.tab_min_width, self.height + 1)) + fx_bmp = self.tab_shadow.Render() + + img = fx_bmp.ConvertToImage() + if not img.HasAlpha(): + img.InitAlpha() + img = img.Blur(2) + img = img.AdjustChannels(0.3, 0.3, 0.3, 0.35) + + self.fx_bmp = wx.Bitmap(img) + + def AddTab(self, title=wx.EmptyString, img=None, closeable=False): + self.ClearTabsSelected() + + tab_renderer = _TabRenderer((200, self.height), title, img, closeable) + tab_renderer.SetSelected(True) + + self.tabs.append(tab_renderer) + self.AdjustTabsSize() + self.Refresh() + + def ClearTabsSelected(self): + for tab in self.tabs: + tab.SetSelected(False) + + def DeleteTab(self, tab): + tab_renderer = self.tabs[tab] + was_selected = tab_renderer.GetSelected() + self.tabs.remove(tab_renderer) + + if tab_renderer: + del tab_renderer + + # determine our new selection + if was_selected and self.GetTabsCount() > 0: + if tab > self.GetTabsCount() - 1: + self.tabs[self.GetTabsCount() - 1].SetSelected(True) + else: + self.tabs[tab].SetSelected(True) + + self.AdjustTabsSize() + self.Refresh() + + def GetTabsCount(self): + return len(self.tabs) + + def AdjustTabsSize(self): + """ + Adjust tab sizes to ensure that they are all consistent and can fit into + the tab container. + """ + + # first we loop through our tabs and calculate the the largest tab. This + # is the size that we will base our calculations off + + max_width = 100 # Tab should be at least 100 + for tab in self.tabs: + mw, _ = tab.GetMinSize() # Tab min size includes tab contents + max_width = max(mw, max_width) + + # Divide tab container by number of tabs and add inclination. This will + # return the ideal max size for the containers size + if self.GetTabsCount() > 0: + dx = self.tab_container_width / self.GetTabsCount() + self.inclination * 2 + self.tab_min_width = min(dx, max_width) + + # Apply new size to all tabs + for tab in self.tabs: + tab.SetSize((self.tab_min_width, self.height)) + + if self.GetTabsCount() > 0: + # update drop shadow based on new sizes + self.UpdateTabFX() + + self.UpdateTabsPosition() + + def UpdateTabsPosition(self, skip_tab=None): + tabsWidth = 0 + for tab in self.tabs: + tabsWidth += tab.tab_width - self.inclination * 2 + + pos = tabsWidth + selected = None + for i in range(len(self.tabs) - 1, -1, -1): + tab = self.tabs[i] + width = tab.tab_width - self.inclination * 2 + pos -= width + if not tab.IsSelected(): + tab.SetPosition((pos, self.container_height - self.height)) + else: + selected = tab + selpos = pos + + if selected is not skip_tab: + selected.SetPosition((selpos, self.container_height - self.height)) + + self.add_button.SetPosition((round(tabsWidth) + self.inclination * 2, + self.container_height - self.height / 2 - self.add_button.GetHeight() / 3)) + + def OnLeaveWindow(self, event): + if self.start_drag and not self.dragging: + self.dragging = False + self.start_drag = False + self.dragged_tab = None + self.drag_trigger = self.drag_trail + if self.HasCapture(): + self.ReleaseMouse() + + if self.preview_wnd: + self.preview_wnd.Show(False) + del self.preview_wnd + self.preview_wnd = None + event.Skip() + + def OnTimer(self, event): + mposx, mposy = wx.GetMousePosition() + cposx, cposy = self.ScreenToClient((mposx, mposy)) + + if self.FindTabAtPos(cposx, cposy) == self.preview_tab: + if not self.preview_tab.GetSelected(): + page = self.Parent.GetPage(self.GetTabIndex(self.preview_tab)) + if page.Snapshot(): + + self.preview_wnd = PFNotebookPagePreview( + self, + (mposx + 3, mposy + 3), + page.Snapshot(), + self.preview_tab.text) + self.preview_wnd.Show() + + event.Skip() + + +class PFNotebookPagePreview(wx.Frame): + def __init__(self, parent, pos, bitmap, title): + super().__init__(parent, id=wx.ID_ANY, title=wx.EmptyString, pos=pos, + size=wx.DefaultSize, style=wx.NO_BORDER | + wx.FRAME_NO_TASKBAR | + wx.STAY_ON_TOP) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + self.title = title + self.bitmap = bitmap + self.SetSize((bitmap.GetWidth(), bitmap.GetHeight())) + self.Bind(wx.EVT_PAINT, self.OnWindowPaint) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnWindowEraseBk) + self.Bind(wx.EVT_TIMER, self.OnTimer) + + self.timer = wx.Timer(self, wx.ID_ANY) + self.timerSleep = None + self.timerSleepId = wx.NewId() + self.direction = 1 + self.padding = 15 + self.transp = 0 + + hfont = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + self.SetFont(hfont) + + tx, ty = self.GetTextExtent(self.title) + tx += self.padding * 2 + + if bitmap.GetWidth() < tx: + width = tx + else: + width = bitmap.GetWidth() + + self.SetSize((width, bitmap.GetHeight() + 16)) + + self.SetTransparent(0) + self.Refresh() + + def OnTimer(self, event): + self.transp += 20 * self.direction + + if self.transp > 220: + self.transp = 220 + self.timer.Stop() + + if self.transp < 0: + self.transp = 0 + self.timer.Stop() + wx.Frame.Show(self, False) + self.Destroy() + return + self.SetTransparent(self.transp) + + def RaiseParent(self): + wnd = self + lastwnd = None + while wnd is not None: + lastwnd = wnd + wnd = wnd.Parent + if lastwnd: + lastwnd.Raise() + + def Show(self, showWnd=True): + if showWnd: + wx.Frame.Show(self, showWnd) + self.RaiseParent() + self.direction = 1 + self.timer.Start(10) + else: + self.direction = -1 + self.timer.Start(10) + + def OnWindowEraseBk(self, event): + pass + + def OnWindowPaint(self, event): + rect = self.GetRect() + canvas = wx.Bitmap(rect.width, rect.height) + mdc = wx.BufferedPaintDC(self) + mdc.SelectObject(canvas) + color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) + mdc.SetBackground(wx.Brush(color)) + mdc.Clear() + + font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + mdc.SetFont(font) + + x, y = mdc.GetTextExtent(self.title) + + mdc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))) + mdc.DrawRectangle(0, 0, rect.width, 16) + + mdc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)) + + mdc.DrawBitmap(self.bitmap, 0, 16) + + mdc.SetPen(wx.Pen("#000000", width=1)) + mdc.SetBrush(wx.TRANSPARENT_BRUSH) + + mdc.DrawRectangle(0, 16, rect.width, rect.height - 16) + + +if __name__ == "__main__": + + # need to set up some paths, since bitmap loader requires config to have things + # Should probably change that so that it's not dependant on config + import os + os.chdir('..') + import config + config.defPaths(None) + + class Frame(wx.Frame): + def __init__(self, title): + super().__init__(None, title=title, size=(1000, 500)) + + if 'wxMSW' in wx.PlatformInfo: + color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) + self.SetBackgroundColour(color) + + main_sizer = wx.BoxSizer(wx.HORIZONTAL) + splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) + main_sizer.Add(splitter, 1, wx.EXPAND | wx.ALL, 2) + + # Main test notebook + self.notebook = ChromeNotebook(splitter) + + # Tests can_add, has dummy tabs + notebook2 = ChromeNotebook(splitter, can_add=False) + + self.statusbar = self.CreateStatusBar() + + panel = wx.Panel(self) + box = wx.BoxSizer(wx.VERTICAL) + + head = wx.StaticText(panel, -1, "Chome Tabs Test") + head.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD)) + box.Add(head, 0, wx.ALL, 10) + + self.tctrl = wx.TextCtrl(panel, wx.ID_ANY, "Tab Name") + + self.close_check = wx.CheckBox(panel, label="Closable?") + self.close_check.SetValue(True) + + self.icon_check = wx.CheckBox(panel, label="Icon?") + self.icon_check.SetValue(True) + + button = wx.Button(panel, wx.ID_ANY, "Create") + button.Bind(wx.EVT_BUTTON, self.OnCreate) + + box.Add(self.tctrl, 0, wx.ALL, 5) + box.Add(self.close_check, 0, wx.ALL, 5) + box.Add(self.icon_check, 0, wx.ALL, 5) + box.Add(button, 0, wx.ALL, 10) + + self.notebook.AddPage(panel, "Tab1", closeable=False) + + # Add dummy pages + notebook2.AddPage() + notebook2.AddPage() + + splitter.SplitVertically(self.notebook, notebook2) + + panel.SetSizer(box) + panel.Layout() + self.SetSizer(main_sizer) + + def OnCreate(self, event): + tab_name = self.tctrl.GetValue() + tab_icon = BitmapLoader.getImage("ship_small", "gui") + self.notebook.AddPage( + title=tab_name, + image=tab_icon if self.icon_check.GetValue() else None, + closeable=self.close_check.GetValue()) + + app = wx.App(redirect=False) # Error messages go to popup window + top = Frame("Test Chrome Tabs") + top.Show() + app.MainLoop() + diff --git a/gui/contextMenu.py b/gui/contextMenu.py index 043140db3..11d574157 100644 --- a/gui/contextMenu.py +++ b/gui/contextMenu.py @@ -72,7 +72,7 @@ class ContextMenu(object): display_amount += 1 texts = m.getText(itemContext, selection) - if isinstance(texts, basestring): + if isinstance(texts, str): texts = (texts,) bitmap = m.getBitmap(srcContext, selection) @@ -112,7 +112,7 @@ class ContextMenu(object): else: rootItem.SetBitmap(bitmap) - rootMenu.AppendItem(rootItem) + rootMenu.Append(rootItem) empty = False diff --git a/gui/copySelectDialog.py b/gui/copySelectDialog.py index 57ce48f47..5675c835e 100644 --- a/gui/copySelectDialog.py +++ b/gui/copySelectDialog.py @@ -27,25 +27,25 @@ class CopySelectDialog(wx.Dialog): copyFormatEftImps = 1 copyFormatXml = 2 copyFormatDna = 3 - copyFormatCrest = 4 + copyFormatEsi = 4 copyFormatMultiBuy = 5 def __init__(self, parent): - wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Select a format", size=(-1, -1), + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Select a format", size=(-1, -1), style=wx.DEFAULT_DIALOG_STYLE) mainSizer = wx.BoxSizer(wx.VERTICAL) - copyFormats = [u"EFT", u"EFT (Implants)", u"XML", u"DNA", u"CREST", u"MultiBuy"] - copyFormatTooltips = {CopySelectDialog.copyFormatEft: u"EFT text format", - CopySelectDialog.copyFormatEftImps: u"EFT text format", - CopySelectDialog.copyFormatXml: u"EVE native XML format", - CopySelectDialog.copyFormatDna: u"A one-line text format", - CopySelectDialog.copyFormatCrest: u"A JSON format used for EVE CREST", - CopySelectDialog.copyFormatMultiBuy: u"MultiBuy text format"} - selector = wx.RadioBox(self, wx.ID_ANY, label=u"Copy to the clipboard using:", choices=copyFormats, + copyFormats = ["EFT", "EFT (Implants)", "XML", "DNA", "CREST", "MultiBuy"] + copyFormatTooltips = {CopySelectDialog.copyFormatEft: "EFT text format", + CopySelectDialog.copyFormatEftImps: "EFT text format", + CopySelectDialog.copyFormatXml: "EVE native XML format", + CopySelectDialog.copyFormatDna: "A one-line text format", + CopySelectDialog.copyFormatEsi: "A JSON format used for EVE CREST", + CopySelectDialog.copyFormatMultiBuy: "MultiBuy text format"} + selector = wx.RadioBox(self, wx.ID_ANY, label="Copy to the clipboard using:", choices=copyFormats, style=wx.RA_SPECIFY_ROWS) selector.Bind(wx.EVT_RADIOBOX, self.Selected) - for format, tooltip in copyFormatTooltips.iteritems(): + for format, tooltip in copyFormatTooltips.items(): selector.SetItemToolTip(format, tooltip) self.copyFormat = CopySelectDialog.copyFormatEft diff --git a/gui/devTools.py b/gui/devTools.py new file mode 100644 index 000000000..98a14662c --- /dev/null +++ b/gui/devTools.py @@ -0,0 +1,114 @@ +# ============================================================================= +# Copyright (C) 2010 Diego Duclos +# +# This file is part of pyfa. +# +# pyfa is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# pyfa is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyfa. If not, see . +# ============================================================================= + +# noinspection PyPackageRequirements +import wx +from logbook import Logger +import gc +import eos +import time +import threading +from gui.builtinShipBrowser.events import FitSelected + + +pyfalog = Logger(__name__) + + +class DevTools(wx.Dialog): + DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") + + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Damage Pattern Editor", size=wx.Size(400, 240)) + + self.block = False + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) + + mainSizer = wx.BoxSizer(wx.VERTICAL) + + self.id_get = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition) + mainSizer.Add(self.id_get, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + self.idBtn = wx.Button(self, wx.ID_ANY, "Print object", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.idBtn, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + self.idBtn.Bind(wx.EVT_BUTTON, self.objects_by_id) + + self.gcCollect = wx.Button(self, wx.ID_ANY, "GC Collect", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.gcCollect, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + self.gcCollect.Bind(wx.EVT_BUTTON, self.gc_collect) + + self.fitTest = wx.Button(self, wx.ID_ANY, "Test fits", wx.DefaultPosition, wx.DefaultSize, 0) + mainSizer.Add(self.fitTest, 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5) + + self.fitTest .Bind(wx.EVT_BUTTON, self.fit_test) + + self.SetSizer(mainSizer) + + self.Layout() + self.CenterOnParent() + self.Show() + + def objects_by_id(self, evt): + input = self.id_get.GetValue() + if input.startswith("0x"): + input = int(input, 16) + + print("Finding {} ({})".format(str(input), hex(input))) + + for obj in gc.get_objects(): + if id(obj) == input: + print(obj) + print(bool(obj)) + print(str(len(gc.get_referents(obj))) + " references") + + break + else: + print(None) + + def gc_collect(self, evt): + print(gc.collect()) + print(gc.get_debug()) + print(gc.get_stats()) + + def fit_test(self, evt): + fits = eos.db.getFitList() + self.thread = FitTestThread([x.ID for x in fits], self.Parent) + self.thread.start() + + +class FitTestThread(threading.Thread): + def __init__(self, fitIDs, mainFrame): + threading.Thread.__init__(self) + self.name = "FitTestThread" + self.mainFrame = mainFrame + self.stopRunning = False + self.fits = fitIDs + + def stop(self): + self.stopRunning = True + + def run(self): + # wait 1 second just in case a lot of modifications get made + if self.stopRunning: + return + + for fit in self.fits: + time.sleep(1) + e = FitSelected(fitID=fit) + wx.PostEvent(self.mainFrame, e) diff --git a/gui/display.py b/gui/display.py index 2ae83aa44..8ee42692e 100644 --- a/gui/display.py +++ b/gui/display.py @@ -17,7 +17,6 @@ # along with pyfa. If not, see . # ============================================================================= -import sys # noinspection PyPackageRequirements import wx import gui.mainFrame @@ -38,9 +37,6 @@ class Display(wx.ListCtrl): self.Bind(wx.EVT_LIST_COL_END_DRAG, self.resizeChecker) self.Bind(wx.EVT_LIST_COL_BEGIN_DRAG, self.resizeSkip) - if "wxMSW" in wx.PlatformInfo: - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBk) - self.mainFrame = gui.mainFrame.MainFrame.getInstance() i = 0 @@ -55,7 +51,7 @@ class Display(wx.ListCtrl): name, type, defaultValue = param value = params[x] if len(params) > x else defaultValue value = value if value != "" else defaultValue - if type == bool and isinstance(value, basestring): + if type == bool and isinstance(value, str): value = bool(value) if value.lower() != "false" and value != "0" else False paramDict[name] = value col = colClass(self, paramDict) @@ -69,7 +65,7 @@ class Display(wx.ListCtrl): info = wx.ListItem() # noinspection PyPropertyAccess info.m_mask = wx.LIST_MASK_WIDTH - self.InsertColumnInfo(i, info) + self.InsertColumn(i, info) self.SetColumnWidth(i, 0) self.imageListBase = self.imageList.ImageCount @@ -113,50 +109,16 @@ class Display(wx.ListCtrl): return rowIndex, 0, -1 - def OnEraseBk(self, event): - if self.GetItemCount() > 0: - width, height = self.GetClientSize() - dc = event.GetDC() - - dc.DestroyClippingRegion() - dc.SetClippingRegion(0, 0, width, height) - x, y, w, h = dc.GetClippingBox() - - topItem = self.GetTopItem() - bottomItem = topItem + self.GetCountPerPage() - - if bottomItem >= self.GetItemCount(): - bottomItem = self.GetItemCount() - 1 - - topRect = self.GetItemRect(topItem, wx.LIST_RECT_LABEL) - bottomRect = self.GetItemRect(bottomItem, wx.LIST_RECT_BOUNDS) - - items_rect = wx.Rect(topRect.left, 0, bottomRect.right - topRect.left, bottomRect.bottom) - - updateRegion = wx.Region(x, y, w, h) - updateRegion.SubtractRect(items_rect) - - dc.DestroyClippingRegion() - dc.SetClippingRegionAsRegion(updateRegion) - - dc.SetBackground(wx.Brush(self.GetBackgroundColour(), wx.SOLID)) - dc.Clear() - - dc.DestroyClippingRegion() - - else: - event.Skip() - # noinspection PyPropertyAccess def addColumn(self, i, col): self.activeColumns.append(col) info = wx.ListItem() - info.m_mask = col.mask | wx.LIST_MASK_FORMAT | wx.LIST_MASK_WIDTH - info.m_image = col.imageId - info.m_text = col.columnText - info.m_width = -1 - info.m_format = wx.LIST_FORMAT_LEFT - self.InsertColumnInfo(i, info) + info.SetMask(col.mask | wx.LIST_MASK_FORMAT | wx.LIST_MASK_WIDTH) + info.SetImage(col.imageId) + info.SetText(col.columnText) + info.SetWidth(-1) + info.SetAlign(wx.LIST_FORMAT_LEFT) + self.InsertColumn(i, info) col.resized = False if i == 0 and col.size != wx.LIST_AUTOSIZE_USEHEADER: col.size += 4 @@ -219,13 +181,13 @@ class Display(wx.ListCtrl): if listItemCount < stuffItemCount: for i in range(stuffItemCount - listItemCount): - self.InsertStringItem(sys.maxint, "") + self.InsertItem(self.GetItemCount(), "") if listItemCount > stuffItemCount: if listItemCount - stuffItemCount > 20 > stuffItemCount: self.DeleteAllItems() for i in range(stuffItemCount): - self.InsertStringItem(sys.maxint, "") + self.InsertItem(self.GetItemCount(), "") else: for i in range(listItemCount - stuffItemCount): self.DeleteItem(self.getLastItem()) @@ -247,7 +209,7 @@ class Display(wx.ListCtrl): newText = col.getText(st) if newText is False: col.delayedText(st, self, colItem) - newText = u"\u21bb" + newText = "\u21bb" newImageId = col.getImageId(st) diff --git a/gui/errorDialog.py b/gui/errorDialog.py index 378ddddd2..706e3c5a3 100644 --- a/gui/errorDialog.py +++ b/gui/errorDialog.py @@ -17,42 +17,57 @@ # along with pyfa. If not, see . # =============================================================================== -import platform +# import platform import sys - +# # noinspection PyPackageRequirements import wx +import traceback +import config +from logbook import Logger +from service.prereqsCheck import version_block -try: - import config -except: - config = None +pyfalog = Logger(__name__) -try: - import sqlalchemy - sqlalchemy_version = sqlalchemy.__version__ -except: - sqlalchemy_version = "Unknown" +class ErrorHandler(object): + __parent = None + __frame = None -try: - from logbook import __version__ as logbook_version -except: - logbook_version = "Unknown" + @classmethod + def HandleException(cls, exc_type, exc_value, exc_traceback): + with config.logging_setup.threadbound(): + # Print the base level traceback + t = traceback.format_exception(exc_type, exc_value, exc_traceback) + pyfalog.critical("\n\n" + "".join(t)) + + if cls.__parent is None: + app = wx.App(False) + cls.__frame = ErrorFrame(None) + cls.__frame.addException("".join(t)) + app.MainLoop() + sys.exit() + else: + if not cls.__frame: + cls.__frame = ErrorFrame(cls.__parent) + cls.__frame.Show() + cls.__frame.addException("".join(t)) + + @classmethod + def SetParent(cls, parent): + cls.__parent = parent class ErrorFrame(wx.Frame): - def __init__(self, exception=None, tb=None, error_title='Error!'): - v = sys.version_info - - wx.Frame.__init__(self, None, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600), + def __init__(self, parent=None, error_title='Error!'): + wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="pyfa error", pos=wx.DefaultPosition, size=wx.Size(500, 600), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER | wx.STAY_ON_TOP) desc = "pyfa has experienced an unexpected issue. Below is a message that contains crucial\n" \ "information about how this was triggered. Please contact the developers with the\n" \ "information provided through the EVE Online forums or file a GitHub issue." - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) if 'wxMSW' in wx.PlatformInfo: self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) @@ -74,69 +89,32 @@ class ErrorFrame(wx.Frame): descText = wx.StaticText(self, wx.ID_ANY, desc) box.Add(descText, 1, wx.ALL, 5) - github = wx.HyperlinkCtrl(self, wx.ID_ANY, "Github", "https://github.com/pyfa-org/Pyfa/issues", - wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE) - box.Add(github, 0, wx.ALL, 5) - - eveForums = wx.HyperlinkCtrl(self, wx.ID_ANY, "EVE Forums", "https://forums.eveonline.com/t/27156", - wx.DefaultPosition, wx.DefaultSize, wx.HL_DEFAULT_STYLE) - box.Add(eveForums, 0, wx.ALL, 5) + # github = wx.lib.agw.hyperlink.HyperLinkCtrl(self, wx.ID_ANY, label="Github", URL="https://github.com/pyfa-org/Pyfa/issues") + # box.Add(github, 0, wx.ALL, 5) + # + # eveForums = wx.lib.agw.hyperlink.HyperLinkCtrl(self, wx.ID_ANY, label="EVE Forums", URL="https://forums.eveonline.com/t/27156") + # box.Add(eveForums, 0, wx.ALL, 5) # mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) - errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, (-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP) - errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) - mainSizer.Add(errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5) - - try: - errorTextCtrl.AppendText("OS version: \t" + str(platform.platform())) - except: - errorTextCtrl.AppendText("OS version: Unknown") - errorTextCtrl.AppendText("\n") - - try: - errorTextCtrl.AppendText("Python: \t" + '{}.{}.{}'.format(v.major, v.minor, v.micro)) - except: - errorTextCtrl.AppendText("Python: Unknown") - errorTextCtrl.AppendText("\n") - - try: - errorTextCtrl.AppendText("wxPython: \t" + wx.VERSION_STRING) - except: - errorTextCtrl.AppendText("wxPython: Unknown") - errorTextCtrl.AppendText("\n") - - errorTextCtrl.AppendText("SQLAlchemy: \t" + str(sqlalchemy_version)) - errorTextCtrl.AppendText("\n") - - errorTextCtrl.AppendText("Logbook: \t" + str(logbook_version)) - errorTextCtrl.AppendText("\n") - - try: - errorTextCtrl.AppendText("pyfa version: {0} {1} - {2} {3}".format(config.version, config.tag, config.expansionName, config.expansionVersion)) - except: - errorTextCtrl.AppendText("pyfa version: Unknown") - errorTextCtrl.AppendText('\n') - - errorTextCtrl.AppendText("pyfa root: " + str(config.pyfaPath or "Unknown")) - errorTextCtrl.AppendText('\n') - errorTextCtrl.AppendText("save path: " + str(config.savePath or "Unknown")) - errorTextCtrl.AppendText('\n') - errorTextCtrl.AppendText("fs encoding: " + str(sys.getfilesystemencoding() or "Unknown")) - errorTextCtrl.AppendText('\n\n') - - errorTextCtrl.AppendText("EXCEPTION: " + str(exception or "Unknown")) - errorTextCtrl.AppendText('\n\n') - - if tb: - for line in tb: - errorTextCtrl.AppendText(line) - errorTextCtrl.Layout() + self.errorTextCtrl = wx.TextCtrl(self, wx.ID_ANY, version_block.strip(), wx.DefaultPosition, + (-1, 400), wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2 | wx.TE_DONTWRAP) + self.errorTextCtrl.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) + mainSizer.Add(self.errorTextCtrl, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, 5) + self.errorTextCtrl.AppendText("\n") + self.errorTextCtrl.Layout() self.SetSizer(mainSizer) mainSizer.Layout() self.Layout() self.Centre(wx.BOTH) + self.Bind(wx.EVT_CLOSE, self.OnClose) self.Show() + + def OnClose(self, evt): + self.Hide() + + def addException(self, text): + self.errorTextCtrl.AppendText("\n{}\n\n{}".format("#" * 20, text)) diff --git a/gui/crestFittings.py b/gui/esiFittings.py similarity index 62% rename from gui/crestFittings.py rename to gui/esiFittings.py index 30953b120..31fdd5a71 100644 --- a/gui/crestFittings.py +++ b/gui/esiFittings.py @@ -15,13 +15,15 @@ from gui.display import Display import gui.globalEvents as GE from logbook import Logger +import calendar +from service.esi import Esi +from esipy.exceptions import APIException +from service.port import ESIExportException + pyfalog = Logger(__name__) -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - from service.crest import Crest, CrestModes - -class CrestFittings(wx.Frame): +class EveFittings(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Browse EVE Fittings", pos=wx.DefaultPosition, size=wx.Size(550, 450), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL) @@ -30,22 +32,15 @@ class CrestFittings(wx.Frame): self.mainFrame = parent mainSizer = wx.BoxSizer(wx.VERTICAL) - sCrest = Crest.getInstance() + sEsi = Esi.getInstance() characterSelectSizer = wx.BoxSizer(wx.HORIZONTAL) - if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s" % sCrest.implicitCharacter.name, - wx.DefaultPosition, wx.DefaultSize) - self.stLogged.Wrap(-1) + self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) + characterSelectSizer.Add(self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) + self.updateCharList() - characterSelectSizer.Add(self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) - else: - self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) - characterSelectSizer.Add(self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) - self.updateCharList() - - self.fetchBtn = wx.Button(self, wx.ID_ANY, u"Fetch Fits", wx.DefaultPosition, wx.DefaultSize, 5) + self.fetchBtn = wx.Button(self, wx.ID_ANY, "Fetch Fits", wx.DefaultPosition, wx.DefaultSize, 5) characterSelectSizer.Add(self.fetchBtn, 0, wx.ALL, 5) mainSizer.Add(characterSelectSizer, 0, wx.EXPAND, 5) @@ -64,8 +59,8 @@ class CrestFittings(wx.Frame): fitSizer.Add(self.fitView, 1, wx.ALL | wx.EXPAND, 5) btnSizer = wx.BoxSizer(wx.HORIZONTAL) - self.importBtn = wx.Button(self, wx.ID_ANY, u"Import to pyfa", wx.DefaultPosition, wx.DefaultSize, 5) - self.deleteBtn = wx.Button(self, wx.ID_ANY, u"Delete from EVE", wx.DefaultPosition, wx.DefaultSize, 5) + self.importBtn = wx.Button(self, wx.ID_ANY, "Import to pyfa", wx.DefaultPosition, wx.DefaultSize, 5) + self.deleteBtn = wx.Button(self, wx.ID_ANY, "Delete from EVE", wx.DefaultPosition, wx.DefaultSize, 5) btnSizer.Add(self.importBtn, 1, wx.ALL, 5) btnSizer.Add(self.deleteBtn, 1, wx.ALL, 5) fitSizer.Add(btnSizer, 0, wx.EXPAND) @@ -85,9 +80,6 @@ class CrestFittings(wx.Frame): self.statusbar.SetFieldsCount() self.SetStatusBar(self.statusbar) - self.cacheTimer = wx.Timer(self) - self.Bind(wx.EVT_TIMER, self.updateCacheStatus, self.cacheTimer) - self.SetSizer(mainSizer) self.Layout() @@ -98,91 +90,96 @@ class CrestFittings(wx.Frame): event.Skip() def updateCharList(self): - sCrest = Crest.getInstance() - chars = sCrest.getCrestCharacters() + sEsi = Esi.getInstance() + chars = sEsi.getSsoCharacters() if len(chars) == 0: self.Close() self.charChoice.Clear() for char in chars: - self.charChoice.Append(char.name, char.ID) + self.charChoice.Append(char.characterName, char.ID) self.charChoice.SetSelection(0) - def updateCacheStatus(self, event): - t = time.gmtime(self.cacheTime - time.time()) - if t < 0: - self.cacheTimer.Stop() - else: - sTime = time.strftime("%H:%M:%S", t) - self.statusbar.SetStatusText("Cached for %s" % sTime, 0) - def ssoLogout(self, event): - if event.type == CrestModes.IMPLICIT: - self.Close() - else: - self.updateCharList() + self.updateCharList() event.Skip() # continue event def OnClose(self, event): - self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT, handler=self.ssoLogout) - self.mainFrame.Unbind(GE.EVT_SSO_LOGIN, handler=self.ssoLogin) + self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT) + self.mainFrame.Unbind(GE.EVT_SSO_LOGIN) + # self.cacheTimer.Stop() # must be manually stopped, otherwise crash. See https://github.com/wxWidgets/Phoenix/issues/632 event.Skip() def getActiveCharacter(self): - sCrest = Crest.getInstance() - - if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - return sCrest.implicitCharacter.ID - selection = self.charChoice.GetCurrentSelection() return self.charChoice.GetClientData(selection) if selection is not None else None def fetchFittings(self, event): - sCrest = Crest.getInstance() + sEsi = Esi.getInstance() + waitDialog = wx.BusyInfo("Fetching fits, please wait...", parent=self) + try: - waitDialog = wx.BusyInfo("Fetching fits, please wait...", parent=self) - fittings = sCrest.getFittings(self.getActiveCharacter()) - self.cacheTime = fittings.get('cached_until') - self.updateCacheStatus(None) - self.cacheTimer.Start(1000) + fittings = sEsi.getFittings(self.getActiveCharacter()) + # self.cacheTime = fittings.get('cached_until') + # self.updateCacheStatus(None) + # self.cacheTimer.Start(1000) self.fitTree.populateSkillTree(fittings) del waitDialog except requests.exceptions.ConnectionError: msg = "Connection error, please check your internet connection" pyfalog.error(msg) self.statusbar.SetStatusText(msg) + except APIException as ex: + del waitDialog # Can't do this in a finally because then it obscures the message dialog + ESIExceptionHandler(self, ex) + except Exception as ex: + del waitDialog def importFitting(self, event): selection = self.fitView.fitSelection if not selection: return - data = self.fitTree.fittingsTreeCtrl.GetPyData(selection) + data = self.fitTree.fittingsTreeCtrl.GetItemData(selection) sPort = Port.getInstance() fits = sPort.importFitFromBuffer(data) self.mainFrame._openAfterImport(fits) def deleteFitting(self, event): - sCrest = Crest.getInstance() + sEsi = Esi.getInstance() selection = self.fitView.fitSelection if not selection: return - data = json.loads(self.fitTree.fittingsTreeCtrl.GetPyData(selection)) + data = json.loads(self.fitTree.fittingsTreeCtrl.GetItemData(selection)) dlg = wx.MessageDialog(self, - "Do you really want to delete %s (%s) from EVE?" % (data['name'], data['ship']['name']), + "Do you really want to delete %s (%s) from EVE?" % (data['name'], getItem(data['ship_type_id']).name), "Confirm Delete", wx.YES | wx.NO | wx.ICON_QUESTION) if dlg.ShowModal() == wx.ID_YES: try: - sCrest.delFitting(self.getActiveCharacter(), data['fittingID']) + sEsi.delFitting(self.getActiveCharacter(), data['fitting_id']) except requests.exceptions.ConnectionError: msg = "Connection error, please check your internet connection" pyfalog.error(msg) self.statusbar.SetStatusText(msg) +class ESIExceptionHandler(object): + def __init__(self, parentWindow, ex): + if ex.response['error'] == "invalid_token": + dlg = wx.MessageDialog(parentWindow, + "There was an error validating characters' SSO token. Please try " + "logging into the character again to reset the token.", "Invalid Token", + wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + pyfalog.error(ex) + else: + # We don't know how to handle the error, raise it for the global error handler to pick it up + raise ex + + class ExportToEve(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, id=wx.ID_ANY, title="Export fit to EVE", pos=wx.DefaultPosition, @@ -191,23 +188,16 @@ class ExportToEve(wx.Frame): self.mainFrame = parent self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) - sCrest = Crest.getInstance() + sEsi = Esi.getInstance() mainSizer = wx.BoxSizer(wx.VERTICAL) hSizer = wx.BoxSizer(wx.HORIZONTAL) - if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - self.stLogged = wx.StaticText(self, wx.ID_ANY, "Currently logged in as %s" % sCrest.implicitCharacter.name, - wx.DefaultPosition, wx.DefaultSize) - self.stLogged.Wrap(-1) + self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) + hSizer.Add(self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) + self.updateCharList() + self.charChoice.SetSelection(0) - hSizer.Add(self.stLogged, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) - else: - self.charChoice = wx.Choice(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, []) - hSizer.Add(self.charChoice, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5) - self.updateCharList() - self.charChoice.SetSelection(0) - - self.exportBtn = wx.Button(self, wx.ID_ANY, u"Export Fit", wx.DefaultPosition, wx.DefaultSize, 5) + self.exportBtn = wx.Button(self, wx.ID_ANY, "Export Fit", wx.DefaultPosition, wx.DefaultSize, 5) hSizer.Add(self.exportBtn, 0, wx.ALL, 5) mainSizer.Add(hSizer, 0, wx.EXPAND, 5) @@ -222,22 +212,22 @@ class ExportToEve(wx.Frame): self.mainFrame.Bind(GE.EVT_SSO_LOGIN, self.ssoLogin) self.Bind(wx.EVT_CLOSE, self.OnClose) - self.SetSizer(hSizer) + self.SetSizer(mainSizer) self.SetStatusBar(self.statusbar) self.Layout() self.Centre(wx.BOTH) def updateCharList(self): - sCrest = Crest.getInstance() - chars = sCrest.getCrestCharacters() + sEsi = Esi.getInstance() + chars = sEsi.getSsoCharacters() if len(chars) == 0: self.Close() self.charChoice.Clear() for char in chars: - self.charChoice.Append(char.name, char.ID) + self.charChoice.Append(char.characterName, char.ID) self.charChoice.SetSelection(0) @@ -246,24 +236,16 @@ class ExportToEve(wx.Frame): event.Skip() def ssoLogout(self, event): - if event.type == CrestModes.IMPLICIT: - self.Close() - else: - self.updateCharList() + self.updateCharList() event.Skip() # continue event def OnClose(self, event): - self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT, handler=self.ssoLogout) - self.mainFrame.Unbind(GE.EVT_SSO_LOGIN, handler=self.ssoLogin) + self.mainFrame.Unbind(GE.EVT_SSO_LOGOUT) + self.mainFrame.Unbind(GE.EVT_SSO_LOGIN) event.Skip() def getActiveCharacter(self): - sCrest = Crest.getInstance() - - if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - return sCrest.implicitCharacter.ID - selection = self.charChoice.GetCurrentSelection() return self.charChoice.GetClientData(selection) if selection is not None else None @@ -278,29 +260,36 @@ class ExportToEve(wx.Frame): return self.statusbar.SetStatusText("Sending request and awaiting response", 1) - sCrest = Crest.getInstance() + sEsi = Esi.getInstance() try: sFit = Fit.getInstance() - data = sPort.exportCrest(sFit.getFit(fitID)) - res = sCrest.postFitting(self.getActiveCharacter(), data) + data = sPort.exportESI(sFit.getFit(fitID)) + res = sEsi.postFitting(self.getActiveCharacter(), data) - self.statusbar.SetStatusText("%d: %s" % (res.status_code, res.reason), 0) - try: - text = json.loads(res.text) - self.statusbar.SetStatusText(text['message'], 1) - except ValueError: - pyfalog.warning("Value error on loading JSON.") - self.statusbar.SetStatusText("", 1) + self.statusbar.SetStatusText("", 0) + self.statusbar.SetStatusText("", 1) + # try: + # text = json.loads(res.text) + # self.statusbar.SetStatusText(text['message'], 1) + # except ValueError: + # pyfalog.warning("Value error on loading JSON.") + # self.statusbar.SetStatusText("", 1) except requests.exceptions.ConnectionError: msg = "Connection error, please check your internet connection" pyfalog.error(msg) self.statusbar.SetStatusText(msg) + except ESIExportException as ex: + pyfalog.error(ex) + self.statusbar.SetStatusText("ERROR", 0) + self.statusbar.SetStatusText(ex.args[0], 1) + except APIException as ex: + ESIExceptionHandler(self, ex) -class CrestMgmt(wx.Dialog): +class SsoCharacterMgmt(wx.Dialog): def __init__(self, parent): - wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="CREST Character Management", pos=wx.DefaultPosition, + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="SSO Character Management", pos=wx.DefaultPosition, size=wx.Size(550, 250), style=wx.DEFAULT_DIALOG_STYLE) self.mainFrame = parent mainSizer = wx.BoxSizer(wx.HORIZONTAL) @@ -308,7 +297,7 @@ class CrestMgmt(wx.Dialog): self.lcCharacters = wx.ListCtrl(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LC_REPORT) self.lcCharacters.InsertColumn(0, heading='Character') - self.lcCharacters.InsertColumn(1, heading='Refresh Token') + self.lcCharacters.InsertColumn(1, heading='Character ID') self.popCharList() @@ -316,10 +305,10 @@ class CrestMgmt(wx.Dialog): btnSizer = wx.BoxSizer(wx.VERTICAL) - self.addBtn = wx.Button(self, wx.ID_ANY, u"Add Character", wx.DefaultPosition, wx.DefaultSize, 0) + self.addBtn = wx.Button(self, wx.ID_ANY, "Add Character", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.addBtn, 0, wx.ALL | wx.EXPAND, 5) - self.deleteBtn = wx.Button(self, wx.ID_ANY, u"Revoke Character", wx.DefaultPosition, wx.DefaultSize, 0) + self.deleteBtn = wx.Button(self, wx.ID_ANY, "Revoke Character", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.deleteBtn, 0, wx.ALL | wx.EXPAND, 5) mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) @@ -339,14 +328,14 @@ class CrestMgmt(wx.Dialog): event.Skip() def popCharList(self): - sCrest = Crest.getInstance() - chars = sCrest.getCrestCharacters() + sEsi = Esi.getInstance() + chars = sEsi.getSsoCharacters() self.lcCharacters.DeleteAllItems() for index, char in enumerate(chars): - self.lcCharacters.InsertStringItem(index, char.name) - self.lcCharacters.SetStringItem(index, 1, char.refresh_token) + self.lcCharacters.InsertItem(index, char.characterName) + self.lcCharacters.SetItem(index, 1, str(char.characterID)) self.lcCharacters.SetItemData(index, char.ID) self.lcCharacters.SetColumnWidth(0, wx.LIST_AUTOSIZE) @@ -354,16 +343,15 @@ class CrestMgmt(wx.Dialog): @staticmethod def addChar(event): - sCrest = Crest.getInstance() - uri = sCrest.startServer() - webbrowser.open(uri) + sEsi = Esi.getInstance() + sEsi.login() def delChar(self, event): item = self.lcCharacters.GetFirstSelected() if item > -1: charID = self.lcCharacters.GetItemData(item) - sCrest = Crest.getInstance() - sCrest.delCrestCharacter(charID) + sEsi = Esi.getInstance() + sEsi.delSsoCharacter(charID) self.popCharList() @@ -379,7 +367,7 @@ class FittingsTreeView(wx.Panel): self.root = tree.AddRoot("Fits") self.populateSkillTree(None) - self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.displayFit) + self.Bind(wx.EVT_TREE_SEL_CHANGED, self.displayFit) self.SetSizer(pmainSizer) @@ -393,23 +381,24 @@ class FittingsTreeView(wx.Panel): tree.DeleteChildren(root) dict = {} - fits = data['items'] + fits = data for fit in fits: - if fit['ship']['name'] not in dict: - dict[fit['ship']['name']] = [] - dict[fit['ship']['name']].append(fit) + ship = getItem(fit['ship_type_id']) + if ship.name not in dict: + dict[ship.name] = [] + dict[ship.name].append(fit) - for name, fits in dict.iteritems(): + for name, fits in dict.items(): shipID = tree.AppendItem(root, name) for fit in fits: fitId = tree.AppendItem(shipID, fit['name']) - tree.SetPyData(fitId, json.dumps(fit)) + tree.SetItemData(fitId, json.dumps(fit)) tree.SortChildren(root) def displayFit(self, event): selection = self.fittingsTreeCtrl.GetSelection() - data = self.fittingsTreeCtrl.GetPyData(selection) + data = self.fittingsTreeCtrl.GetItemData(selection) if data is None: event.Skip() @@ -420,7 +409,7 @@ class FittingsTreeView(wx.Panel): for item in fit['items']: try: - cargo = Cargo(getItem(item['type']['id'])) + cargo = Cargo(getItem(item['type_id'])) cargo.amount = item['quantity'] list.append(cargo) except Exception as e: diff --git a/gui/globalEvents.py b/gui/globalEvents.py index 1c1cc7e38..53784fcb6 100644 --- a/gui/globalEvents.py +++ b/gui/globalEvents.py @@ -5,5 +5,6 @@ FitChanged, FIT_CHANGED = wx.lib.newevent.NewEvent() CharListUpdated, CHAR_LIST_UPDATED = wx.lib.newevent.NewEvent() CharChanged, CHAR_CHANGED = wx.lib.newevent.NewEvent() +SsoLoggingIn, EVT_SSO_LOGGING_IN = wx.lib.newevent.NewEvent() SsoLogin, EVT_SSO_LOGIN = wx.lib.newevent.NewEvent() SsoLogout, EVT_SSO_LOGOUT = wx.lib.newevent.NewEvent() diff --git a/gui/graphFrame.py b/gui/graphFrame.py index 9c58a32b6..a36c9c66a 100644 --- a/gui/graphFrame.py +++ b/gui/graphFrame.py @@ -28,7 +28,7 @@ import gui.display import gui.mainFrame import gui.globalEvents as GE from gui.graph import Graph -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader import traceback pyfalog = Logger(__name__) @@ -93,15 +93,15 @@ class GraphFrame(wx.Frame): graphFrame_enabled = True if int(mpl.__version__[0]) < 1: - print("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds") + print(("pyfa: Found matplotlib version ", mpl.__version__, " - activating OVER9000 workarounds")) print("pyfa: Recommended minimum matplotlib version is 1.0.0") self.legendFix = True mplImported = True - wx.Frame.__init__(self, parent, title=u"pyfa: Graph Generator", style=style, size=(520, 390)) + wx.Frame.__init__(self, parent, title="pyfa: Graph Generator", style=style, size=(520, 390)) - i = wx.IconFromBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) + i = wx.Icon(BitmapLoader.getBitmap("graphs_small", "gui")) self.SetIcon(i) self.mainFrame = gui.mainFrame.MainFrame.getInstance() self.CreateStatusBar() @@ -143,7 +143,7 @@ class GraphFrame(wx.Frame): dummyBox = wx.BoxSizer(wx.VERTICAL) self.gridPanel.SetSizer(dummyBox) - self.gridSizer = wx.FlexGridSizer(0, 4) + self.gridSizer = wx.FlexGridSizer(0, 4, 0, 0) self.gridSizer.AddGrowableCol(1) dummyBox.Add(self.gridSizer, 0, wx.EXPAND) @@ -179,7 +179,7 @@ class GraphFrame(wx.Frame): def getValues(self): values = {} - for fieldName, field in self.fields.iteritems(): + for fieldName, field in self.fields.items(): values[fieldName] = field.GetValue() return values @@ -193,14 +193,14 @@ class GraphFrame(wx.Frame): self.fields.clear() # Setup textboxes - for field, defaultVal in view.getFields().iteritems(): + for field, defaultVal in view.getFields().items(): textBox = wx.TextCtrl(self.gridPanel, wx.ID_ANY, style=0) self.fields[field] = textBox textBox.Bind(wx.EVT_TEXT, self.onFieldChanged) sizer.Add(textBox, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3) if defaultVal is not None: - if not isinstance(defaultVal, basestring): + if not isinstance(defaultVal, str): defaultVal = ("%f" % defaultVal).rstrip("0") if defaultVal[-1:] == ".": defaultVal += "0" @@ -229,6 +229,15 @@ class GraphFrame(wx.Frame): def draw(self, event=None): global mpl_version + if event is not None: + event.Skip() + + # todo: FIX THIS, see #1430. draw() is not being unbound properly when the window closes, this is an easy fix, + # but not a proper solution + if not self: + pyfalog.warning("GraphFrame handled event, however GraphFrame no longer exists. Ignoring event") + return + values = self.getValues() view = self.getView() self.subplot.clear() @@ -247,9 +256,9 @@ class GraphFrame(wx.Frame): self.subplot.plot(x, y) legend.append(fit.name) - except: - pyfalog.warning(u"Invalid values in '{0}'", fit.name) - self.SetStatusText(u"Invalid values in '%s'" % fit.name) + except Exception as ex: + pyfalog.warning("Invalid values in '{0}'", fit.name) + self.SetStatusText("Invalid values in '%s'" % fit.name) self.canvas.draw() return @@ -299,8 +308,6 @@ class GraphFrame(wx.Frame): self.canvas.draw() self.SetStatusText("") - if event is not None: - event.Skip() def onFieldChanged(self, event): self.draw() diff --git a/gui/itemStats.py b/gui/itemStats.py index dec1bc12c..3440fb322 100644 --- a/gui/itemStats.py +++ b/gui/itemStats.py @@ -23,7 +23,7 @@ import wx import config from service.market import Market import gui.mainFrame -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.builtinItemStatsViews.itemTraits import ItemTraits from gui.builtinItemStatsViews.itemDescription import ItemDescription @@ -84,7 +84,7 @@ class ItemStatsDialog(wx.Dialog): iconFile = "%s%s%s" % (before, sep, "0%s" % after if len(after) < 2 else after) itemImg = BitmapLoader.getBitmap(iconFile, "icons") if itemImg is not None: - self.SetIcon(wx.IconFromBitmap(itemImg)) + self.SetIcon(wx.Icon(itemImg)) self.SetTitle("%s: %s%s" % ("%s Stats" % itmContext if itmContext is not None else "Stats", item.name, " (%d)" % item.ID if config.debug else "")) @@ -99,7 +99,7 @@ class ItemStatsDialog(wx.Dialog): self.mainSizer.Add(self.container, 1, wx.EXPAND) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + self.closeBtn = wx.Button(self, wx.ID_ANY, "Close", wx.DefaultPosition, wx.DefaultSize, 0) self.mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) diff --git a/gui/mainFrame.py b/gui/mainFrame.py index d4b23f3a4..c9e5fa09c 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -25,8 +25,6 @@ import sqlalchemy # noinspection PyPackageRequirements import wx # noinspection PyPackageRequirements -from wx._core import PyDeadObjectError -# noinspection PyPackageRequirements from wx.lib.wordwrap import wordwrap # noinspection PyPackageRequirements from wx.lib.inspection import InspectionTool @@ -39,10 +37,10 @@ import config from eos.config import gamedata_version import gui.aboutData -from gui.chromeTabs import PFNotebook +from gui.chrome_tabs import ChromeNotebook import gui.globalEvents as GE -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.mainMenuBar import MainMenuBar from gui.additionsPane import AdditionsPane from gui.marketBrowser import MarketBrowser @@ -56,8 +54,10 @@ from gui.characterSelection import CharacterSelection from gui.patternEditor import DmgPatternEditorDlg from gui.resistsEditor import ResistsEditorDlg from gui.setEditor import ImplantSetEditorDlg +from gui.devTools import DevTools from gui.preferenceDialog import PreferenceDialog from gui.graphFrame import GraphFrame +from gui.ssoLogin import SsoLogin from gui.copySelectDialog import CopySelectDialog from gui.utils.clipboard import toClipboard, fromClipboard from gui.updateDialog import UpdateDialog @@ -81,11 +81,10 @@ from time import gmtime, strftime import threading import webbrowser +import wx.adv -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - from service.crest import Crest - from service.crest import CrestModes - from gui.crestFittings import CrestFittings, ExportToEve, CrestMgmt +from service.esi import Esi, LoginMethod +from gui.esiFittings import EveFittings, ExportToEve, SsoCharacterMgmt disableOverrideEditor = False @@ -93,7 +92,7 @@ try: from gui.propertyEditor import AttributeEditor except ImportError as e: AttributeEditor = None - print("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message) + print(("Error loading Attribute Editor: %s.\nAccess to Attribute Editor is disabled." % e.message)) disableOverrideEditor = True pyfalog = Logger(__name__) @@ -139,7 +138,8 @@ class OpenFitsThread(threading.Thread): wx.CallAfter(self.callback) -class MainFrame(wx.Frame, IPortUser): +# todo: include IPortUser again +class MainFrame(wx.Frame): __instance = None @classmethod @@ -163,7 +163,7 @@ class MainFrame(wx.Frame, IPortUser): self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE)) # Load and set the icon for pyfa main window - i = wx.IconFromBitmap(BitmapLoader.getBitmap("pyfa", "gui")) + i = wx.Icon(BitmapLoader.getBitmap("pyfa", "gui")) self.SetIcon(i) # Create the layout and windows @@ -177,17 +177,17 @@ class MainFrame(wx.Frame, IPortUser): self.fitMultiSwitch = MultiSwitch(self.fitting_additions_split) self.additionsPane = AdditionsPane(self.fitting_additions_split) - self.notebookBrowsers = PFNotebook(self.browser_fitting_split, False) + self.notebookBrowsers = ChromeNotebook(self.browser_fitting_split, False) marketImg = BitmapLoader.getImage("market_small", "gui") shipBrowserImg = BitmapLoader.getImage("ship_small", "gui") self.marketBrowser = MarketBrowser(self.notebookBrowsers) - self.notebookBrowsers.AddPage(self.marketBrowser, "Market", tabImage=marketImg, showClose=False) + self.notebookBrowsers.AddPage(self.marketBrowser, "Market", image=marketImg, closeable=False) self.marketBrowser.splitter.SetSashPosition(self.marketHeight) self.shipBrowser = ShipBrowser(self.notebookBrowsers) - self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", tabImage=shipBrowserImg, showClose=False) + self.notebookBrowsers.AddPage(self.shipBrowser, "Fittings", image=shipBrowserImg, closeable=False) self.notebookBrowsers.SetSelection(1) @@ -205,6 +205,7 @@ class MainFrame(wx.Frame, IPortUser): self.charSelection = CharacterSelection(self) cstatsSizer.Add(self.charSelection, 0, wx.EXPAND) + # @todo pheonix: fix all stats stuff self.statsPane = StatsPane(self) cstatsSizer.Add(self.statsPane, 0, wx.EXPAND) @@ -236,15 +237,19 @@ class MainFrame(wx.Frame, IPortUser): self.sUpdate = Update.getInstance() self.sUpdate.CheckUpdate(self.ShowUpdateBox) - if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin) - self.Bind(GE.EVT_SSO_LOGOUT, self.onSSOLogout) + self.Bind(GE.EVT_SSO_LOGIN, self.onSSOLogin) + self.Bind(GE.EVT_SSO_LOGGING_IN, self.ShowSsoLogin) - self.titleTimer = wx.Timer(self) - self.Bind(wx.EVT_TIMER, self.updateTitle, self.titleTimer) + def ShowSsoLogin(self, event): + if getattr(event, "login_mode", LoginMethod.SERVER) == LoginMethod.MANUAL: + dlg = SsoLogin(self) + if dlg.ShowModal() == wx.ID_OK: + sEsi = Esi.getInstance() + # todo: verify that this is a correct SSO Info block + sEsi.handleLogin(dlg.ssoInfoCtrl.Value.strip()) - def ShowUpdateBox(self, release): - dlg = UpdateDialog(self, release) + def ShowUpdateBox(self, release, version): + dlg = UpdateDialog(self, release, version) dlg.ShowModal() def LoadPreviousOpenFits(self): @@ -343,7 +348,7 @@ class MainFrame(wx.Frame, IPortUser): # save open fits self.prevOpenFits['pyfaOpenFits'] = [] # clear old list - for page in self.fitMultiSwitch.pages: + for page in self.fitMultiSwitch._pages: m = getattr(page, "getActiveFit", None) if m is not None: self.prevOpenFits['pyfaOpenFits'].append(m()) @@ -357,34 +362,37 @@ class MainFrame(wx.Frame, IPortUser): event.Skip() def ShowAboutBox(self, evt): - v = sys.version_info - info = wx.AboutDialogInfo() + info = wx.adv.AboutDialogInfo() info.Name = "pyfa" - info.Version = gui.aboutData.versionString + info.Version = config.getVersion() # gui.aboutData.versionString + # + # try: + # import matplotlib + # matplotlib_version = matplotlib.__version__ + # except: + # matplotlib_version = None + # + # info.Description = wordwrap(gui.aboutData.description + "\n\nDevelopers:\n\t" + + # "\n\t".join(gui.aboutData.developers) + + # "\n\nAdditional credits:\n\t" + + # "\n\t".join(gui.aboutData.credits) + + # "\n\nLicenses:\n\t" + + # "\n\t".join(gui.aboutData.licenses) + + # "\n\nEVE Data: \t" + gamedata_version + + # "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + + # "\nwxPython: \t" + wx.__version__ + + # "\nSQLAlchemy: \t" + sqlalchemy.__version__ + + # "\nmatplotlib: \t {}".format(matplotlib_version if matplotlib_version else "Not Installed"), + # 500, wx.ClientDC(self)) + # if "__WXGTK__" in wx.PlatformInfo: + # forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" + # else: + # forumUrl = "http://forums.eveonline.com/default.aspx?g=posts&t=466425" + # info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") + wx.adv.AboutBox(info) - try: - import matplotlib - matplotlib_version = matplotlib.__version__ - except: - matplotlib_version = None - - info.Description = wordwrap(gui.aboutData.description + "\n\nDevelopers:\n\t" + - "\n\t".join(gui.aboutData.developers) + - "\n\nAdditional credits:\n\t" + - "\n\t".join(gui.aboutData.credits) + - "\n\nLicenses:\n\t" + - "\n\t".join(gui.aboutData.licenses) + - "\n\nEVE Data: \t" + gamedata_version + - "\nPython: \t\t" + '{}.{}.{}'.format(v.major, v.minor, v.micro) + - "\nwxPython: \t" + wx.__version__ + - "\nSQLAlchemy: \t" + sqlalchemy.__version__ + - "\nmatplotlib: \t {}".format(matplotlib_version if matplotlib_version else "Not Installed"), - 500, wx.ClientDC(self)) - - forumUrl = "https://forums.eveonline.com/t/27156" - - info.WebSite = (forumUrl, "pyfa thread at EVE Online forum") - wx.AboutBox(info) + def showDevTools(self, event): + DevTools(self) def showCharacterEditor(self, event): dlg = CharacterEditor(self) @@ -402,7 +410,7 @@ class MainFrame(wx.Frame, IPortUser): dlg.ShowModal() try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") def showImplantSetEditor(self, event): @@ -412,7 +420,7 @@ class MainFrame(wx.Frame, IPortUser): """ Export active fit """ sFit = Fit.getInstance() fit = sFit.getFit(self.getActiveFit()) - defaultFile = u"%s - %s.xml" % (fit.ship.item.name, fit.name) if fit else None + defaultFile = "%s - %s.xml" % (fit.ship.item.name, fit.name) if fit else None dlg = wx.FileDialog(self, "Save Fitting As...", wildcard="EVE XML fitting files (*.xml)|*.xml", @@ -426,10 +434,10 @@ class MainFrame(wx.Frame, IPortUser): if '.' not in os.path.basename(path): path += ".xml" else: - print("oops, invalid fit format %d" % format_) + print(("oops, invalid fit format %d" % format_)) try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") return @@ -439,7 +447,7 @@ class MainFrame(wx.Frame, IPortUser): try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") def showPreferenceDialog(self, event): @@ -472,6 +480,7 @@ class MainFrame(wx.Frame, IPortUser): # Widgets Inspector if config.debug: self.Bind(wx.EVT_MENU, self.openWXInspectTool, id=self.widgetInspectMenuID) + self.Bind(wx.EVT_MENU, self.showDevTools, id=menuBar.devToolsId) # About self.Bind(wx.EVT_MENU, self.ShowAboutBox, id=wx.ID_ABOUT) # Char editor @@ -606,68 +615,26 @@ class MainFrame(wx.Frame, IPortUser): wx.PostEvent(self, GE.FitChanged(fitID=fitID)) def eveFittings(self, event): - dlg = CrestFittings(self) + dlg = EveFittings(self) dlg.Show() - def updateTitle(self, event): - sCrest = Crest.getInstance() - char = sCrest.implicitCharacter - if char: - t = time.gmtime(char.eve.expires - time.time()) - sTime = time.strftime("%H:%M:%S", t if t >= 0 else 0) - newTitle = "%s | %s - %s" % (self.title, char.name, sTime) - self.SetTitle(newTitle) - def onSSOLogin(self, event): menu = self.GetMenuBar() menu.Enable(menu.eveFittingsId, True) menu.Enable(menu.exportToEveId, True) - if event.type == CrestModes.IMPLICIT: - menu.SetLabel(menu.ssoLoginId, "Logout Character") - self.titleTimer.Start(1000) - - def onSSOLogout(self, event): - self.titleTimer.Stop() - self.SetTitle(self.title) - + def updateEsiMenus(self, type): menu = self.GetMenuBar() - if event.type == CrestModes.IMPLICIT or event.numChars == 0: - menu.Enable(menu.eveFittingsId, False) - menu.Enable(menu.exportToEveId, False) + sEsi = Esi.getInstance() - if event.type == CrestModes.IMPLICIT: - menu.SetLabel(menu.ssoLoginId, "Login to EVE") - - def updateCrestMenus(self, type): - # in case we are logged in when switching, change title back - self.titleTimer.Stop() - self.SetTitle(self.title) - - menu = self.GetMenuBar() - sCrest = Crest.getInstance() - - if type == CrestModes.IMPLICIT: - menu.SetLabel(menu.ssoLoginId, "Login to EVE") - menu.Enable(menu.eveFittingsId, False) - menu.Enable(menu.exportToEveId, False) - else: - menu.SetLabel(menu.ssoLoginId, "Manage Characters") - enable = len(sCrest.getCrestCharacters()) == 0 - menu.Enable(menu.eveFittingsId, not enable) - menu.Enable(menu.exportToEveId, not enable) + menu.SetLabel(menu.ssoLoginId, "Manage Characters") + enable = len(sEsi.getSsoCharacters()) == 0 + menu.Enable(menu.eveFittingsId, not enable) + menu.Enable(menu.exportToEveId, not enable) def ssoHandler(self, event): - sCrest = Crest.getInstance() - if sCrest.settings.get('mode') == CrestModes.IMPLICIT: - if sCrest.implicitCharacter is not None: - sCrest.logout() - else: - uri = sCrest.startServer() - webbrowser.open(uri) - else: - dlg = CrestMgmt(self) - dlg.Show() + dlg = SsoCharacterMgmt(self) + dlg.Show() def exportToEve(self, event): dlg = ExportToEve(self) @@ -742,9 +709,9 @@ class MainFrame(wx.Frame, IPortUser): fit = db_getFit(self.getActiveFit()) toClipboard(Port.exportDna(fit)) - def clipboardCrest(self): + def clipboardEsi(self): fit = db_getFit(self.getActiveFit()) - toClipboard(Port.exportCrest(fit)) + toClipboard(Port.exportESI(fit)) def clipboardXml(self): fit = db_getFit(self.getActiveFit()) @@ -768,7 +735,7 @@ class MainFrame(wx.Frame, IPortUser): CopySelectDialog.copyFormatEftImps: self.clipboardEftImps, CopySelectDialog.copyFormatXml: self.clipboardXml, CopySelectDialog.copyFormatDna: self.clipboardDna, - CopySelectDialog.copyFormatCrest: self.clipboardCrest, + CopySelectDialog.copyFormatEsi: self.clipboardEsi, CopySelectDialog.copyFormatMultiBuy: self.clipboardMultiBuy} dlg = CopySelectDialog(self) dlg.ShowModal() @@ -778,7 +745,7 @@ class MainFrame(wx.Frame, IPortUser): try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") def exportSkillsNeeded(self, event): @@ -834,7 +801,7 @@ class MainFrame(wx.Frame, IPortUser): self.progressDialog.ShowModal() try: dlg.Destroy() - except PyDeadObjectError: + except RuntimeError: pyfalog.error("Tried to destroy an object that doesn't exist in .") def backupToXml(self, event): @@ -1025,7 +992,7 @@ class MainFrame(wx.Frame, IPortUser): # Find a widget to be selected in the tree. Use either the # one under the cursor, if any, or this frame. - wnd = wx.FindWindowAtPointer() + wnd, _ = wx.FindWindowAtPointer() if not wnd: wnd = self InspectionTool().Show(wnd, True) diff --git a/gui/mainMenuBar.py b/gui/mainMenuBar.py index bade53d3d..9c421f779 100644 --- a/gui/mainMenuBar.py +++ b/gui/mainMenuBar.py @@ -25,14 +25,13 @@ from service.character import Character from service.fit import Fit import gui.graphFrame import gui.globalEvents as GE -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from logbook import Logger -pyfalog = Logger(__name__) +# from service.crest import Crest +# from service.crest import CrestModes -if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - from service.crest import Crest - from service.crest import CrestModes +pyfalog = Logger(__name__) class MainMenuBar(wx.MenuBar): @@ -59,7 +58,9 @@ class MainMenuBar(wx.MenuBar): self.toggleOverridesId = wx.NewId() self.importDatabaseDefaultsId = wx.NewId() self.toggleIgnoreRestrictionID = wx.NewId() + self.devToolsId = wx.NewId() + # pheonix: evaluate if this is needed if 'wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0): wx.ID_COPY = wx.NewId() wx.ID_PASTE = wx.NewId() @@ -107,23 +108,23 @@ class MainMenuBar(wx.MenuBar): charEditItem = wx.MenuItem(windowMenu, self.characterEditorId, "&Character Editor\tCTRL+E") charEditItem.SetBitmap(BitmapLoader.getBitmap("character_small", "gui")) - windowMenu.AppendItem(charEditItem) + windowMenu.Append(charEditItem) damagePatternEditItem = wx.MenuItem(windowMenu, self.damagePatternEditorId, "Damage Pattern Editor\tCTRL+D") damagePatternEditItem.SetBitmap(BitmapLoader.getBitmap("damagePattern_small", "gui")) - windowMenu.AppendItem(damagePatternEditItem) + windowMenu.Append(damagePatternEditItem) targetResistsEditItem = wx.MenuItem(windowMenu, self.targetResistsEditorId, "Target Resists Editor\tCTRL+R") targetResistsEditItem.SetBitmap(BitmapLoader.getBitmap("explosive_small", "gui")) - windowMenu.AppendItem(targetResistsEditItem) + windowMenu.Append(targetResistsEditItem) implantSetEditItem = wx.MenuItem(windowMenu, self.implantSetEditorId, "Implant Set Editor\tCTRL+I") implantSetEditItem.SetBitmap(BitmapLoader.getBitmap("hardwire_small", "gui")) - windowMenu.AppendItem(implantSetEditItem) + windowMenu.Append(implantSetEditItem) graphFrameItem = wx.MenuItem(windowMenu, self.graphFrameId, "Graphs\tCTRL+G") graphFrameItem.SetBitmap(BitmapLoader.getBitmap("graphs_small", "gui")) - windowMenu.AppendItem(graphFrameItem) + windowMenu.Append(graphFrameItem) if not gui.graphFrame.graphFrame_enabled: self.Enable(self.graphFrameId, False) @@ -131,31 +132,28 @@ class MainMenuBar(wx.MenuBar): preferencesShortCut = "CTRL+," if 'wxMac' in wx.PlatformInfo else "CTRL+P" preferencesItem = wx.MenuItem(windowMenu, wx.ID_PREFERENCES, "Preferences\t" + preferencesShortCut) preferencesItem.SetBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) - windowMenu.AppendItem(preferencesItem) + windowMenu.Append(preferencesItem) - if 'wxMac' not in wx.PlatformInfo or ('wxMac' in wx.PlatformInfo and wx.VERSION >= (3, 0)): - self.sCrest = Crest.getInstance() + # self.sEsi = Crest.getInstance() - # CREST Menu - crestMenu = wx.Menu() - self.Append(crestMenu, "&CREST") - if self.sCrest.settings.get('mode') != CrestModes.IMPLICIT: - crestMenu.Append(self.ssoLoginId, "Manage Characters") - else: - crestMenu.Append(self.ssoLoginId, "Login to EVE") - crestMenu.Append(self.eveFittingsId, "Browse EVE Fittings") - crestMenu.Append(self.exportToEveId, "Export To EVE") + # CREST Menu + esiMMenu = wx.Menu() + self.Append(esiMMenu, "EVE &SSO") - if self.sCrest.settings.get('mode') == CrestModes.IMPLICIT or len(self.sCrest.getCrestCharacters()) == 0: - self.Enable(self.eveFittingsId, False) - self.Enable(self.exportToEveId, False) + esiMMenu.Append(self.ssoLoginId, "Manage Characters") + esiMMenu.Append(self.eveFittingsId, "Browse EVE Fittings") + esiMMenu.Append(self.exportToEveId, "Export To EVE") - if not self.mainFrame.disableOverrideEditor: - windowMenu.AppendSeparator() - attrItem = wx.MenuItem(windowMenu, self.attrEditorId, "Attribute Overrides\tCTRL+B") - attrItem.SetBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) - windowMenu.AppendItem(attrItem) - windowMenu.Append(self.toggleOverridesId, "Turn Overrides On") + # if self.sEsi.settings.get('mode') == CrestModes.IMPLICIT or len(self.sEsi.getCrestCharacters()) == 0: + self.Enable(self.eveFittingsId, True) + self.Enable(self.exportToEveId, True) + + if not self.mainFrame.disableOverrideEditor: + windowMenu.AppendSeparator() + attrItem = wx.MenuItem(windowMenu, self.attrEditorId, "Attribute Overrides\tCTRL+B") + attrItem.SetBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) + windowMenu.Append(attrItem) + windowMenu.Append(self.toggleOverridesId, "Turn Overrides On") # Help menu helpMenu = wx.Menu() @@ -170,6 +168,8 @@ class MainMenuBar(wx.MenuBar): if config.debug: helpMenu.Append(self.mainFrame.widgetInspectMenuID, "Open Widgets Inspect tool", "Open Widgets Inspect tool") + helpMenu.Append(self.devToolsId, "Open Dev Tools", + "Dev Tools") self.mainFrame.Bind(GE.FIT_CHANGED, self.fitChanged) diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index 480836c04..d39477523 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -64,7 +64,7 @@ class MarketBrowser(wx.Panel): vbox.Add(p, 0, wx.EXPAND) self.metaButtons = [] btn = None - for name in self.sMkt.META_MAP.keys(): + for name in list(self.sMkt.META_MAP.keys()): btn = MetaButton(p, wx.ID_ANY, name.capitalize(), style=wx.BU_EXACTFIT) setattr(self, name, btn) box.Add(btn, 1, wx.ALIGN_CENTER) @@ -78,10 +78,10 @@ class MarketBrowser(wx.Panel): def toggleMetaButton(self, event): """Process clicks on toggle buttons""" - appendMeta = wx.GetMouseState().CmdDown() + mstate = wx.GetMouseState() clickedBtn = event.EventObject - if appendMeta: + if mstate.cmdDown: activeBtns = [btn for btn in self.metaButtons if btn.GetValue()] if activeBtns: clickedBtn.setUserSelection(clickedBtn.GetValue()) diff --git a/gui/multiSwitch.py b/gui/multiSwitch.py index 2efe80bc0..b199848eb 100644 --- a/gui/multiSwitch.py +++ b/gui/multiSwitch.py @@ -17,13 +17,13 @@ # along with pyfa. If not, see . # ============================================================================= -from gui.chromeTabs import PFNotebook +from gui.chrome_tabs import ChromeNotebook import gui.builtinViews.emptyView -class MultiSwitch(PFNotebook): +class MultiSwitch(ChromeNotebook): def __init__(self, parent): - PFNotebook.__init__(self, parent) + ChromeNotebook.__init__(self, parent) # self.AddPage() # now handled by mainFrame self.handlers = handlers = [] for type in TabSpawner.tabTypes: @@ -40,10 +40,10 @@ class MultiSwitch(PFNotebook): tabWnd = gui.builtinViews.emptyView.BlankPage(self) tabWnd.handleDrag = lambda type, info: self.handleDrag(type, info) - PFNotebook.AddPage(self, tabWnd, tabTitle, tabImage, True) + ChromeNotebook.AddPage(self, tabWnd, tabTitle, tabImage, True) def DeletePage(self, n, *args, **kwargs): - PFNotebook.DeletePage(self, n, *args, **kwargs) + ChromeNotebook.DeletePage(self, n, *args, **kwargs) if self.GetPageCount() == 0: self.AddPage() diff --git a/gui/patternEditor.py b/gui/patternEditor.py index 879683373..3bed203a2 100644 --- a/gui/patternEditor.py +++ b/gui/patternEditor.py @@ -19,7 +19,7 @@ # noinspection PyPackageRequirements import wx -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader # noinspection PyPackageRequirements from wx.lib.intctrl import IntCtrl from gui.utils.clipboard import toClipboard, fromClipboard @@ -51,7 +51,7 @@ class DmgPatternTextValidor(BaseValidator): return True except ValueError as e: pyfalog.error(e) - wx.MessageBox(u"{}".format(e), "Error") + wx.MessageBox("{}".format(e), "Error") textCtrl.SetFocus() return False @@ -89,10 +89,10 @@ class DmgPatternEditorDlg(wx.Dialog): DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") def __init__(self, parent): - wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Damage Pattern Editor", size=wx.Size(400, 240)) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Damage Pattern Editor", size=wx.Size(400, 240)) self.block = False - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) @@ -128,7 +128,7 @@ class DmgPatternEditorDlg(wx.Dialog): # set text edit setattr(self, "%sEdit" % type_, IntCtrl(self, wx.ID_ANY, 0, wx.DefaultPosition, defSize)) - setattr(self, "%sPerc" % type_, wx.StaticText(self, wx.ID_ANY, u"0%")) + setattr(self, "%sPerc" % type_, wx.StaticText(self, wx.ID_ANY, "0%")) editObj = getattr(self, "%sEdit" % type_) dmgeditSizer.Add(bmp, 0, style, border) @@ -147,7 +147,7 @@ class DmgPatternEditorDlg(wx.Dialog): footerSizer = wx.BoxSizer(wx.HORIZONTAL) perSizer = wx.BoxSizer(wx.VERTICAL) - self.stNotice = wx.StaticText(self, wx.ID_ANY, u"") + self.stNotice = wx.StaticText(self, wx.ID_ANY, "") self.stNotice.Wrap(-1) perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) @@ -160,7 +160,7 @@ class DmgPatternEditorDlg(wx.Dialog): mainSizer.Add(contentSizer, 1, wx.EXPAND, 0) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + self.closeBtn = wx.Button(self, wx.ID_ANY, "Close", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) @@ -179,7 +179,7 @@ class DmgPatternEditorDlg(wx.Dialog): btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) + btn.SetToolTip("%s patterns %s clipboard" % (name, direction)) footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower()))) @@ -200,7 +200,7 @@ class DmgPatternEditorDlg(wx.Dialog): return p = self.entityEditor.getActiveEntity() - total = sum(map(lambda attr: getattr(self, "%sEdit" % attr).GetValue(), self.DAMAGE_TYPES)) + total = sum([getattr(self, "%sEdit" % attr).GetValue() for attr in self.DAMAGE_TYPES]) for type_ in self.DAMAGE_TYPES: editObj = getattr(self, "%sEdit" % type_) percObj = getattr(self, "%sPerc" % type_) diff --git a/gui/preferenceDialog.py b/gui/preferenceDialog.py index 2ed0f829a..37ca5ddaf 100644 --- a/gui/preferenceDialog.py +++ b/gui/preferenceDialog.py @@ -20,14 +20,14 @@ # noinspection PyPackageRequirements import wx from gui.preferenceView import PreferenceView -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader class PreferenceDialog(wx.Dialog): def __init__(self, parent): wx.Dialog.__init__(self, parent, id=wx.ID_ANY, size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE) self.SetTitle("pyfa - Preferences") - i = wx.IconFromBitmap(BitmapLoader.getBitmap("preferences_small", "gui")) + i = wx.Icon(BitmapLoader.getBitmap("preferences_small", "gui")) self.SetIcon(i) mainSizer = wx.BoxSizer(wx.VERTICAL) @@ -38,7 +38,7 @@ class PreferenceDialog(wx.Dialog): # self.listview.SetSize((500, -1)) self.imageList = wx.ImageList(32, 32) - self.listbook.SetImageList(self.imageList) + self.listbook.AssignImageList(self.imageList) mainSizer.Add(self.listbook, 1, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.LEFT, 5) @@ -46,8 +46,8 @@ class PreferenceDialog(wx.Dialog): mainSizer.Add(self.m_staticline2, 0, wx.EXPAND, 5) btnSizer = wx.BoxSizer(wx.HORIZONTAL) - btnSizer.AddSpacer((0, 0), 1, wx.EXPAND, 5) - self.btnOK = wx.Button(self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0) + btnSizer.AddStretchSpacer() + self.btnOK = wx.Button(self, wx.ID_ANY, "OK", wx.DefaultPosition, wx.DefaultSize, 0) btnSizer.Add(self.btnOK, 0, wx.ALL, 5) mainSizer.Add(btnSizer, 0, wx.EXPAND, 5) self.SetSizer(mainSizer) @@ -55,23 +55,23 @@ class PreferenceDialog(wx.Dialog): self.Centre(wx.BOTH) for prefView in PreferenceView.views: - page = wx.Panel(self.listbook) + page = wx.ScrolledWindow(self.listbook) + page.SetScrollbars(1, 1, 20, 20) bmp = prefView.getImage() if bmp: imgID = self.imageList.Add(bmp) else: imgID = -1 prefView.populatePanel(page) + self.listbook.AddPage(page, prefView.title, imageId=imgID) - # Set the height based on a condition. Can all the panels fit in the current height? - # If not, use the .GetBestVirtualSize() to ensure that all content is available. minHeight = 550 bestFit = self.GetBestVirtualSize() if minHeight > bestFit[1]: - self.SetSizeWH(650, minHeight) + self.SetSize(650, minHeight) else: - self.SetSizeWH(650, bestFit[1]) + self.SetSize(650, bestFit[1]) self.Layout() diff --git a/gui/preferenceView.py b/gui/preferenceView.py index a52a720e4..9ce2e676d 100644 --- a/gui/preferenceView.py +++ b/gui/preferenceView.py @@ -43,7 +43,7 @@ from gui.builtinPreferenceViews import ( # noqa: E402, F401 pyfaGeneralPreferences, pyfaNetworkPreferences, pyfaHTMLExportPreferences, - pyfaCrestPreferences, + pyfaEsiPreferences, pyfaContextMenuPreferences, pyfaStatViewPreferences, pyfaUpdatePreferences, diff --git a/gui/propertyEditor.py b/gui/propertyEditor.py index b6f4ab1a1..68cc93ee8 100644 --- a/gui/propertyEditor.py +++ b/gui/propertyEditor.py @@ -19,7 +19,7 @@ import gui.display as d import gui.globalEvents as GE import gui.builtinMarketBrowser.pfSearchBox as SBox from gui.marketBrowser import SearchBox -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader pyfalog = Logger(__name__) @@ -30,7 +30,7 @@ class AttributeEditor(wx.Frame): size=wx.Size(650, 600), style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.TAB_TRAVERSAL) - i = wx.IconFromBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) + i = wx.Icon(BitmapLoader.getBitmap("fit_rename_small", "gui")) self.SetIcon(i) self.mainFrame = parent @@ -48,7 +48,7 @@ class AttributeEditor(wx.Frame): self.Bind(wx.EVT_MENU, self.OnExport, fileExport) self.Bind(wx.EVT_MENU, self.OnClear, fileClear) - i = wx.IconFromBitmap(BitmapLoader.getBitmap("fit_rename_small", "gui")) + i = wx.Icon(BitmapLoader.getBitmap("fit_rename_small", "gui")) self.SetIcon(i) self.mainFrame = parent @@ -70,7 +70,7 @@ class AttributeEditor(wx.Frame): mainSizer.Add(leftPanel, 1, wx.ALL | wx.EXPAND, 5) rightSizer = wx.BoxSizer(wx.VERTICAL) - self.btnRemoveOverrides = wx.Button(panel, wx.ID_ANY, u"Remove Overides for Item", wx.DefaultPosition, + self.btnRemoveOverrides = wx.Button(panel, wx.ID_ANY, "Remove Overides for Item", wx.DefaultPosition, wx.DefaultSize, 0) self.pg = AttributeGrid(panel) rightSizer.Add(self.pg, 1, wx.ALL | wx.EXPAND, 5) @@ -102,9 +102,11 @@ class AttributeEditor(wx.Frame): style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() - with open(path, 'rb') as csvfile: + with open(path, 'r') as csvfile: spamreader = csv.reader(csvfile) for row in spamreader: + if len(row) == 0: # csvwriter seems to added blank lines to the end sometimes + continue itemID, attrID, value = row item = getItem(int(itemID)) attr = getAttributeInfo(int(attrID)) @@ -123,10 +125,10 @@ class AttributeEditor(wx.Frame): if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() - with open(path, 'wb') as csvfile: + with open(path, 'w', encoding='utf-8') as csvfile: writer = csv.writer(csvfile) for item in items: - for key, override in item.overrides.iteritems(): + for key, override in item.overrides.items(): writer.writerow([item.ID, override.attrID, override.value]) def OnClear(self, event): @@ -145,7 +147,7 @@ class AttributeEditor(wx.Frame): # them due to the eve/user database disconnect. We must loop through # all items that have overrides and remove them for item in items: - for _, x in item.overrides.items(): + for _, x in list(item.overrides.items()): item.deleteOverride(x.attr) self.itemView.updateItems(True) self.pg.Clear() @@ -247,7 +249,7 @@ class AttributeGrid(wxpg.PropertyGrid): if self.item is None: return - for x in self.item.overrides.values(): + for x in list(self.item.overrides.values()): self.item.deleteOverride(x.attr) self.itemView.updateItems(True) self.ClearModifiedStatus() diff --git a/gui/pyfa_gauge.py b/gui/pyfa_gauge.py new file mode 100644 index 000000000..0144f651c --- /dev/null +++ b/gui/pyfa_gauge.py @@ -0,0 +1,449 @@ +# =============================================================================== +# PyfaGauge is a generic Gauge implementation tailored for pyfa (the Python +# Fitting Assistant). It uses the easeOutQuad equation from +# caurina.transitions.Tweener to do animations +# +# ToDo: make SetGradient(from and not dependant on value) +# ToDo: fix 0 range (currently resets range to 0.01, but this causes problems if +# we really set range at 0.01). Perhaps make it -1 and test percentage as +# a negativeor something. +# ToDo: possibly devise a way to determine transition percents on init +# (currently hardcoded) +# +# =============================================================================== + +import copy +import wx + +from gui.utils import color as color_utils +from gui.utils import draw, anim_effects +from service.fit import Fit + + +class PyGauge(wx.Window): + def __init__(self, parent, font, max_range=100, size=(-1, 30), *args, + **kargs): + + super().__init__(parent, size=size, *args, **kargs) + + self._size = size + + self._border_colour = wx.BLACK + self._bar_colour = None + self._bar_gradient = None + + self._border_padding = 0 + self._max_range = max_range + self._value = 0 + + self._fraction_digits = 0 + + self._timer_id = wx.NewId() + self._timer = None + + self._oldValue = 0 + + self._anim_duration = 500 + self._anim_step = 0 + self._period = 20 + self._anim_value = 0 + self._anim_direction = 0 + self.anim_effect = anim_effects.OUT_QUAD + + # transition colors used based on how full (or overfilled) the gauge is. + self.transition_colors = [ + (wx.Colour(191, 191, 191), wx.Colour(96, 191, 0)), # < 0-100% + (wx.Colour(191, 167, 96), wx.Colour(255, 191, 0)), # < 100-101% + (wx.Colour(255, 191, 0), wx.Colour(255, 128, 0)), # < 101-103% + (wx.Colour(255, 128, 0), wx.Colour(255, 0, 0)) # < 103-105% + ] + + self.gradient_effect = -35 + + self._percentage = 0 + self._old_percentage = 0 + self._show_remaining = False + + self.font = font + + self.SetBackgroundColour(wx.Colour(51, 51, 51)) + + self._tooltip = wx.ToolTip("0.00/100.00") + self.SetToolTip(self._tooltip) + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_TIMER, self.OnTimer) + self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + + def OnEraseBackground(self, event): + pass + + def OnWindowEnter(self, event): + self._show_remaining = True + self.Refresh() + + def OnWindowLeave(self, event): + self._show_remaining = False + self.Refresh() + + def GetBorderColour(self): + return self._border_colour + + def SetBorderColour(self, colour): + self._border_colour = colour + + def GetBarColour(self): + return self._bar_colour + + def SetBarColour(self, colour): + self._bar_colour = colour + + def SetFractionDigits(self, digits): + self._fraction_digits = digits + + def GetBarGradient(self): + if self._bar_gradient is None: + return None + + return self._bar_gradient[0] + + def SetBarGradient(self, gradient=None): + if gradient is None: + self._bar_gradient = None + else: + if not isinstance(gradient, list): + self._bar_gradient = [gradient] + else: + self._bar_gradient = list(gradient) + + def GetBorderPadding(self): + return self._border_padding + + def SetBorderPadding(self, padding): + self._border_padding = padding + + def GetRange(self): + """ Returns the maximum value of the gauge. """ + return self._max_range + + def Animate(self): + sFit = Fit.getInstance() + if sFit.serviceFittingOptions["enableGaugeAnimation"]: + if not self._timer: + self._timer = wx.Timer(self, self._timer_id) + + self._anim_step = 0 + self._timer.Start(self._period) + else: + self._anim_value = self._percentage + self.Refresh() + + def SetRange(self, range, reinit=False, animate=True): + """ + Sets the range of the gauge. The gauge length is its + value as a proportion of the range. + """ + + if self._max_range == range: + return + + # we cannot have a range of zero (laws of physics, etc), so we set it + if range <= 0: + self._max_range = 0.01 + else: + self._max_range = range + + if reinit is False: + self._old_percentage = self._percentage + self._percentage = (self._value / self._max_range) * 100 + else: + self._old_percentage = self._percentage + self._percentage = 0 + self._value = 0 + + if animate: + self.Animate() + + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range if self._max_range > 0.01 else 0)) + + def GetValue(self): + return self._value + + def SetValue(self, value, animate=True): + """ Sets the current position of the gauge. """ + if self._value == value: + return + + self._old_percentage = self._percentage + self._value = value + + if value < 0: + self._value = 0 + + self._percentage = (self._value / self._max_range) * 100 + + if animate: + self.Animate() + + self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._max_range)) + + def SetValueRange(self, value, range, reinit=False): + """ Set both value and range of the gauge. """ + range_ = float(range) + + if range_ <= 0: + self._max_range = 0.01 + else: + self._max_range = range_ + + value = float(value) + + self._value = value + if value < 0: + self._value = float(0) + + if reinit is False: + self._old_percentage = self._percentage + self._percentage = (self._value / self._max_range) * 100 + + else: + self._old_percentage = self._percentage + self._percentage = 0 + + self.Animate() + self._tooltip.SetTip("%.2f/%.2f" % + (self._value, self._max_range if float(self._max_range) > 0.01 else 0)) + + def OnPaint(self, event): + dc = wx.AutoBufferedPaintDC(self) + rect = self.GetClientRect() + + dc.SetBackground(wx.Brush(self.GetBackgroundColour())) + dc.Clear() + + colour = self.GetBackgroundColour() + + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.Pen(colour)) + + dc.DrawRectangle(rect) + + value = self._percentage + + if self._timer: + if self._timer.IsRunning(): + value = self._anim_value + + if self._border_colour: + dc.SetPen(wx.Pen(self.GetBorderColour())) + dc.DrawRectangle(rect) + pad = 1 + self.GetBorderPadding() + rect.Deflate(pad, pad) + + if self.GetBarColour(): + # if we have a bar color set, then we will use this + + colour = self.GetBarColour() + dc.SetBrush(wx.Brush(colour)) + dc.SetPen(wx.Pen(colour)) + + # calculate width of bar and draw it + if value > 100: + w = rect.width + else: + w = rect.width * (float(value) / 100) + + r = copy.copy(rect) + r.width = w + dc.DrawRectangle(r) + else: + # if bar color is not set, then we use pre-defined transitions + # for the colors based on the percentage value + + # calculate width of bar + if value > 100: + w = rect.width + else: + w = rect.width * (float(value) / 100) + r = copy.copy(rect) + r.width = w + + # determine transition range number and calculate xv (which is the + # progress between the two transition ranges) + pv = value + if pv <= 100: + xv = pv / 100 + transition = 0 + elif pv <= 101: + xv = pv - 100 + transition = 1 + elif pv <= 103: + xv = (pv - 101) / 2 + transition = 2 + elif pv <= 105: + xv = (pv - 103) / 2 + transition = 3 + else: + pv = 106 + xv = pv - 100 + transition = -1 + + if transition != -1: + start_color, end_color = self.transition_colors[transition] + color = color_utils.CalculateTransition(start_color, end_color, + xv) + else: + color = wx.Colour(191, 48, 48) # dark red + + color_factor = self.gradient_effect / 100 + mid_factor = (self.gradient_effect / 2) / 100 + + if self.gradient_effect > 0: + gradient_color = color_utils.Brighten(color, color_factor) + gradient_mid = color_utils.Brighten(color, mid_factor) + else: + gradient_color = color_utils.Darken(color, color_factor * -1) + gradient_mid = color_utils.Darken(color, mid_factor * -1) + + # draw bar + gradient_bitmap = draw.DrawGradientBar( + r.width, + r.height, + gradient_mid, + color, + gradient_color + ) + dc.DrawBitmap(gradient_bitmap, r.left, r.top) + + # font stuff begins here + dc.SetFont(self.font) + + # determine shadow position + r = copy.copy(rect) + r.left += 1 + r.top += 1 + + if self._max_range == 0.01 and self._value > 0: + format = u'\u221e' # infinity symbol + # drop shadow + dc.SetTextForeground(wx.Colour(80, 80, 80)) # dark grey + dc.DrawLabel(format, r, wx.ALIGN_CENTER) + # text + dc.SetTextForeground(wx.WHITE) + dc.DrawLabel(format, rect, wx.ALIGN_CENTER) + else: + if not self.GetBarColour() and self._show_remaining: + # we only do these for gradients with mouse over + range = self._max_range if self._max_range > 0.01 else 0 + value = range - self._value + if value < 0: + label = "over" + value = -value + else: + label = "left" + format = "{0:." + str(self._fraction_digits) + "f} " + label + else: + format = "{0:." + str(self._fraction_digits) + "f}%" + + # drop shadow + dc.SetTextForeground(wx.Colour(80, 80, 80)) + dc.DrawLabel(format.format(value), r, wx.ALIGN_CENTER) + # text + dc.SetTextForeground(wx.WHITE) + dc.DrawLabel(format.format(value), rect, wx.ALIGN_CENTER) + + def OnTimer(self, event): + old_value = self._old_percentage + value = self._percentage + start = 0 + + # -1 = left direction, 1 = right direction + direction = 1 if old_value < value else -1 + + end = direction * (value - old_value) + + self._anim_direction = direction + step = self.anim_effect(self._anim_step, start, end, self._anim_duration) + + self._anim_step += self._period + + if self._timer_id == event.GetId(): + stop_timer = False + + if self._anim_step > self._anim_duration: + stop_timer = True + + # add new value to the animation if we haven't reached our goal + # otherwise, stop animation + if direction == 1: + if old_value + step < value: + self._anim_value = old_value + step + else: + stop_timer = True + else: + if old_value - step > value: + self._anim_value = old_value - step + else: + stop_timer = True + + if stop_timer: + self._timer.Stop() + + self.Refresh() + + +if __name__ == "__main__": + def frange(x, y, jump): + while x < y: + yield x + x += jump + + class MyPanel(wx.Panel): + def __init__(self, parent, size=(500, 500)): + wx.Panel.__init__(self, parent, size=size) + box = wx.BoxSizer(wx.VERTICAL) + + # tests the colors of transition based on percentage used + # list of test values (from 99 -> 106 in increments of 0.5) + tests = [x for x in frange(99, 106.5, .5)] + + font = wx.Font(9, wx.SWISS, wx.NORMAL, wx.NORMAL, False) + + for i, value in enumerate(tests): + self.gauge = PyGauge(self, font, size=(100, 25)) + self.gauge.SetValueRange(value, 100) + self.gauge.SetFractionDigits(2) + box.Add(self.gauge, 0, wx.ALL, 2) + + gauge = PyGauge(self, font, size=(100, 25)) + gauge.SetBackgroundColour(wx.Colour(52, 86, 98)) + gauge.SetBarColour(wx.Colour(38, 133, 198)) + gauge.SetValue(59) + gauge.SetFractionDigits(1) + box.Add(gauge, 0, wx.ALL, 2) + + self.SetSizer(box) + self.Layout() + + # see animation going backwards with last gauge + wx.CallLater(2000, self.ChangeValues) + + def ChangeValues(self): + self.gauge.SetValueRange(4, 100) + + class Frame(wx.Frame): + def __init__(self, title, size=(500, 800)): + wx.Frame.__init__(self, None, title=title, size=size) + self.statusbar = self.CreateStatusBar() + main_sizer = wx.BoxSizer(wx.VERTICAL) + panel = MyPanel(self, size=size) + main_sizer.Add(panel) + self.SetSizer(main_sizer) + + app = wx.App(redirect=False) # Error messages go to popup window + top = Frame("Test Chrome Tabs") + top.Show() + app.MainLoop() diff --git a/gui/pyfatogglepanel.py b/gui/pyfatogglepanel.py deleted file mode 100644 index cdd4398bb..000000000 --- a/gui/pyfatogglepanel.py +++ /dev/null @@ -1,199 +0,0 @@ -########################################################################### -# pyfatogllepanel.py -# -# Author: Darriele - HomeWorld -# -# Project home: https://github.com/pyfa-org/Pyfa - pyfa project -# Some portions of code are based on -# AGW:pycollapsiblepane generic implementation of wx.CollapsiblePane -# AGW:pycollapsiblepane credits ( from the original source file used ): -# Andrea Gavana, @ 09 Aug 2007 -# Latest Revision: 12 Apr 2010, 12.00 GMT -# -# Module description: -# TogglePanel class is a wx.collipsablepane like implementation that uses -# some optimization from awg::pycollipsablepane to provide certain -# features tailored for PYFA needs. -# -# This module is part of PYFA (PYthon Fitting Assitant) and it shares the same -# licence ( read PYFA licence notice: gpl.txt ) -# -# Notes: leave the commented code as it is, those line will be removed someday -########################################################################### - -# noinspection PyPackageRequirements -import wx -from gui.bitmapLoader import BitmapLoader - - -class TogglePanel(wx.Panel): - def __init__(self, parent, forceLayout=-1): - wx.Panel.__init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.Size(-1, -1), - style=wx.TAB_TRAVERSAL) - - self._toggle = 1 - self.parent = parent - self.forceLayout = forceLayout - self.bkColour = self.GetBackgroundColour() - - # Create the main sizer of this panel - self.mainSizer = wx.BoxSizer(wx.VERTICAL) - self.SetSizer(self.mainSizer) - # parentSize = parent.GetMinSize() - - # Create the header panel - self.headerPanel = wx.Panel(self) - self.mainSizer.Add(self.headerPanel, 0, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.RIGHT, 1) - - # Load expanded/collapsed bitmaps from the icons folder - self.bmpExpanded = BitmapLoader.getBitmap("down-arrow2", "gui") - self.bmpCollapsed = BitmapLoader.getBitmap("up-arrow2", "gui") - - # Make the bitmaps have the same color as window text - sysTextColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) - - img = self.bmpExpanded.ConvertToImage() - img.Replace(0, 0, 0, sysTextColour[0], sysTextColour[1], sysTextColour[2]) - self.bmpExpanded = wx.BitmapFromImage(img) - - img = self.bmpCollapsed.ConvertToImage() - img.Replace(0, 0, 0, sysTextColour[0], sysTextColour[1], sysTextColour[2]) - self.bmpCollapsed = wx.BitmapFromImage(img) - - self.headerBmp = wx.StaticBitmap(self.headerPanel) - self.headerBmp.SetBitmap(self.bmpExpanded) - - # Create the header sizer and add static bitmap and static text controls to it - - headerSizer = wx.BoxSizer(wx.HORIZONTAL) - self.headerPanel.SetSizer(headerSizer) - - hbmpSizer = wx.BoxSizer(wx.HORIZONTAL) - hlblSizer = wx.BoxSizer(wx.HORIZONTAL) - self.hcntSizer = wx.BoxSizer(wx.HORIZONTAL) - - hbmpSizer.Add(self.headerBmp, 0, 0, 5) - - self.headerLabel = wx.StaticText(self.headerPanel, wx.ID_ANY, u"PYFA", wx.DefaultPosition, wx.DefaultSize, 0) - hlblSizer.Add(self.headerLabel, 0, wx.EXPAND, 5) - - headerSizer.Add(hbmpSizer, 0, wx.RIGHT, 5) - headerSizer.Add(hlblSizer, 0, wx.RIGHT, 5) - headerSizer.Add(self.hcntSizer, 0, wx.RIGHT, 5) - - # Set the static text font weight to BOLD - - headerFont = parent.GetFont() - headerFont.SetWeight(wx.BOLD) - self.headerLabel.SetFont(headerFont) - - # Create the content panel and its main sizer - - self.contentSizer = wx.BoxSizer(wx.VERTICAL) - self.contentPanel = wx.Panel(self) - self.contentPanel.SetSizer(self.contentSizer) - - self.mainSizer.Add(self.contentPanel, 0, wx.EXPAND | wx.RIGHT | wx.LEFT, 5) - - self.Layout() - - # Connect Events - self.headerLabel.Bind(wx.EVT_LEFT_UP, self.toggleContent) - self.headerBmp.Bind(wx.EVT_LEFT_UP, self.toggleContent) - self.headerPanel.Bind(wx.EVT_LEFT_UP, self.toggleContent) - - def __del__(self): - pass - - def AddToggleItem(self, hitem): - hitem.Bind(wx.EVT_LEFT_UP, self.toggleContent) - - def GetHeaderContentSizer(self): - return self.hcntSizer - - def GetHeaderPanel(self): - return self.headerPanel - - def InsertItemInHeader(self, item): - self.hcntSizer.Add(item, 0, 0, 0) - self.Layout() - - def AddSizer(self, sizer): - self.contentSizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 0) - self.Layout() - - def GetContentPane(self): - return self.contentPanel - - def SetLabel(self, label): - self.headerLabel.SetLabel(label) - - def IsCollapsed(self): - """ Returns ``True`` if the pane window is currently hidden. """ - if self._toggle == 1: - return False - else: - return True - - def IsExpanded(self): - """ Returns ``True`` if the pane window is currently shown. """ - if self._toggle == 1: - return False - else: - return True - - def OnStateChange(self, sz): - """ - Handles the status changes (collapsing/expanding). - - :param sz: an instance of `wx.Size`. - """ - - # minimal size has priority over the best size so set here our min size - self.SetMinSize(sz) - self.SetSize(sz) - - self.parent.GetSizer().SetSizeHints(self.parent) - - if self.IsCollapsed(): - # expanded . collapsed transition - if self.parent.GetSizer(): - # we have just set the size hints... - sz = self.parent.GetSizer().CalcMin() - - # use SetClientSize() and not SetSize() otherwise the size for - # e.g. a wxFrame with a menubar wouldn't be correctly set - self.parent.SetClientSize(sz) - else: - self.parent.Layout() - else: - # collapsed . expanded transition - # force our parent to "fit", i.e. expand so that it can honour - # our minimal size - self.parent.Fit() - - # Toggle the content panel (hide/show) - def toggleContent(self, event): - self.Freeze() - - if self._toggle == 1: - self.contentMinSize = self.contentPanel.GetSize() - self.contentPanel.Hide() - self.headerBmp.SetBitmap(self.bmpCollapsed) - else: - self.contentPanel.Show() - self.headerBmp.SetBitmap(self.bmpExpanded) - - self._toggle *= -1 - self.Layout() - self.Thaw() - - if self.forceLayout == -1: - if wx.VERSION >= (3, 0): - x, y = self.GetBestSize() - y -= self.contentPanel.GetSize()[1] - else: - x, y = self.GetBestSize() - self.OnStateChange((x, y)) - else: - self.parent.Layout() diff --git a/gui/pygauge.py b/gui/pygauge.py deleted file mode 100644 index dbb0c4531..000000000 --- a/gui/pygauge.py +++ /dev/null @@ -1,448 +0,0 @@ -# --------------------------------------------------------------------------------- # -# PYFAGAUGE wxPython IMPLEMENTATION -# -# Darriele, @ 08/30/2010 -# Updated: 09/07/2010 -# Based on AWG : pygauge code -# --------------------------------------------------------------------------------- # - -""" -PyfaGauge is a generic Gauge implementation tailored for PYFA (Python Fitting Assistant) -It uses the easeOutQuad equation from caurina.transitions.Tweener to do the animation stuff -""" - -# noinspection PyPackageRequirements -import wx -import copy - -from gui.utils import colorUtils -import gui.utils.drawUtils as drawUtils -import gui.utils.animEffects as animEffects -import gui.utils.fonts as fonts - -from service.fit import Fit - - -class PyGauge(wx.PyWindow): - """ - This class provides a visual alternative for `wx.Gauge`. It currently - only support determinant mode (see SetValue and SetRange) - """ - - def __init__(self, parent, id=wx.ID_ANY, range=100, pos=wx.DefaultPosition, - size=(-1, 30), style=0): - """ - Default class constructor. - - :param `parent`: parent window. Must not be ``None``; - :param `id`: window identifier. A value of -1 indicates a default value; - :param `pos`: the control position. A value of (-1, -1) indicates a default position, - chosen by either the windowing system or wxPython, depending on platform; - :param `size`: the control size. A value of (-1, -1) indicates a default size, - chosen by either the windowing system or wxPython, depending on platform. - """ - - wx.PyWindow.__init__(self, parent, id, pos, size, style) - - self._size = size - - self._border_colour = wx.BLACK - self._barColour = self._barColourSorted = [wx.Colour(212, 228, 255)] - self._barGradient = self._barGradientSorted = None - - self._border_padding = 0 - self._range = range - self._value = 0 - - self._fractionDigits = 0 - - self._timerId = wx.NewId() - self._timer = None - - self._oldValue = 0 - - self._animDuration = 500 - self._animStep = 0 - self._period = 20 - self._animValue = 0 - self._animDirection = 0 - self.animEffect = animEffects.OUT_QUAD - - self.transitionsColors = [(wx.Colour(191, 191, 191, 255), wx.Colour(96, 191, 0, 255)), - (wx.Colour(191, 167, 96, 255), wx.Colour(255, 191, 0, 255)), - (wx.Colour(255, 191, 0, 255), wx.Colour(255, 128, 0, 255)), - (wx.Colour(255, 128, 0, 255), wx.Colour(255, 0, 0, 255))] - self.gradientEffect = -35 - - self._percentage = 0 - self._oldPercentage = 0 - self._showRemaining = False - - self.font = wx.Font(fonts.NORMAL, wx.SWISS, wx.NORMAL, wx.NORMAL, False) - - self.SetBarGradient((wx.Colour(119, 119, 119), wx.Colour(153, 153, 153))) - self.SetBackgroundColour(wx.Colour(51, 51, 51)) - self._tooltip = wx.ToolTip("") - self.SetToolTip(self._tooltip) - self._tooltip.SetTip("0.00/100.00") - - self.Bind(wx.EVT_PAINT, self.OnPaint) - self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) - self.Bind(wx.EVT_TIMER, self.OnTimer) - self.Bind(wx.EVT_ENTER_WINDOW, self.OnWindowEnter) - self.Bind(wx.EVT_LEAVE_WINDOW, self.OnWindowLeave) - - def OnWindowEnter(self, event): - self._showRemaining = True - self.Refresh() - - def OnWindowLeave(self, event): - self._showRemaining = False - self.Refresh() - - def DoGetBestSize(self): - """ - Overridden base class virtual. Determines the best size of the - button based on the label and bezel size. - """ - - return wx.Size(self._size[0], self._size[1]) - - def GetBorderColour(self): - return self._border_colour - - def SetBorderColour(self, colour): - self._border_colour = colour - - SetBorderColor = SetBorderColour - GetBorderColor = GetBorderColour - - def GetBarColour(self): - return self._barColour[0] - - def SetBarColour(self, colour): - if not isinstance(colour, list): - self._barColour = [colour] - else: - self._barColour = list(colour) - - SetBarColor = SetBarColour - GetBarColor = GetBarColour - - def SetFractionDigits(self, digits): - self._fractionDigits = digits - - def GetBarGradient(self): - """ Returns a tuple containing the gradient start and end colours. """ - - if self._barGradient is None: - return None - - return self._barGradient[0] - - def SetBarGradient(self, gradient=None): - """ - Sets the bar gradient. This overrides the BarColour. - - :param gradient: a tuple containing the gradient start and end colours. - """ - if gradient is None: - self._barGradient = None - else: - if not isinstance(gradient, list): - self._barGradient = [gradient] - else: - self._barGradient = list(gradient) - - def GetBorderPadding(self): - """ Gets the border padding. """ - - return self._border_padding - - def SetBorderPadding(self, padding): - """ - Sets the border padding. - - :param padding: pixels between the border and the progress bar. - """ - - self._border_padding = padding - - def GetRange(self): - """ Returns the maximum value of the gauge. """ - - return self._range - - def Animate(self): - sFit = Fit.getInstance() - if sFit.serviceFittingOptions["enableGaugeAnimation"]: - if not self._timer: - self._timer = wx.Timer(self, self._timerId) - self._animStep = 0 - self._timer.Start(self._period) - else: - self._animValue = self._percentage - self.Refresh() - - def SetRange(self, range, reinit=False): - """ - Sets the range of the gauge. The gauge length is its - value as a proportion of the range. - - :param reinit: - :param range: The maximum value of the gauge. - """ - - if self._range == range: - return - - range_ = float(range) - - if range_ <= 0: - self._range = 0.01 - else: - self._range = range_ - - if reinit is False: - self._oldPercentage = self._percentage - self._percentage = (self._value / self._range) * 100 - else: - self._oldPercentage = self._percentage - self._percentage = 0 - self._value = 0 - - self.Animate() - - self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range > 0.01 else 0)) - - def GetValue(self): - """ Returns the current position of the gauge. """ - - return self._value - - def SetValue(self, value): - """ Sets the current position of the gauge. """ - if self._value == value: - return - - value = float(value) - self._oldPercentage = self._percentage - self._value = value - if value < 0: - self._value = 0 - self._percentage = (self._value / self._range) * 100 - - self.Animate() - - self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range)) - - def SetValueRange(self, value, range, reinit=False): - if self._value == value and self._range == range: - return - - range_ = float(range) - - if range_ <= 0: - self._range = 0.01 - else: - self._range = range_ - - value = float(value) - - self._value = value - if value < 0: - self._value = float(0) - - if reinit is False: - self._oldPercentage = self._percentage - self._percentage = (self._value / self._range) * 100 - - else: - self._oldPercentage = self._percentage - self._percentage = 0 - - self.Animate() - self._tooltip.SetTip("%.2f/%.2f" % (self._value, self._range if self._range > 0.01 else 0)) - - @staticmethod - def OnEraseBackground(event): - """ - Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{PyGauge}. - - :param event: a `wx.EraseEvent` event to be processed. - - :note: This method is intentionally empty to reduce flicker. - """ - - pass - - def OnPaint(self, event): - """ - Handles the ``wx.EVT_PAINT`` event for L{PyGauge}. - - :param event: a `wx.PaintEvent` event to be processed. - """ - - dc = wx.BufferedPaintDC(self) - rect = self.GetClientRect() - - dc.SetBackground(wx.Brush(self.GetBackgroundColour())) - dc.Clear() - - colour = self.GetBackgroundColour() - - dc.SetBrush(wx.Brush(colour)) - dc.SetPen(wx.Pen(colour)) - - dc.DrawRectangleRect(rect) - - value = self._percentage - if self._timer: - if self._timer.IsRunning(): - value = self._animValue - - if self._border_colour: - dc.SetPen(wx.Pen(self.GetBorderColour())) - dc.DrawRectangleRect(rect) - pad = 1 + self.GetBorderPadding() - rect.Deflate(pad, pad) - - if self.GetBarGradient(): - - if value > 100: - w = rect.width - else: - w = rect.width * (float(value) / 100) - r = copy.copy(rect) - r.width = w - - if r.width > 0: - # If we draw it with zero width, GTK throws errors. This way, - # only draw it if the gauge will actually show something. - # We stick other calculations in this block to avoid wasting - # time on them if not needed. See GH issue #282 - - pv = value - - if pv <= 100: - xv = pv / 100 - transition = 0 - - elif pv <= 101: - xv = pv - 100 - transition = 1 - - elif pv <= 103: - xv = (pv - 101) / 2 - transition = 2 - - elif pv <= 105: - xv = (pv - 103) / 2 - transition = 3 - - else: - pv = 106 - xv = pv - 100 - transition = -1 - - if transition != -1: - colorS, colorE = self.transitionsColors[transition] - color = colorUtils.CalculateTransitionColor(colorS, colorE, xv) - else: - color = wx.Colour(191, 48, 48) - - if self.gradientEffect > 0: - gcolor = colorUtils.BrightenColor(color, float(self.gradientEffect) / 100) - gMid = colorUtils.BrightenColor(color, float(self.gradientEffect / 2) / 100) - else: - gcolor = colorUtils.DarkenColor(color, float(-self.gradientEffect) / 100) - gMid = colorUtils.DarkenColor(color, float(-self.gradientEffect / 2) / 100) - - gBmp = drawUtils.DrawGradientBar(r.width, r.height, gMid, color, gcolor) - dc.DrawBitmap(gBmp, r.left, r.top) - - else: - colour = self.GetBarColour() - dc.SetBrush(wx.Brush(colour)) - dc.SetPen(wx.Pen(colour)) - if value > 100: - w = rect.width - else: - w = rect.width * (float(value) / 100) - r = copy.copy(rect) - r.width = w - dc.DrawRectangleRect(r) - - dc.SetFont(self.font) - - r = copy.copy(rect) - r.left += 1 - r.top += 1 - if self._range == 0.01 and self._value > 0: - formatStr = u'\u221e' - dc.SetTextForeground(wx.Colour(80, 80, 80)) - dc.DrawLabel(formatStr, r, wx.ALIGN_CENTER) - - dc.SetTextForeground(wx.Colour(255, 255, 255)) - dc.DrawLabel(formatStr, rect, wx.ALIGN_CENTER) - else: - if self.GetBarGradient() and self._showRemaining: - range = self._range if self._range > 0.01 else 0 - value = range - self._value - if value < 0: - label = "over" - value = -value - else: - label = "left" - formatStr = "{0:." + str(self._fractionDigits) + "f} " + label - - else: - formatStr = "{0:." + str(self._fractionDigits) + "f}%" - - dc.SetTextForeground(wx.Colour(80, 80, 80)) - dc.DrawLabel(formatStr.format(value), r, wx.ALIGN_CENTER) - - dc.SetTextForeground(wx.Colour(255, 255, 255)) - dc.DrawLabel(formatStr.format(value), rect, wx.ALIGN_CENTER) - - def OnTimer(self, event): - """ - Handles the ``wx.EVT_TIMER`` event for L{PyfaGauge}. - - :param event: a timer event - """ - oldValue = self._oldPercentage - value = self._percentage - start = 0 - - direction = 1 if oldValue < value else -1 - - end = direction * (value - oldValue) - - self._animDirection = direction - step = self.animEffect(self._animStep, start, end, self._animDuration) - - self._animStep += self._period - - if self._timerId == event.GetId(): - stop_timer = False - - if self._animStep > self._animDuration: - stop_timer = True - - if direction == 1: - if (oldValue + step) < value: - self._animValue = oldValue + step - else: - stop_timer = True - else: - if (oldValue - step) > value: - self._animValue = oldValue - step - - else: - stop_timer = True - - if stop_timer: - self._timer.Stop() - - self.Refresh() diff --git a/gui/resistsEditor.py b/gui/resistsEditor.py index c8bab84bb..b6997e19d 100644 --- a/gui/resistsEditor.py +++ b/gui/resistsEditor.py @@ -20,7 +20,7 @@ # noinspection PyPackageRequirements import wx from service.targetResists import TargetResists -from gui.bitmapLoader import BitmapLoader +from gui.bitmap_loader import BitmapLoader from gui.utils.clipboard import toClipboard, fromClipboard from gui.builtinViews.entityEditor import EntityEditor, BaseValidator from logbook import Logger @@ -49,7 +49,7 @@ class TargetResistsTextValidor(BaseValidator): return True except ValueError as e: pyfalog.error(e) - wx.MessageBox(u"{}".format(e), "Error") + wx.MessageBox("{}".format(e), "Error") textCtrl.SetFocus() return False @@ -87,10 +87,10 @@ class ResistsEditorDlg(wx.Dialog): DAMAGE_TYPES = ("em", "thermal", "kinetic", "explosive") def __init__(self, parent): - wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Target Resists Editor", size=wx.Size(350, 240)) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Target Resists Editor", size=wx.Size(350, 240)) self.block = False - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) @@ -124,7 +124,7 @@ class ResistsEditorDlg(wx.Dialog): setattr(self, "%sEdit" % type_, wx.TextCtrl(self, wx.ID_ANY, "", wx.DefaultPosition, defSize)) editObj = getattr(self, "%sEdit" % type_) resistEditSizer.Add(editObj, 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) - resistEditSizer.Add(wx.StaticText(self, wx.ID_ANY, u"%", wx.DefaultPosition, wx.DefaultSize, 0), 0, + resistEditSizer.Add(wx.StaticText(self, wx.ID_ANY, "%", wx.DefaultPosition, wx.DefaultSize, 0), 0, wx.BOTTOM | wx.TOP | wx.ALIGN_CENTER_VERTICAL, 5) editObj.Bind(wx.EVT_TEXT, self.ValuesUpdated) @@ -138,7 +138,7 @@ class ResistsEditorDlg(wx.Dialog): footerSizer = wx.BoxSizer(wx.HORIZONTAL) perSizer = wx.BoxSizer(wx.VERTICAL) - self.stNotice = wx.StaticText(self, wx.ID_ANY, u"") + self.stNotice = wx.StaticText(self, wx.ID_ANY, "") self.stNotice.Wrap(-1) perSizer.Add(self.stNotice, 0, wx.BOTTOM | wx.TOP | wx.LEFT, 5) @@ -151,7 +151,7 @@ class ResistsEditorDlg(wx.Dialog): mainSizer.Add(contentSizer, 1, wx.EXPAND, 0) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + self.closeBtn = wx.Button(self, wx.ID_ANY, "Close", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) @@ -170,7 +170,7 @@ class ResistsEditorDlg(wx.Dialog): btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s patterns %s clipboard" % (name, direction)) + btn.SetToolTip("%s patterns %s clipboard" % (name, direction)) footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) btn.Bind(wx.EVT_BUTTON, getattr(self, "{}Patterns".format(name.lower()))) diff --git a/gui/setEditor.py b/gui/setEditor.py index d7239254c..caf9d7eaf 100644 --- a/gui/setEditor.py +++ b/gui/setEditor.py @@ -50,7 +50,7 @@ class ImplantTextValidor(BaseValidator): return True except ValueError as e: pyfalog.error(e) - wx.MessageBox(u"{}".format(e), "Error") + wx.MessageBox("{}".format(e), "Error") textCtrl.SetFocus() return False @@ -114,10 +114,10 @@ class ImplantSetEditor(BaseImplantEditorView): class ImplantSetEditorDlg(wx.Dialog): def __init__(self, parent): - wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title=u"Implant Set Editor", size=wx.Size(640, 600)) + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Implant Set Editor", size=wx.Size(640, 600)) self.block = False - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) @@ -135,12 +135,12 @@ class ImplantSetEditorDlg(wx.Dialog): footerSizer = wx.BoxSizer(wx.HORIZONTAL) - self.stNotice = wx.StaticText(self, wx.ID_ANY, u"") + self.stNotice = wx.StaticText(self, wx.ID_ANY, "") self.stNotice.Wrap(-1) footerSizer.Add(self.stNotice, 1, wx.BOTTOM | wx.TOP | wx.LEFT, 5) if "wxGTK" in wx.PlatformInfo: - self.closeBtn = wx.Button(self, wx.ID_ANY, u"Close", wx.DefaultPosition, wx.DefaultSize, 0) + self.closeBtn = wx.Button(self, wx.ID_ANY, "Close", wx.DefaultPosition, wx.DefaultSize, 0) mainSizer.Add(self.closeBtn, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.closeBtn.Bind(wx.EVT_BUTTON, self.closeEvent) @@ -157,7 +157,7 @@ class ImplantSetEditorDlg(wx.Dialog): btn.Layout() setattr(self, name, btn) btn.Enable(True) - btn.SetToolTipString("%s implant sets %s clipboard" % (name, direction)) + btn.SetToolTip("%s implant sets %s clipboard" % (name, direction)) footerSizer.Add(btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_RIGHT) mainSizer.Add(footerSizer, 0, wx.ALL | wx.EXPAND, 5) diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py index 935797718..2b12edd8c 100644 --- a/gui/shipBrowser.py +++ b/gui/shipBrowser.py @@ -11,7 +11,7 @@ from gui.builtinShipBrowser.shipItem import ShipItem from service.fit import Fit from service.market import Market -import gui.builtinShipBrowser.events as events +from gui.builtinShipBrowser.events import EVT_SB_IMPORT_SEL, EVT_SB_STAGE1_SEL, EVT_SB_STAGE2_SEL, EVT_SB_STAGE3_SEL, EVT_SB_SEARCH_SEL from gui.builtinShipBrowser.pfWidgetContainer import PFWidgetsContainer from gui.builtinShipBrowser.navigationPanel import NavigationPanel from gui.builtinShipBrowser.raceSelector import RaceSelector @@ -50,7 +50,7 @@ class ShipBrowser(wx.Panel): if race: self.racesFilter[race] = False - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) @@ -76,11 +76,11 @@ class ShipBrowser(wx.Panel): self.Show() self.Bind(wx.EVT_SIZE, self.SizeRefreshList) - self.Bind(events.EVT_SB_STAGE2_SEL, self.stage2) - self.Bind(events.EVT_SB_STAGE1_SEL, self.stage1) - self.Bind(events.EVT_SB_STAGE3_SEL, self.stage3) - self.Bind(events.EVT_SB_SEARCH_SEL, self.searchStage) - self.Bind(events.EVT_SB_IMPORT_SEL, self.importStage) + self.Bind(EVT_SB_STAGE2_SEL, self.stage2) + self.Bind(EVT_SB_STAGE1_SEL, self.stage1) + self.Bind(EVT_SB_STAGE3_SEL, self.stage3) + self.Bind(EVT_SB_SEARCH_SEL, self.searchStage) + self.Bind(EVT_SB_IMPORT_SEL, self.importStage) self.mainFrame.Bind(GE.FIT_CHANGED, self.RefreshList) @@ -214,12 +214,12 @@ class ShipBrowser(wx.Panel): if ship.race not in racesList: racesList.append(ship.race) - for race, state in self.racesFilter.iteritems(): + for race, state in self.racesFilter.items(): if race in racesList: subRacesFilter[race] = self.racesFilter[race] override = True - for race, state in subRacesFilter.iteritems(): + for race, state in subRacesFilter.items(): if state: override = False break @@ -386,7 +386,7 @@ class ShipBrowser(wx.Panel): self.lpane.AddWidget(FitItem(self.lpane, ID, (shipName, shipTrait, name, booster, timestamp, notes), shipID)) if len(ships) == 0 and len(fitList) == 0: - self.lpane.AddWidget(PFStaticText(self.lpane, label=u"No matching results.")) + self.lpane.AddWidget(PFStaticText(self.lpane, label="No matching results.")) self.lpane.RefreshList(doFocus=False) self.lpane.Thaw() diff --git a/gui/ssoLogin.py b/gui/ssoLogin.py new file mode 100644 index 000000000..2ff364752 --- /dev/null +++ b/gui/ssoLogin.py @@ -0,0 +1,25 @@ +import wx + +class SsoLogin(wx.Dialog): + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="SSO Login", size=wx.Size(400, 240)) + + bSizer1 = wx.BoxSizer(wx.VERTICAL) + + text = wx.StaticText(self, wx.ID_ANY, "Copy and paste the block of text provided by pyfa.io, then click OK") + 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.SetFont(wx.Font(8, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL)) + self.ssoInfoCtrl.Layout() + + bSizer1.Add(self.ssoInfoCtrl, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 10) + + bSizer3 = wx.BoxSizer(wx.VERTICAL) + bSizer3.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.BOTTOM | wx.EXPAND, 10) + + bSizer3.Add(self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL), 0, wx.EXPAND) + bSizer1.Add(bSizer3, 0, wx.ALL | wx.EXPAND, 10) + + self.SetSizer(bSizer1) + self.Center() diff --git a/gui/statsPane.py b/gui/statsPane.py index c186c4531..b3984ccfa 100644 --- a/gui/statsPane.py +++ b/gui/statsPane.py @@ -28,7 +28,7 @@ import gui.globalEvents as GE # import gui.builtinViews.fittingView as fv from gui.statsView import StatsView from gui.contextMenu import ContextMenu -from gui.pyfatogglepanel import TogglePanel +from gui.toggle_panel import TogglePanel from logbook import Logger pyfalog = Logger(__name__) @@ -96,7 +96,7 @@ class StatsPane(wx.Panel): i = 0 for viewName in self.DEFAULT_VIEWS: tp = TogglePanel(self) - contentPanel = tp.GetContentPane() + contentPanel = tp.GetContentPanel() contentPanel.viewName = viewName try: diff --git a/gui/toggle_panel.py b/gui/toggle_panel.py new file mode 100644 index 000000000..2f7421d38 --- /dev/null +++ b/gui/toggle_panel.py @@ -0,0 +1,201 @@ +# =============================================================================== +# TogglePanel is based on PyCollapsiblePane - includes a few improvements +# such as adding items to header, lack of button implementation ("GTK +# expander" style is implemented with plain text with unicode arrows rather +# than drawn geometry), etc. +# +# When adding TogglePanel to sizer, it is important to ensure the following: +# sizer is vertical +# set proportion = 0 +# +# ToDo: Create animations for collapsing / expanding +# +# =============================================================================== + +import wx + + +class TogglePanel (wx.Panel): + def __init__(self, parent, force_layout=False, *args, **kargs): + super().__init__(parent, *args, **kargs) + + self._toggled = True + self.parent = parent + self.force_layout = force_layout + + # Create the main sizer of this panel + self.main_sizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self.main_sizer) + + # Create the header panel, set sizer, and add to main sizer + self.header_panel = wx.Panel(self) + header_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.header_panel.SetSizer(header_sizer) + + self.main_sizer.Add(self.header_panel, 0, wx.EXPAND | wx.TOP | + wx.BOTTOM | wx.RIGHT, 1) + + # Add arrow + self.header_arrow = wx.StaticText(self.header_panel, wx.ID_ANY, + "\u25bc", size=wx.Size((10, -1))) + header_sizer.Add(self.header_arrow, 0, wx.RIGHT, 5) + + # Add header text + self.header_label = wx.StaticText(self.header_panel, wx.ID_ANY, "") + font = parent.GetFont() + font.SetWeight(wx.BOLD) + self.header_label.SetFont(font) + header_sizer.Add(self.header_label, 0, wx.RIGHT, 5) + + # Add a sizer for additional header items if we need it + self.hcontent_sizer = wx.BoxSizer(wx.HORIZONTAL) + header_sizer.Add(self.hcontent_sizer, 0, wx.RIGHT, 5) + + # Create the content panel, set sizer, and add to main sizer + self.content_panel = wx.Panel(self) + self.content_sizer = wx.BoxSizer(wx.VERTICAL) + self.content_panel.SetSizer(self.content_sizer) + + self.main_sizer.Add(self.content_panel, 0, wx.EXPAND | wx.RIGHT | + wx.LEFT, 5) + + self.Layout() + + # Connect Events + self.header_label.Bind(wx.EVT_LEFT_UP, self.ToggleContent) + self.header_arrow.Bind(wx.EVT_LEFT_UP, self.ToggleContent) + self.header_panel.Bind(wx.EVT_LEFT_UP, self.ToggleContent) + + def __del__(self): + pass + + def AddToggleItem(self, item): + item.Bind(wx.EVT_LEFT_UP, self.ToggleContent) + + def GetHeaderContentSizer(self): + return self.hcontent_sizer + + def GetHeaderPanel(self): + return self.header_panel + + def InsertItemInHeader(self, item): + self.hcontent_sizer.Add(item, 0, 0, 0) + self.AddToggleItem(item) + self.Layout() + + def AddSizer(self, sizer): + self.content_sizer.Add(sizer, 0, wx.EXPAND | wx.ALL, 0) + self.Layout() + + def GetContentPanel(self): + return self.content_panel + + def SetLabel(self, label): + self.header_label.SetLabel(label) + + def IsCollapsed(self): + return not self._toggled + + def IsExpanded(self): + return self._toggled + + def OnStateChange(self, sz): + self.SetSize(sz) + + self.parent.GetSizer().SetSizeHints(self.parent) + + if not self._toggled: + if self.parent.GetSizer(): + # we have just set the size hints... + sz = self.parent.GetSizer().CalcMin() + + # use SetClientSize() and not SetSize() otherwise the size for + # e.g. a wxFrame with a menubar wouldn't be correctly set + self.parent.SetClientSize(sz) + else: + self.parent.Layout() + else: + # force our parent to "fit", i.e. expand so that it can honor + # our minimal size + self.parent.Fit() + + def ToggleContent(self, event): + # self.Freeze() + + if self._toggled: + # If we are expanded, save previous size and collapse by setting + # content height to 0 + self.content_min_size = self.content_panel.GetSize() + self.content_panel.SetMinSize((self.content_min_size[0], 0)) + self.header_arrow.SetLabel("\u25b6") + else: + # If we are collapsed, set content size to previously saved value + self.content_panel.SetMinSize(self.content_min_size) + self.header_arrow.SetLabel("\u25bc") + + self._toggled = not self._toggled + + # self.Thaw() + + if self.force_layout: + self.parent.Layout() + else: + self.OnStateChange(self.GetBestSize()) + + +if __name__ == "__main__": + + from wx.lib.inspection import InspectionTool + + class MainPanel(wx.Panel): + def __init__(self, parent): + super().__init__(parent, size=(-1, -1)) + + if 'wxMSW' in wx.PlatformInfo: + color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNFACE) + self.SetBackgroundColour(color) + + main_sizer = wx.BoxSizer(wx.VERTICAL) + + # Generate 3 test panels with different font sizes + for x in range(3): + toggle_panel = TogglePanel(self) + toggle_panel.SetLabel("Test Header") + + content_panel = toggle_panel.GetContentPanel() + content_panel.SetBackgroundColour(wx.WHITE) + + content_sizer = wx.BoxSizer(wx.HORIZONTAL) + header = wx.StaticText(content_panel, -1, "TogglePanel Test") + header.SetFont(wx.Font(10 + (x * 3), wx.SWISS, wx.NORMAL, wx.BOLD)) + content_sizer.Add(header, 0, wx.ALL, 10) + content_panel.SetSizer(content_sizer) + + main_sizer.Add(toggle_panel, 0, wx.EXPAND | wx.ALL, 2) + + self.SetSizer(main_sizer) + + class Frame(wx.Frame): + def __init__(self, title): + super().__init__(None, title=title, size=(500, 500)) + main_sizer = wx.BoxSizer(wx.VERTICAL) + + self.statsPane = MainPanel(self) + main_sizer.Add(self.statsPane, 0, wx.EXPAND) + + self.SetSizerAndFit(main_sizer) + + if not InspectionTool().initialized: + InspectionTool().Init() + + # Find a widget to be selected in the tree. Use either the + # one under the cursor, if any, or this frame. + wnd, _ = wx.FindWindowAtPointer() + if not wnd: + wnd = self + InspectionTool().Show(wnd, True) + + app = wx.App(redirect=False) # Error messages go to popup window + top = Frame("Test Toggle Panel") + top.Show() + app.MainLoop() diff --git a/gui/updateDialog.py b/gui/updateDialog.py index 019901819..c575eeaf3 100644 --- a/gui/updateDialog.py +++ b/gui/updateDialog.py @@ -22,66 +22,63 @@ import wx # noinspection PyPackageRequirements import dateutil.parser from service.settings import UpdateSettings as svc_UpdateSettings +import wx.html2 +import webbrowser +import re +import markdown2 + +# HTML template. We link to a bootstrap cdn for quick and easy css, and include some additional teaks. +html_tmpl = """ + + +

pyfa {0}

+
{1}
+
+{2} +{3} +""" class UpdateDialog(wx.Dialog): - def __init__(self, parent, release): - wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Pyfa Update", pos=wx.DefaultPosition, - size=wx.Size(400, 300), style=wx.DEFAULT_DIALOG_STYLE) + def __init__(self, parent, release, version): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="pyfa Update Available", pos=wx.DefaultPosition, + size=wx.Size(550, 450), style=wx.DEFAULT_DIALOG_STYLE) self.UpdateSettings = svc_UpdateSettings.getInstance() self.releaseInfo = release - self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize) + self.SetSizeHints(wx.DefaultSize, wx.DefaultSize) mainSizer = wx.BoxSizer(wx.VERTICAL) - headSizer = wx.BoxSizer(wx.HORIZONTAL) - - self.headingText = wx.StaticText(self, wx.ID_ANY, "Pyfa Update Available!", wx.DefaultPosition, wx.DefaultSize, - wx.ALIGN_CENTRE) - self.headingText.Wrap(-1) - self.headingText.SetFont(wx.Font(14, 74, 90, 92, False)) - - headSizer.Add(self.headingText, 1, wx.ALL, 5) - mainSizer.Add(headSizer, 0, wx.EXPAND, 5) - - mainSizer.Add(wx.StaticLine(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL), 0, - wx.EXPAND | wx.ALL, 5) - - versionSizer = wx.BoxSizer(wx.HORIZONTAL) - - if self.releaseInfo['prerelease']: - self.releaseText = wx.StaticText(self, wx.ID_ANY, "Pre-release", wx.DefaultPosition, wx.DefaultSize, - wx.ALIGN_RIGHT) - self.releaseText.SetFont(wx.Font(12, 74, 90, 92, False)) - self.releaseText.SetForegroundColour(wx.Colour(230, 0, 0)) - else: - self.releaseText = wx.StaticText(self, wx.ID_ANY, "Stable", wx.DefaultPosition, wx.DefaultSize, - wx.ALIGN_RIGHT) - self.releaseText.SetFont(wx.Font(12, 74, 90, 90, False)) - - self.releaseText.Wrap(-1) - - versionSizer.Add(self.releaseText, 1, wx.ALL, 5) - - self.versionText = wx.StaticText(self, wx.ID_ANY, self.releaseInfo['tag_name'], wx.DefaultPosition, - wx.DefaultSize, wx.ALIGN_LEFT) - self.versionText.Wrap(-1) - self.versionText.SetFont(wx.Font(12, 74, 90, 90, False)) - - versionSizer.Add(self.versionText, 1, wx.ALL, 5) - versionSizer.AddSpacer((15, 5), 0, wx.EXPAND, 5) - - mainSizer.Add(versionSizer, 0, wx.EXPAND, 5) - mainSizer.AddSpacer((0, 5), 0, wx.EXPAND, 5) - releaseDate = dateutil.parser.parse(self.releaseInfo['published_at']) notesSizer = wx.BoxSizer(wx.HORIZONTAL) - self.notesTextCtrl = wx.TextCtrl(self, wx.ID_ANY, str(releaseDate.date()) + ":\n\n" + self.releaseInfo['body'], - wx.DefaultPosition, wx.DefaultSize, - wx.TE_AUTO_URL | wx.TE_MULTILINE | wx.TE_READONLY | wx.DOUBLE_BORDER | wx.TRANSPARENT_WINDOW) + self.browser = wx.html2.WebView.New(self) + self.browser.Bind(wx.html2.EVT_WEBVIEW_NEWWINDOW, self.OnNewWindow) - notesSizer.Add(self.notesTextCtrl, 1, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) + link_patterns = [ + (re.compile("([0-9a-f]{6,40})", re.I), r"https://github.com/pyfa-org/Pyfa/commit/\1"), + (re.compile("#(\d+)", re.I), r"https://github.com/pyfa-org/Pyfa/issues/\1"), + (re.compile("@(\w+)", re.I), r"https://github.com/\1") + ] + + markdowner = markdown2.Markdown( + extras=['cuddled-lists', 'fenced-code-blocks', 'target-blank-links', 'toc', 'link-patterns'], + link_patterns=link_patterns) + + self.browser.SetPage(html_tmpl.format( + self.releaseInfo['tag_name'], + releaseDate.strftime('%B %d, %Y'), + "

This is a pre-release, be prepared for unstable features

" if version.is_prerelease else "", + markdowner.convert(self.releaseInfo['body']) + ),"") + + notesSizer.Add(self.browser, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, 5) mainSizer.Add(notesSizer, 1, wx.EXPAND, 5) self.supressCheckbox = wx.CheckBox(self, wx.ID_ANY, "Don't remind me again for this release", @@ -119,6 +116,10 @@ class UpdateDialog(wx.Dialog): def OnClose(self, e): self.Close() + def OnNewWindow(self, event): + url = event.GetURL() + webbrowser.open(url) + def SuppressChange(self, e): if self.supressCheckbox.IsChecked(): self.UpdateSettings.set('version', self.releaseInfo['tag_name']) diff --git a/gui/utils/animUtils.py b/gui/utils/anim.py similarity index 85% rename from gui/utils/animUtils.py rename to gui/utils/anim.py index 4003e7760..0f3333dc1 100644 --- a/gui/utils/animUtils.py +++ b/gui/utils/anim.py @@ -1,6 +1,6 @@ # noinspection PyPackageRequirements import wx -import gui.utils.colorUtils as colorUtils +import gui.utils.color as colorUtils class LoadAnimation(wx.Window): @@ -24,6 +24,8 @@ class LoadAnimation(wx.Window): self.animTimer.Start(self.animTimerPeriod) + self.SetBackgroundStyle(wx.BG_STYLE_PAINT) + def Play(self): if self.animTimer.IsRunning(): self.animTimer.Stop() @@ -52,13 +54,13 @@ class LoadAnimation(wx.Window): def OnPaint(self, event): rect = self.GetClientRect() - dc = wx.BufferedPaintDC(self) - windowColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW) + dc = wx.AutoBufferedPaintDC(self) + windowColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) dc.SetBackground(wx.Brush(windowColor)) dc.Clear() - barColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) - shadeColor = colorUtils.GetSuitableColor(barColor, 0.75) + barColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) + shadeColor = colorUtils.GetSuitable(barColor, 0.75) barWidth = rect.width / self.bars barHeight = rect.height - self.padding * 2 @@ -72,7 +74,7 @@ class LoadAnimation(wx.Window): bh = barHeight y = self.padding else: - barColor = colorUtils.GetSuitableColor(barColor, float(self.animCount / 2) / 10) + barColor = colorUtils.GetSuitable(barColor, float(self.animCount / 2) / 10) dc.SetPen(wx.Pen(barColor)) dc.SetBrush(wx.Brush(barColor)) bh = rect.height @@ -81,7 +83,7 @@ class LoadAnimation(wx.Window): dc.DrawRectangle(x, y, barWidth, bh) x += barWidth - textColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + textColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) dc.SetTextForeground(textColor) dc.DrawLabel(self.label, rect, wx.ALIGN_CENTER) diff --git a/gui/utils/animEffects.py b/gui/utils/anim_effects.py similarity index 100% rename from gui/utils/animEffects.py rename to gui/utils/anim_effects.py diff --git a/gui/utils/color.py b/gui/utils/color.py new file mode 100644 index 000000000..f89b56d7a --- /dev/null +++ b/gui/utils/color.py @@ -0,0 +1,73 @@ +# noinspection PyPackageRequirements +import wx + + +def Brighten(color, factor): + """ Brightens a Color using a factor between 0 and 1""" + r, g, b, a = color + + factor = min(max(factor, 0), 1) + + r += (255 - r) * factor + b += (255 - b) * factor + g += (255 - g) * factor + + return wx.Colour(r, g, b, a) + + +def Darken(color, factor): + """ Darkens a Color using a factor between 0 and 1""" + r, g, b, a = color + + factor = min(max(factor, 0), 1) + factor = 1 - factor + + r *= factor + g *= factor + b *= factor + + r = min(max(r, 0), 255) + b = min(max(b, 0), 255) + g = min(max(g, 0), 255) + + return wx.Colour(r, g, b, a) + + +def _getBrightness(color): + """ + Calculates brightness of color + http://stackoverflow.com/a/596243/788054 + """ + r, g, b, a = color + return 0.299 * r + 0.587 * g + 0.114 * b + + +def GetSuitable(color, factor: [0, 1]): + """ + Calculates a suitable color based on original color (wx.Colour), its + brightness, and a factor (darken/brighten by factor depending on + calculated brightness) + """ + + brightness = _getBrightness(color) + + if brightness > 129: + return Darken(color, factor) + else: + return Brighten(color, factor) + + +def CalculateTransition(s_color, e_color, delta): + """ + Calculates the color between a given start and end color using a delta + value between 0 and 1 + """ + + sR, sG, sB, sA = s_color + eR, eG, eB, eA = e_color + + tR = sR + (eR - sR) * delta + tG = sG + (eG - sG) * delta + tB = sB + (eB - sB) * delta + + return wx.Colour(tR, tG, tB, (sA + eA) / 2) diff --git a/gui/utils/colorUtils.py b/gui/utils/colorUtils.py deleted file mode 100644 index d2f10336b..000000000 --- a/gui/utils/colorUtils.py +++ /dev/null @@ -1,81 +0,0 @@ -# noinspection PyPackageRequirements -import wx -import math - - -def BrightenColor(color, factor): - # Brightens a color (wx.Colour), factor = [0,1] - - r, g, b = color - a = color.Alpha() - - factor = min(max(factor, 0), 1) - - r += (255 - r) * factor - b += (255 - b) * factor - g += (255 - g) * factor - - return wx.Colour(r, g, b, a) - - -def DarkenColor(color, factor): - # Darkens a color (wx.Colour), factor = [0, 1] - - bkR, bkG, bkB = color - - alpha = color.Alpha() - - factor = min(max(factor, 0), 1) - factor = 1 - factor - - r = float(bkR * factor) - g = float(bkG * factor) - b = float(bkB * factor) - - r = min(max(r, 0), 255) - b = min(max(b, 0), 255) - g = min(max(g, 0), 255) - - return wx.Colour(r, g, b, alpha) - - -def GetBrightnessO1(color): - # Calculates the brightness of a color, different options - - r, g, b = color - return 0.299 * r + 0.587 * g + 0.114 * b - - -def GetBrightnessO2(color): - r, g, b = color - return math.sqrt(0.241 * r * r + 0.691 * g * g + 0.068 * b * b) - - -def GetSuitableColor(color, factor): - # Calculates a suitable color based on original color (wx.Colour), its brightness, a factor=[0,1] (darken/brighten by factor depending on calculated brightness) - - brightness = GetBrightnessO1(color) - - if brightness > 129: - return DarkenColor(color, factor) - else: - return BrightenColor(color, factor) - - -def CalculateTransitionColor(startColor, endColor, delta): - """ - Calculates the color between a given start and end colors, delta = [0,1] - Colors are wx.Colour objects - """ - - sR, sG, sB = startColor - eR, eG, eB = endColor - - alphaS = startColor.Alpha() - alphaE = endColor.Alpha() - - tR = sR + (eR - sR) * delta - tG = sG + (eG - sG) * delta - tB = sB + (eB - sB) * delta - - return wx.Colour(tR, tG, tB, (alphaS + alphaE) / 2) diff --git a/gui/utils/draw.py b/gui/utils/draw.py new file mode 100644 index 000000000..992dbb9fb --- /dev/null +++ b/gui/utils/draw.py @@ -0,0 +1,90 @@ +# noinspection PyPackageRequirements +import wx +from . import color + + +def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor=None , fillRatio=2): + + if sFactor == 0 and eFactor == 0 and mFactor is None: + return DrawFilledBitmap(width, height, windowColor) + + gStart = color.GetSuitable(windowColor, sFactor) + + if mFactor: + gMid = color.GetSuitable(windowColor, mFactor) + else: + gMid = color.GetSuitable(windowColor, sFactor + (eFactor - sFactor) / 2) + + gEnd = color.GetSuitable(windowColor, eFactor) + + return DrawGradientBar(width, height, gStart, gEnd, gMid, fillRatio) + + +def DrawFilledBitmap(width, height, color): + canvas = wx.Bitmap(width, height) + + mdc = wx.MemoryDC() + mdc.SelectObject(canvas) + + mdc.SetBackground(wx.Brush(color)) + mdc.Clear() + + mdc.SelectObject(wx.NullBitmap) + + return canvas + + +def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4): + canvas = wx.Bitmap(width, height) + + mdc = wx.MemoryDC() + mdc.SelectObject(canvas) + + r = wx.Rect(0, 0, width, height) + r.SetHeight(height / fillRatio) + + if gMid is None: + gMid = gStart + + mdc.GradientFillLinear(r, gStart, gMid, wx.SOUTH) + r.SetTop(r.GetHeight()) + r.SetHeight(height * (fillRatio - 1) / fillRatio + (1 if height % fillRatio != 0 else 0)) + + mdc.GradientFillLinear(r, gMid, gEnd, wx.SOUTH) + + mdc.SelectObject(wx.NullBitmap) + + return canvas + + +def GetPartialText(dc, text , maxWidth, defEllipsis="..."): + ellipsis = defEllipsis + base_w, h = dc.GetTextExtent(ellipsis) + + lenText = len(text) + drawntext = text + w, dummy = dc.GetTextExtent(text) + + while lenText > 0: + + if w + base_w <= maxWidth: + break + + w_c, h_c = dc.GetTextExtent(drawntext[-1]) + drawntext = drawntext[0:-1] + lenText -= 1 + w -= w_c + + while len(ellipsis) > 0 and w + base_w > maxWidth: + ellipsis = ellipsis[0:-1] + base_w, h = dc.GetTextExtent(ellipsis) + if len(text) > lenText: + return drawntext + ellipsis + else: + return text + + +def CreateDropShadowBitmap(bitmap, opacity): + img = bitmap.ConvertToImage() + img = img.AdjustChannels(0, 0, 0, opacity) + return wx.Bitmap(img) diff --git a/gui/utils/drawUtils.py b/gui/utils/drawUtils.py deleted file mode 100644 index d7034ece7..000000000 --- a/gui/utils/drawUtils.py +++ /dev/null @@ -1,111 +0,0 @@ -# noinspection PyPackageRequirements -import wx -import gui.utils.colorUtils as colorUtils - - -def RenderGradientBar(windowColor, width, height, sFactor, eFactor, mFactor=None, fillRatio=2): - if sFactor == 0 and eFactor == 0 and mFactor is None: - return DrawFilledBitmap(width, height, windowColor) - - gStart = colorUtils.GetSuitableColor(windowColor, sFactor) - - if mFactor: - gMid = colorUtils.GetSuitableColor(windowColor, mFactor) - else: - gMid = colorUtils.GetSuitableColor(windowColor, sFactor + (eFactor - sFactor) / 2) - - gEnd = colorUtils.GetSuitableColor(windowColor, eFactor) - - return DrawGradientBar(width, height, gStart, gEnd, gMid, fillRatio) - - -def DrawFilledBitmap(width, height, color): - canvas = wx.EmptyBitmap(width, height) - - mdc = wx.MemoryDC() - mdc.SelectObject(canvas) - - mdc.SetBackground(wx.Brush(color)) - mdc.Clear() - - mdc.SelectObject(wx.NullBitmap) - - return canvas - - -# noinspection PyPropertyAccess -def DrawGradientBar(width, height, gStart, gEnd, gMid=None, fillRatio=4): - # we need to have dimensions to draw - # assert width > 0 and height > 0 - canvas = wx.EmptyBitmap(width, height) - - mdc = wx.MemoryDC() - mdc.SelectObject(canvas) - - r = wx.Rect(0, 0, width, height) - r.height = height / fillRatio - - if gMid is None: - gMid = gStart - - mdc.GradientFillLinear(r, gStart, gMid, wx.SOUTH) - r.top = r.height - r.height = height * (fillRatio - 1) / fillRatio + (1 if height % fillRatio != 0 else 0) - - mdc.GradientFillLinear(r, gMid, gEnd, wx.SOUTH) - - mdc.SelectObject(wx.NullBitmap) - - return canvas - - -def GetPartialText(dc, text, maxWidth, defEllipsis="..."): - ellipsis = defEllipsis - base_w, h = dc.GetTextExtent(ellipsis) - - lenText = len(text) - drawntext = text - w, dummy = dc.GetTextExtent(text) - - while lenText > 0: - - if w + base_w <= maxWidth: - break - - w_c, h_c = dc.GetTextExtent(drawntext[-1]) - drawntext = drawntext[0:-1] - lenText -= 1 - w -= w_c - - while len(ellipsis) > 0 and w + base_w > maxWidth: - ellipsis = ellipsis[0:-1] - base_w, h = dc.GetTextExtent(ellipsis) - if len(text) > lenText: - return drawntext + ellipsis - else: - return text - - -def GetRoundBitmap(w, h, r): - maskColor = wx.Color(0, 0, 0) - shownColor = wx.Color(5, 5, 5) - b = wx.EmptyBitmap(w, h) - dc = wx.MemoryDC(b) - dc.SetBrush(wx.Brush(maskColor)) - dc.DrawRectangle(0, 0, w, h) - dc.SetBrush(wx.Brush(shownColor)) - dc.SetPen(wx.Pen(shownColor)) - dc.DrawRoundedRectangle(0, 0, w, h, r) - dc.SelectObject(wx.NullBitmap) - b.SetMaskColour(maskColor) - return b - - -def GetRoundShape(w, h, r): - return wx.RegionFromBitmap(GetRoundBitmap(w, h, r)) - - -def CreateDropShadowBitmap(bitmap, opacity): - img = wx.ImageFromBitmap(bitmap) - img = img.AdjustChannels(0, 0, 0, opacity) - return wx.BitmapFromImage(img) diff --git a/gui/utils/exportHtml.py b/gui/utils/exportHtml.py index ba075a35f..612df367e 100644 --- a/gui/utils/exportHtml.py +++ b/gui/utils/exportHtml.py @@ -63,11 +63,13 @@ class exportHtmlThread(threading.Thread): HTML = self.generateFullHTML(sMkt, sFit, dnaUrl) try: - FILE = open(settings.getPath(), "w") - FILE.write(HTML.encode('utf-8')) + FILE = open(settings.getPath(), "w", encoding='utf-8') + FILE.write(HTML) FILE.close() - except IOError: - print("Failed to write to " + settings.getPath()) + except IOError as ex: + print(("Failed to write to " + settings.getPath())) + pass + except Exception as ex: pass if self.callback: @@ -84,6 +86,7 @@ class exportHtmlThread(threading.Thread): Pyfa Fittings +