mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-06-07 19:03:23 +03:00
Merge branch 'releases' into stable
This commit is contained in:
commit
4883c1606b
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -12,13 +12,13 @@ assignees: ''
|
|||||||
**Please also check if the issue is covered in our [Troubleshooting Guide](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting).** It might be something with a known work around, or where a third party (such as EDSM) is causing logging that is harmless.
|
**Please also check if the issue is covered in our [Troubleshooting Guide](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting).** It might be something with a known work around, or where a third party (such as EDSM) is causing logging that is harmless.
|
||||||
|
|
||||||
**Please complete the following information:**
|
**Please complete the following information:**
|
||||||
- Version [e.g. 4.0.6 - See 'Help > About E:D Market Connector']
|
- Version [e.g. 4.0.6 - See 'Help > About E:D Market Connector'. If running from source using git then please paste the output of `git log --decorate=full | head -1`]
|
||||||
- OS: [e.g. Windows 10]
|
- OS: [e.g. Windows 10, Linux Debian 10.6, etc.]
|
||||||
- OS Locale: [e.g. English, French, Serbian...]
|
- OS Locale: [e.g. English, French, Serbian...]
|
||||||
- If applicable: Browser [e.g. chrome, safari]
|
- If applicable: Browser [e.g. chrome, safari]
|
||||||
- Please attach log files:
|
- Please attach **BOTH** log files as follows:
|
||||||
1. Always attach `%TEMP%\EDMarketConnector.log` from *immediately* after the bug occurs (re-running the application overwrites this file).
|
1. `%TEMP%\EDMarketConnector.log` from *immediately* after the bug occurs (re-running the application overwrites this file).
|
||||||
1. If running 4.1.0 (including betas) or later: `%TEMP%\EDMarketConnector\EDMarketConnector-debug.log`. See [Debug Log File](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#debug-log-files).
|
1. %TEMP%\EDMarketConnector\EDMarketConnector-debug.log`. See [Debug Log File](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#debug-log-files). NB: If you don't have this log file then you're not running the latest version of the application and should update first to see if we already fixed the bug you're reporting.
|
||||||
|
|
||||||
Use the Icon that looks like `_`, `|` and `^` all in one character towards the right of the toolbar above.
|
Use the Icon that looks like `_`, `|` and `^` all in one character towards the right of the toolbar above.
|
||||||
|
|
||||||
|
44
.github/workflows/windows-build.yml
vendored
Normal file
44
.github/workflows/windows-build.yml
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
name: Build EDMC for Windows
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Build EDMC
|
||||||
|
runs-on: windows-2019
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: powershell
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: "3.7.9"
|
||||||
|
architecture: "x86"
|
||||||
|
|
||||||
|
- name: Install python tools
|
||||||
|
run: |
|
||||||
|
pip install wheel
|
||||||
|
pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
- name: Download winsparkle
|
||||||
|
run: |
|
||||||
|
Invoke-Webrequest -UseBasicParsing https://github.com/vslavik/winsparkle/releases/download/v0.7.0/WinSparkle-0.7.0.zip -OutFile out.zip
|
||||||
|
Expand-Archive out.zip
|
||||||
|
Move-Item 'out\WinSparkle-0.7.0\Release\*' '.\'
|
||||||
|
|
||||||
|
- name: Build EDMC
|
||||||
|
run: |
|
||||||
|
python setup.py py2exe
|
||||||
|
|
||||||
|
- name: Upload build files
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: Built files
|
||||||
|
path: EDMarketConnector_win*.msi
|
56
ChangeLog.md
56
ChangeLog.md
@ -1,6 +1,62 @@
|
|||||||
This is the master changelog for Elite Dangerous Market Connector. Entries are in reverse chronological order (latest first).
|
This is the master changelog for Elite Dangerous Market Connector. Entries are in reverse chronological order (latest first).
|
||||||
---
|
---
|
||||||
|
|
||||||
|
Release 4.1.5
|
||||||
|
===
|
||||||
|
|
||||||
|
This is a minor maintenance release, mostly addressing behaviour around
|
||||||
|
process shutdown and startup, along with a couple of small enhancements that
|
||||||
|
most users won't notice.
|
||||||
|
|
||||||
|
* If there is already an EDMarketConnector.exe process running when trying
|
||||||
|
to run another instance then that new process will no longer exit silently.
|
||||||
|
Instead you'll get a pop-up telling you it's detected another process, and
|
||||||
|
you need to close that pop-up in order for this additional process to then
|
||||||
|
exit.
|
||||||
|
|
||||||
|
This hopefully makes it obvious when you've got a hung EDMarketConnect.exe
|
||||||
|
process that you need to kill in order to re-run the program.
|
||||||
|
|
||||||
|
* In order to gather more information about how and why EDMarketConnector.exe
|
||||||
|
sometimes doesn't shutdown properly we've added some extra debug logging to
|
||||||
|
the sequence of clean-up calls performed during shutdown.
|
||||||
|
|
||||||
|
Also, to make it more obvious if the process has hung during shutdown the
|
||||||
|
UI window is no longer hidden at the start of this shutdown sequence. It
|
||||||
|
will instead linger, with "Shutting down..." showing in the status line
|
||||||
|
(translation for this small phrase will be added in a later release).
|
||||||
|
|
||||||
|
If you encounter this shutdown hang then please add a comment to
|
||||||
|
[Application can leave a zombie process on shutdown #678](https://github.com/EDCD/EDMarketConnector/issues/678)
|
||||||
|
to help us track down the cause and fix it.
|
||||||
|
|
||||||
|
* Cater for 'mangled name' class functions in our logging code. e.g. where
|
||||||
|
you name a class member with a `__` prefix in order to 'hide' it from
|
||||||
|
out-of-class code.
|
||||||
|
|
||||||
|
* To help track down the cause of [Crashing On Startup #798](https://github.com/EDCD/EDMarketConnector/issues/798)
|
||||||
|
we've added some exception catching in our logging code. If this is
|
||||||
|
triggered you will see `??:??` in logging output, instead of class and/or
|
||||||
|
function names.
|
||||||
|
|
||||||
|
If you encounter this then please comment on that bug report to aid us in
|
||||||
|
tracking down the root cause!
|
||||||
|
|
||||||
|
* Fixed logging from EDMC.exe so that the -debug log goes into `EDMC-debug.log`
|
||||||
|
not `EDMarketConnector-debug.log`.
|
||||||
|
|
||||||
|
* Fix `EDMC.exe -j` handling of file encodings. NB: This command-line
|
||||||
|
argument isn't listed on `EDMC.exe -h` as it's intended for developer use
|
||||||
|
only.
|
||||||
|
|
||||||
|
* Fix the name of 'Void Opal(s)' so that output of market data to files is
|
||||||
|
correct.
|
||||||
|
|
||||||
|
* Fix URL in PLUGINS.md to refer to `main`, not `master` branch.
|
||||||
|
|
||||||
|
* We're able to pull `py2exe` from PyPi now, so docs/Releasing.md has been
|
||||||
|
update to reflect this.
|
||||||
|
|
||||||
Release 4.1.4
|
Release 4.1.4
|
||||||
===
|
===
|
||||||
|
|
||||||
|
25
EDMC.py
25
EDMC.py
@ -12,16 +12,20 @@ from os.path import getmtime, join
|
|||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
from typing import TYPE_CHECKING, Any, Optional
|
from typing import TYPE_CHECKING, Any, Optional
|
||||||
|
|
||||||
# See EDMCLogging.py docs.
|
|
||||||
# isort: off
|
# isort: off
|
||||||
|
|
||||||
|
os.environ["EDMC_NO_UI"] = "1"
|
||||||
|
|
||||||
|
# See EDMCLogging.py docs.
|
||||||
|
# workaround for https://github.com/EDCD/EDMarketConnector/issues/568
|
||||||
from EDMCLogging import edmclogger, logger, logging
|
from EDMCLogging import edmclogger, logger, logging
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from logging import trace, TRACE # type: ignore # noqa: F401
|
from logging import trace, TRACE # type: ignore # noqa: F401
|
||||||
# isort: on
|
|
||||||
edmclogger.set_channels_loglevel(logging.INFO)
|
edmclogger.set_channels_loglevel(logging.INFO)
|
||||||
|
|
||||||
# workaround for https://github.com/EDCD/EDMarketConnector/issues/568
|
# isort: on
|
||||||
os.environ["EDMC_NO_UI"] = "1"
|
|
||||||
|
|
||||||
import collate
|
import collate
|
||||||
import commodity
|
import commodity
|
||||||
@ -169,7 +173,16 @@ sys.path: {sys.path}'''
|
|||||||
if args.j:
|
if args.j:
|
||||||
logger.debug('Import and collate from JSON dump')
|
logger.debug('Import and collate from JSON dump')
|
||||||
# Import and collate from JSON dump
|
# Import and collate from JSON dump
|
||||||
data = json.load(open(args.j))
|
#
|
||||||
|
# Try twice, once with the system locale and once enforcing utf-8. If the file was made on the current
|
||||||
|
# system, chances are its the current locale, and not utf-8. Otherwise if it was copied, its probably
|
||||||
|
# utf8. Either way, try the system FIRST because reading something like cp1251 in UTF-8 results in garbage
|
||||||
|
# but the reverse results in an exception.
|
||||||
|
try:
|
||||||
|
data = json.load(open(args.j))
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
data = json.load(open(args.j, encoding='utf-8'))
|
||||||
|
|
||||||
config.set('querytime', int(getmtime(args.j)))
|
config.set('querytime', int(getmtime(args.j)))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -184,7 +197,7 @@ sys.path: {sys.path}'''
|
|||||||
logfile = join(logdir, logfiles[-1])
|
logfile = join(logdir, logfiles[-1])
|
||||||
|
|
||||||
logger.debug(f'Using logfile "{logfile}"')
|
logger.debug(f'Using logfile "{logfile}"')
|
||||||
with open(logfile, 'r') as loghandle:
|
with open(logfile, 'r', encoding='utf-8') as loghandle:
|
||||||
for line in loghandle:
|
for line in loghandle:
|
||||||
try:
|
try:
|
||||||
monitor.parse_entry(line)
|
monitor.parse_entry(line)
|
||||||
|
@ -284,7 +284,25 @@ class EDMCContextFilter(logging.Filter):
|
|||||||
caller_qualname = caller_class_names = ''
|
caller_qualname = caller_class_names = ''
|
||||||
if frame:
|
if frame:
|
||||||
# <https://stackoverflow.com/questions/2203424/python-how-to-retrieve-class-information-from-a-frame-object#2220759>
|
# <https://stackoverflow.com/questions/2203424/python-how-to-retrieve-class-information-from-a-frame-object#2220759>
|
||||||
frame_info = inspect.getframeinfo(frame)
|
try:
|
||||||
|
frame_info = inspect.getframeinfo(frame)
|
||||||
|
# raise(IndexError) # TODO: Remove, only for testing
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
# Separate from the print below to guarantee we see at least this much.
|
||||||
|
print('EDMCLogging:EDMCContextFilter:caller_attributes(): Failed in `inspect.getframinfo(frame)`')
|
||||||
|
|
||||||
|
# We want to *attempt* to show something about the nature of 'frame',
|
||||||
|
# but at this point we can't trust it will work.
|
||||||
|
try:
|
||||||
|
print(f'frame: {frame}')
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# We've given up, so just return all '??' to signal we couldn't get the info
|
||||||
|
return '??', '??', '??'
|
||||||
|
|
||||||
args, _, _, value_dict = inspect.getargvalues(frame)
|
args, _, _, value_dict = inspect.getargvalues(frame)
|
||||||
if len(args) and args[0] in ('self', 'cls'):
|
if len(args) and args[0] in ('self', 'cls'):
|
||||||
frame_class: 'object' = value_dict[args[0]]
|
frame_class: 'object' = value_dict[args[0]]
|
||||||
|
@ -17,8 +17,117 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
from config import applongname, appname, appversion, appversion_nobuild, config, copyright
|
from config import applongname, appname, appversion, appversion_nobuild, config, copyright
|
||||||
|
|
||||||
# TODO: Test: Make *sure* this redirect is working, else py2exe is going to cause an exit popup
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
def no_other_instance_running() -> bool: # noqa: CCR001
|
||||||
|
"""
|
||||||
|
Ensure only one copy of the app is running under this user account.
|
||||||
|
|
||||||
|
OSX does this automatically.
|
||||||
|
|
||||||
|
:returns: True if we are the single instance, else False.
|
||||||
|
"""
|
||||||
|
# TODO: Linux implementation
|
||||||
|
if platform == 'win32':
|
||||||
|
import ctypes
|
||||||
|
from ctypes.wintypes import BOOL, HWND, INT, LPARAM, LPCWSTR, LPWSTR
|
||||||
|
|
||||||
|
EnumWindows = ctypes.windll.user32.EnumWindows # noqa: N806
|
||||||
|
GetClassName = ctypes.windll.user32.GetClassNameW # noqa: N806
|
||||||
|
GetClassName.argtypes = [HWND, LPWSTR, ctypes.c_int]
|
||||||
|
GetWindowText = ctypes.windll.user32.GetWindowTextW # noqa: N806
|
||||||
|
GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int]
|
||||||
|
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW # noqa: N806
|
||||||
|
GetProcessHandleFromHwnd = ctypes.windll.oleacc.GetProcessHandleFromHwnd # noqa: N806
|
||||||
|
|
||||||
|
SW_RESTORE = 9 # noqa: N806
|
||||||
|
SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow # noqa: N806
|
||||||
|
ShowWindow = ctypes.windll.user32.ShowWindow # noqa: N806
|
||||||
|
ShowWindowAsync = ctypes.windll.user32.ShowWindowAsync # noqa: N806
|
||||||
|
|
||||||
|
COINIT_MULTITHREADED = 0 # noqa: N806,F841
|
||||||
|
COINIT_APARTMENTTHREADED = 0x2 # noqa: N806
|
||||||
|
COINIT_DISABLE_OLE1DDE = 0x4 # noqa: N806
|
||||||
|
CoInitializeEx = ctypes.windll.ole32.CoInitializeEx # noqa: N806
|
||||||
|
|
||||||
|
ShellExecute = ctypes.windll.shell32.ShellExecuteW # noqa: N806
|
||||||
|
ShellExecute.argtypes = [HWND, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, INT]
|
||||||
|
|
||||||
|
def window_title(h):
|
||||||
|
if h:
|
||||||
|
text_length = GetWindowTextLength(h) + 1
|
||||||
|
buf = ctypes.create_unicode_buffer(text_length)
|
||||||
|
if GetWindowText(h, buf, text_length):
|
||||||
|
return buf.value
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
@ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM)
|
||||||
|
def enumwindowsproc(window_handle, l_param):
|
||||||
|
# class name limited to 256 - https://msdn.microsoft.com/en-us/library/windows/desktop/ms633576
|
||||||
|
cls = ctypes.create_unicode_buffer(257)
|
||||||
|
if GetClassName(window_handle, cls, 257) \
|
||||||
|
and cls.value == 'TkTopLevel' \
|
||||||
|
and window_title(window_handle) == applongname \
|
||||||
|
and GetProcessHandleFromHwnd(window_handle):
|
||||||
|
# If GetProcessHandleFromHwnd succeeds then the app is already running as this user
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1].startswith(protocolhandler.redirect):
|
||||||
|
CoInitializeEx(0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
|
||||||
|
# Wait for it to be responsive to avoid ShellExecute recursing
|
||||||
|
ShowWindow(window_handle, SW_RESTORE)
|
||||||
|
ShellExecute(0, None, sys.argv[1], None, None, SW_RESTORE)
|
||||||
|
|
||||||
|
else:
|
||||||
|
ShowWindowAsync(window_handle, SW_RESTORE)
|
||||||
|
SetForegroundWindow(window_handle)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return EnumWindows(enumwindowsproc, 0)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def already_running_popup():
|
||||||
|
"""Create the "already running" popup."""
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk
|
||||||
|
|
||||||
|
root = tk.Tk(className=appname.lower())
|
||||||
|
|
||||||
|
frame = tk.Frame(root)
|
||||||
|
frame.grid(row=1, column=0, sticky=tk.NSEW)
|
||||||
|
|
||||||
|
label = tk.Label(frame)
|
||||||
|
label['text'] = 'An EDMarketConnector.exe process was already running, exiting.'
|
||||||
|
label.grid(row=1, column=0, sticky=tk.NSEW)
|
||||||
|
|
||||||
|
button = ttk.Button(frame, text='OK', command=lambda: sys.exit(0))
|
||||||
|
button.grid(row=2, column=0, sticky=tk.S)
|
||||||
|
|
||||||
|
root.mainloop()
|
||||||
|
|
||||||
|
if not no_other_instance_running():
|
||||||
|
# There's a copy already running. We want to inform the user by
|
||||||
|
# **appending** to the log file, not truncating it.
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
# By default py2exe tries to write log to dirname(sys.executable) which fails when installed
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
# unbuffered not allowed for text in python3, so use `1 for line buffering
|
||||||
|
sys.stdout = sys.stderr = open(join(tempfile.gettempdir(), f'{appname}.log'), mode='a', buffering=1)
|
||||||
|
|
||||||
|
# Logging isn't set up yet.
|
||||||
|
# We'll keep this print, but it will be over-written by any subsequent
|
||||||
|
# write by the already-running process.
|
||||||
|
print("An EDMarketConnector.exe process was already running, exiting.")
|
||||||
|
|
||||||
|
# To be sure the user knows, we need a popup
|
||||||
|
already_running_popup()
|
||||||
|
# If the user closes the popup with the 'X', not the 'OK' button we'll
|
||||||
|
# reach here.
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
# Keep this as the very first code run to be as sure as possible of no
|
# Keep this as the very first code run to be as sure as possible of no
|
||||||
# output until after this redirect is done, if needed.
|
# output until after this redirect is done, if needed.
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
@ -36,6 +145,9 @@ if TYPE_CHECKING:
|
|||||||
from logging import trace, TRACE # type: ignore # noqa: F401
|
from logging import trace, TRACE # type: ignore # noqa: F401
|
||||||
# isort: on
|
# isort: on
|
||||||
|
|
||||||
|
def _(x: str) -> str:
|
||||||
|
"""Fake the l10n translation functions for typing."""
|
||||||
|
return x
|
||||||
|
|
||||||
if getattr(sys, 'frozen', False):
|
if getattr(sys, 'frozen', False):
|
||||||
# Under py2exe sys.path[0] is the executable name
|
# Under py2exe sys.path[0] is the executable name
|
||||||
@ -947,18 +1059,43 @@ class AppWindow(object):
|
|||||||
if platform != 'darwin' or self.w.winfo_rooty() > 0:
|
if platform != 'darwin' or self.w.winfo_rooty() > 0:
|
||||||
x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267'
|
x, y = self.w.geometry().split('+')[1:3] # e.g. '212x170+2881+1267'
|
||||||
config.set('geometry', f'+{x}+{y}')
|
config.set('geometry', f'+{x}+{y}')
|
||||||
self.w.withdraw() # Following items can take a few seconds, so hide the main window while they happen
|
|
||||||
|
# Let the user know we're shutting down.
|
||||||
|
self.status['text'] = _('Shutting down...')
|
||||||
|
self.w.update_idletasks()
|
||||||
|
logger.info('Starting shutdown procedures...')
|
||||||
|
|
||||||
|
logger.info('Closing protocol handler...')
|
||||||
protocolhandler.close()
|
protocolhandler.close()
|
||||||
|
|
||||||
|
logger.info('Unregistering hotkey manager...')
|
||||||
hotkeymgr.unregister()
|
hotkeymgr.unregister()
|
||||||
|
|
||||||
|
logger.info('Closing dashboard...')
|
||||||
dashboard.close()
|
dashboard.close()
|
||||||
|
|
||||||
|
logger.info('Closing journal monitor...')
|
||||||
monitor.close()
|
monitor.close()
|
||||||
|
|
||||||
|
logger.info('Notifying plugins to stop...')
|
||||||
plug.notify_stop()
|
plug.notify_stop()
|
||||||
|
|
||||||
|
logger.info('Closing update checker...')
|
||||||
self.updater.close()
|
self.updater.close()
|
||||||
|
|
||||||
|
logger.info('Closing Frontier CAPI sessions...')
|
||||||
companion.session.close()
|
companion.session.close()
|
||||||
|
|
||||||
|
logger.info('Closing config...')
|
||||||
config.close()
|
config.close()
|
||||||
|
|
||||||
|
logger.info('Destroying app window...')
|
||||||
self.w.destroy()
|
self.w.destroy()
|
||||||
|
|
||||||
def drag_start(self, event):
|
logger.info('Done.')
|
||||||
|
|
||||||
|
def drag_start(self, event) -> None:
|
||||||
|
"""Initiate dragging the window."""
|
||||||
self.drag_offset = (event.x_root - self.w.winfo_rootx(), event.y_root - self.w.winfo_rooty())
|
self.drag_offset = (event.x_root - self.w.winfo_rootx(), event.y_root - self.w.winfo_rooty())
|
||||||
|
|
||||||
def drag_continue(self, event):
|
def drag_continue(self, event):
|
||||||
|
@ -454,6 +454,9 @@
|
|||||||
/* Setting to decide which ship outfitting website to link to - either E:D Shipyard or Coriolis. [prefs.py] */
|
/* Setting to decide which ship outfitting website to link to - either E:D Shipyard or Coriolis. [prefs.py] */
|
||||||
"Shipyard" = "Shipyard";
|
"Shipyard" = "Shipyard";
|
||||||
|
|
||||||
|
/* Status line text that appears when process exit sequence starts [EDMarketConnector.py] */
|
||||||
|
"Shutting down..." = "Shutting down...";
|
||||||
|
|
||||||
/* Empire rank. [stats.py] */
|
/* Empire rank. [stats.py] */
|
||||||
"Squire" = "Squire";
|
"Squire" = "Squire";
|
||||||
|
|
||||||
|
@ -21,6 +21,13 @@ Plugins are python files. The plugin folder must have a file named `load.py`
|
|||||||
that must provide one module level function and optionally provide a few
|
that must provide one module level function and optionally provide a few
|
||||||
others.
|
others.
|
||||||
|
|
||||||
|
If you're running from source (which allows for debugging with e.g. [PyCharm](https://www.jetbrains.com/pycharm/features/))
|
||||||
|
then you'll need to be using an appropriate version of Python. The current
|
||||||
|
version is listed in the [Environment section of Releasing.md](https://github.com/EDCD/EDMarketConnector/blob/main/docs/Releasing.md#environment).
|
||||||
|
If you're developing your plugin simply against an install of EDMarketConnector
|
||||||
|
then you'll be relying on the bundled version of Python (it's baked
|
||||||
|
into the .exe via the py2exe build process).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
@ -351,7 +358,7 @@ threads - doing so may cause the app to crash intermittently. You can signal
|
|||||||
back to the main thread using Tkinter's `event_generate()` widget method,
|
back to the main thread using Tkinter's `event_generate()` widget method,
|
||||||
generating a user-defined event that you have previously registered with the
|
generating a user-defined event that you have previously registered with the
|
||||||
[`bind_all()`](http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm)
|
[`bind_all()`](http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm)
|
||||||
widget method. See the [EDSM plugin](https://github.com/Marginal/EDMarketConnector/blob/master/plugins/edsm.py)
|
widget method. See the [EDSM plugin](https://github.com/Marginal/EDMarketConnector/blob/main/plugins/edsm.py)
|
||||||
for an example of these techniques.
|
for an example of these techniques.
|
||||||
|
|
||||||
#### Journal Entry
|
#### Journal Entry
|
||||||
|
@ -215,6 +215,6 @@ id,symbol,category,name
|
|||||||
128924329,Benitoite,Minerals,Benitoite
|
128924329,Benitoite,Minerals,Benitoite
|
||||||
128924330,Grandidierite,Minerals,Grandidierite
|
128924330,Grandidierite,Minerals,Grandidierite
|
||||||
128924331,Alexandrite,Minerals,Alexandrite
|
128924331,Alexandrite,Minerals,Alexandrite
|
||||||
128924332,Opal,Minerals,Void Opals
|
128924332,Opal,Minerals,Void Opal
|
||||||
128924333,RockforthFertiliser,Chemicals,Rockforth Fertiliser
|
128924333,RockforthFertiliser,Chemicals,Rockforth Fertiliser
|
||||||
128924334,AgronomicTreatment,Chemicals,Agronomic Treatment
|
128924334,AgronomicTreatment,Chemicals,Agronomic Treatment
|
||||||
|
|
@ -13,7 +13,7 @@ appcmdname = 'EDMC'
|
|||||||
# appversion **MUST** follow Semantic Versioning rules:
|
# appversion **MUST** follow Semantic Versioning rules:
|
||||||
# <https://semver.org/#semantic-versioning-specification-semver>
|
# <https://semver.org/#semantic-versioning-specification-semver>
|
||||||
# Major.Minor.Patch(-prerelease)(+buildmetadata)
|
# Major.Minor.Patch(-prerelease)(+buildmetadata)
|
||||||
appversion = '4.1.4' #-rc1+a872b5f'
|
appversion = '4.1.5' #-rc1+a872b5f'
|
||||||
# For some things we want appversion without (possible) +build metadata
|
# For some things we want appversion without (possible) +build metadata
|
||||||
appversion_nobuild = str(semantic_version.Version(appversion).truncate('prerelease'))
|
appversion_nobuild = str(semantic_version.Version(appversion).truncate('prerelease'))
|
||||||
copyright = u'© 2015-2019 Jonathan Harris, 2020 EDCD'
|
copyright = u'© 2015-2019 Jonathan Harris, 2020 EDCD'
|
||||||
|
28
docs/Automatic Builds.md
Normal file
28
docs/Automatic Builds.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Introduction
|
||||||
|
|
||||||
|
Instead of building manually as laid out by [Releasing](https://github.com/EDCD/EDMarketConnector/blob/main/docs/Releasing.md), you can build the EDMC installer using GitHub actions.
|
||||||
|
|
||||||
|
## Initiating a workflow run
|
||||||
|
|
||||||
|
Starting a workflow run is done from the Actions tab at the top of the main GitHub UI
|
||||||
|
|
||||||
|
NB: The branch you want to build must have the workflow file (`.github/workflows/windows-build.yml`), and the version of the file in that branch is the version that will be used for the build (e.g. for different python versions)
|
||||||
|
|
||||||
|
1. Select the Actions tab at the top of the main GitHub interface
|
||||||
|
2. Select the `Build EDMC for Windows` workflow on the left
|
||||||
|
3. Click the "Run workflow" button at the right side of the blue banner
|
||||||
|
1. Select the branch you want to build
|
||||||
|
2. Click the "Run Workflow"
|
||||||
|
|
||||||
|
## Downloading built installer files
|
||||||
|
|
||||||
|
When the workflow is (successfully) completed, it will upload the msi file it built as a "Workflow Artifact". You can find the artifacts as follows:
|
||||||
|
|
||||||
|
1. Select `All workflows` on the left
|
||||||
|
2. Select the `Build EDMC for Windows` action
|
||||||
|
3. Select your build (probably the top one)
|
||||||
|
4. Find the `Built Files` artifact
|
||||||
|
|
||||||
|
Within the `Built Files` zip file is the installer msi
|
||||||
|
|
||||||
|
**Please ensure you test the built msi before creating a release.**
|
@ -10,6 +10,8 @@ This document aims to enable anyone to quickly get up to speed on how to:
|
|||||||
Note that for Windows only a 32-bit application is supported at this time.
|
Note that for Windows only a 32-bit application is supported at this time.
|
||||||
This is principally due to the Windows Registry handling in config.py.
|
This is principally due to the Windows Registry handling in config.py.
|
||||||
|
|
||||||
|
Builds can be run automatically from GitHub Actions. For more information on that process, see [Automatic Builds](https://github.com/EDCD/EDMarketConnector/blob/main/docs/Automatic%20Builds.md).
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
|
|
||||||
You will need several pieces of software installed, or the files from their
|
You will need several pieces of software installed, or the files from their
|
||||||
@ -33,29 +35,16 @@ You will need several pieces of software installed, or the files from their
|
|||||||
[v3.7.9](https://www.python.org/downloads/release/python-379/) is the most
|
[v3.7.9](https://www.python.org/downloads/release/python-379/) is the most
|
||||||
recently tested version. You need the `Windows x86 executable installer`
|
recently tested version. You need the `Windows x86 executable installer`
|
||||||
file, for the 32-bit version.
|
file, for the 32-bit version.
|
||||||
1. [py2exe](https://github.com/albertosottile/py2exe):
|
1. [py2exe](https://github.com/albertosottile/py2exe) - Now available via PyPi,
|
||||||
1. Install the python module. There are two options here.
|
so will be picked up with the `pip install` below. Latest tested as per
|
||||||
1. You can use the latest release version [0.9.3.2](https://github.com/albertosottile/py2exe/releases/tag/v0.9.3.2)
|
`requirements-dev.txt`.
|
||||||
and the current Marginal 'python3' branch as-is. This contains a
|
|
||||||
small hack in `setup.py` to ensure `sqlite3.dll` is packaged.
|
|
||||||
|
|
||||||
pip install py2exe-0.9.3.2-cp37-none-win32.whl
|
|
||||||
1. Or you can use a pre-release version, [0.9.4.0](https://bintray.com/alby128/py2exe/download_file?file_path=py2exe-0.9.4.0-cp37-none-win32.whl), see [this py2exe issue](https://github.com/albertosottile/py2exe/issues/23#issuecomment-541359225),
|
|
||||||
which packages that DLL file correctly.
|
|
||||||
|
|
||||||
pip install py2exe-0.9.4.0-cp37-none-win32.whl
|
|
||||||
You can then edit out the following line from `setup.py`, but it
|
|
||||||
does no harm:
|
|
||||||
|
|
||||||
%s/DLLs/sqlite3.dll' % (sys.base_prefix),
|
|
||||||
|
|
||||||
1. You'll now need to 'pip install' several python modules.
|
1. You'll now need to 'pip install' several python modules.
|
||||||
1. Ensure you have `pip` installed. If needs be see
|
1. Ensure you have `pip` installed. If needs be see
|
||||||
[Installing pip](https://pip.pypa.io/en/stable/installing/)
|
[Installing pip](https://pip.pypa.io/en/stable/installing/)
|
||||||
1. The easiest way is to utilise the `requirements-dev.txt` file:
|
1. The easiest way is to utilise the `requirements-dev.txt` file:
|
||||||
`python -m pip install -r requirements-dev.txt`. This will install all
|
`python -m pip install --user -r requirements-dev.txt`. This will install
|
||||||
dependencies plus anything required for development *other than py2exe, see
|
all dependencies plus anything required for development.
|
||||||
above*.
|
|
||||||
1. Else check the contents of both `requirements.txt` and `requirements-dev.txt`,
|
1. Else check the contents of both `requirements.txt` and `requirements-dev.txt`,
|
||||||
and ensure the modules listed there are installed as per the version
|
and ensure the modules listed there are installed as per the version
|
||||||
requirements.
|
requirements.
|
||||||
|
@ -168,11 +168,73 @@
|
|||||||
<!-- Windows -->
|
<!-- Windows -->
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<title>Release 4.1.4</title>
|
<title>Release 4.1.5</title>
|
||||||
<description>
|
<description>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
<style>body { font-family:"Segoe UI","Tahoma"; font-size: 75%; } h2 { font-family:"Segoe UI","Tahoma"; font-size: 105%; }</style>
|
<style>body { font-family:"Segoe UI","Tahoma"; font-size: 75%; } h2 { font-family:"Segoe UI","Tahoma"; font-size: 105%; }</style>
|
||||||
|
|
||||||
|
<h2>Release 4.1.5</h2>
|
||||||
|
<p>This is a minor maintenance release, mostly addressing behaviour around
|
||||||
|
process shutdown and startup, along with a couple of small enhancements that
|
||||||
|
most users won't notice.</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>If there is already an EDMarketConnector.exe process running when trying
|
||||||
|
to run another instance then that new process will no longer exit silently.
|
||||||
|
Instead you'll get a pop-up telling you it's detected another process, and
|
||||||
|
you need to close that pop-up in order for this additional process to then
|
||||||
|
exit.</p>
|
||||||
|
<p>This hopefully makes it obvious when you've got a hung EDMarketConnect.exe
|
||||||
|
process that you need to kill in order to re-run the program.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>In order to gather more information about how and why EDMarketConnector.exe
|
||||||
|
sometimes doesn't shutdown properly we've added some extra debug logging to
|
||||||
|
the sequence of clean-up calls performed during shutdown.</p>
|
||||||
|
<p>Also, to make it more obvious if the process has hung during shutdown the
|
||||||
|
UI window is no longer hidden at the start of this shutdown sequence. It
|
||||||
|
will instead linger, with "Shutting down..." showing in the status line
|
||||||
|
(translation for this small phrase will be added in a later release).</p>
|
||||||
|
<p>If you encounter this shutdown hang then please add a comment to
|
||||||
|
<a href="https://github.com/EDCD/EDMarketConnector/issues/678">Application can leave a zombie process on shutdown #678</a>
|
||||||
|
to help us track down the cause and fix it.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Cater for 'mangled name' class functions in our logging code. e.g. where
|
||||||
|
you name a class member with a <code>__</code> prefix in order to 'hide' it from
|
||||||
|
out-of-class code.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>To help track down the cause of <a href="https://github.com/EDCD/EDMarketConnector/issues/798">Crashing On Startup #798</a>
|
||||||
|
we've added some exception catching in our logging code. If this is
|
||||||
|
triggered you will see <code>??:??</code> in logging output, instead of class and/or
|
||||||
|
function names.</p>
|
||||||
|
<p>If you encounter this then please comment on that bug report to aid us in
|
||||||
|
tracking down the root cause!</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Fixed logging from EDMC.exe so that the -debug log goes into <code>EDMC-debug.log</code>
|
||||||
|
not <code>EDMarketConnector-debug.log</code>.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Fix <code>EDMC.exe -j</code> handling of file encodings. NB: This command-line
|
||||||
|
argument isn't listed on <code>EDMC.exe -h</code> as it's intended for developer use
|
||||||
|
only.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Fix the name of 'Void Opal(s)' so that output of market data to files is
|
||||||
|
correct.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>Fix URL in PLUGINS.md to refer to <code>main</code>, not <code>master</code> branch.</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>We're able to pull <code>py2exe</code> from PyPi now, so docs/Releasing.md has been
|
||||||
|
update to reflect this.</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
<h2>Release 4.1.4</h2>
|
<h2>Release 4.1.4</h2>
|
||||||
<p>The only change from 4.1.3 is to insert some Windows version checks before
|
<p>The only change from 4.1.3 is to insert some Windows version checks before
|
||||||
even attempting to set a UTF-8 encoding. We'll now only attempt this if the
|
even attempting to set a UTF-8 encoding. We'll now only attempt this if the
|
||||||
@ -824,11 +886,11 @@ If any of your plugins are listed in that section then they will need updating,
|
|||||||
]]>
|
]]>
|
||||||
</description>
|
</description>
|
||||||
<enclosure
|
<enclosure
|
||||||
url="https://github.com/EDCD/EDMarketConnector/releases/download/Release/4.1.4/EDMarketConnector_win_4.1.4.msi"
|
url="https://github.com/EDCD/EDMarketConnector/releases/download/Release/4.1.5/EDMarketConnector_win_4.1.5.msi"
|
||||||
sparkle:os="windows"
|
sparkle:os="windows"
|
||||||
sparkle:installerArguments="/passive LAUNCH=yes"
|
sparkle:installerArguments="/passive LAUNCH=yes"
|
||||||
sparkle:version="4.1.4"
|
sparkle:version="4.1.5"
|
||||||
length="11341824"
|
length="11362304"
|
||||||
type="application/octet-stream"
|
type="application/octet-stream"
|
||||||
/>
|
/>
|
||||||
</item>
|
</item>
|
||||||
|
33
setup.py
33
setup.py
@ -222,36 +222,3 @@ elif sys.platform == 'win32':
|
|||||||
os.system(r'cscript /nologo "%s\WiSubStg.vbs" %s %s\%d.mst %d' % (SDKPATH, PKG, gettempdir(), lcid, lcid))
|
os.system(r'cscript /nologo "%s\WiSubStg.vbs" %s %s\%d.mst %d' % (SDKPATH, PKG, gettempdir(), lcid, lcid))
|
||||||
else:
|
else:
|
||||||
raise AssertionError('Unsupported platform')
|
raise AssertionError('Unsupported platform')
|
||||||
|
|
||||||
if not exists(PKG):
|
|
||||||
raise AssertionError('No %s found prior to appcast' % (PKG))
|
|
||||||
# Make appcast entry
|
|
||||||
appcast = open('appcast_%s_%s.xml' % (sys.platform=='darwin' and 'mac' or 'win', VERSION), 'w')
|
|
||||||
appcast.write('''
|
|
||||||
\t\t<item>
|
|
||||||
\t\t\t<title>Release {VERSION}</title>
|
|
||||||
\t\t\t<description>
|
|
||||||
\t\t\t\t<![CDATA[
|
|
||||||
<style>{STYLE}</style>
|
|
||||||
<h2>Release {VERSION}</h2>
|
|
||||||
<ul>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
\t\t\t\t]]>
|
|
||||||
\t\t\t</description>
|
|
||||||
\t\t\t<enclosure
|
|
||||||
\t\t\t\turl="https://github.com/EDCD/EDMarketConnector/releases/download/rel-{VERSION}/{PKG}"
|
|
||||||
\t\t\t\tsparkle:os="{OS}"
|
|
||||||
\t\t\t\tsparkle:version="{VERSION}"
|
|
||||||
\t\t\t\tlength="{LENGTH}"
|
|
||||||
\t\t\t\ttype="application/octet-stream"
|
|
||||||
\t\t\t/>
|
|
||||||
\t\t</item>
|
|
||||||
'''.format(VERSION=VERSION,
|
|
||||||
STYLE='{}'.format(
|
|
||||||
sys.platform=='win32' and 'body { font-family:"Segoe UI","Tahoma"; font-size: 75%; } h2 { font-family:"Segoe UI","Tahoma"; font-size: 105%; }'
|
|
||||||
or 'h2 { font-size: 105%; }'),
|
|
||||||
PKG=PKG,
|
|
||||||
OS=''.format(sys.platform=='win32' and 'windows"\n\t\t\t\tsparkle:installerArguments="/passive LAUNCH=yes' or 'macos'),
|
|
||||||
LENGTH=os.stat(PKG).st_size)
|
|
||||||
)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user