1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-21 11:27:38 +03:00

PLUGINS.md: s/EDMC/EDMarketConnector/ almost everywhere & reflow

The reflow is on *all* non-code/table text to be within 80 columns,
excepting if a link just makes that impossible.
This commit is contained in:
Athanasius 2021-08-13 13:32:36 +01:00
parent 411bd0edf2
commit 6c34ceaba9
No known key found for this signature in database
GPG Key ID: AE3E527847057C7D

@ -1,6 +1,6 @@
# Developer Plugin Documentation
Plugins allow you to customise and extend the behavior of EDMC.
Plugins allow you to customise and extend the behavior of EDMarketConnector.
## Installing a Plugin
@ -12,28 +12,31 @@ wiki.
Check [Releasing.md](docs/Releasing.md#environment) to be sure of the current
version of Python that we've tested against.
Plugins are loaded when EDMC starts up.
Plugins are loaded when EDMarketConnector starts up.
Each plugin has it's own folder in the `plugins` directory:
- Windows: `%LOCALAPPDATA%\EDMarketConnector\plugins`
- Mac: `~/Library/Application Support/EDMarketConnector/plugins`
- Linux: `$XDG_DATA_HOME/EDMarketConnector/plugins`, or `~/.local/share/EDMarketConnector/plugins` if `$XDG_DATA_HOME` is unset.
- Linux: `$XDG_DATA_HOME/EDMarketConnector/plugins`, or
`~/.local/share/EDMarketConnector/plugins` if `$XDG_DATA_HOME` is unset.
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
others.
If you're running from source (which allows for debugging with e.g. [PyCharm](https://www.jetbrains.com/pycharm/features/))
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).
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).
Please be sure to read the [Avoiding potential pitfalls](#avoiding-potential-pitfalls)
section, else you might inadvertently cause issues for the core EDMC code
including whole application crashes.
section, else you might inadvertently cause issues for the core
EDMarketConnector code including whole application crashes.
It is highly advisable to ensure you are aware of all EDMarketConnector
releases, including the pre-releases. The -beta and -rc changelogs will
@ -118,9 +121,9 @@ the packaged executeable all output is redirected to a log file. See
[Reporting a problem](https://github.com/EDCD/EDMarketConnector/wiki/Troubleshooting#reporting-a-problem)
for the location of this log file.
EDMC now implements proper logging using the Python `logging` module. Plugin
developers should now use the following code instead of simple `print(...)`
statements.
EDMarketConnector now implements proper logging using the Python `logging`
module. Plugin developers should now use the following code instead of simple
`print(...)` statements.
Insert this at the top-level of your load.py file (so not inside
`plugin_start3()`):
@ -160,13 +163,14 @@ exactly the folder name. Our custom `qualname` and `class` formatters won't
work with a 'bare' logger, and will cause your code to throw exceptions if
you're not using our supplied logger.
If running with 4.1.0-beta1 or later of EDMC the logging setup happens in
the core code and will include the extra logfile destinations. If your
plugin is run under a pre-4.1.0 version of EDMC then the above will set up
basic logging only to the console (and thus redirected to the log file).
If running with 4.1.0-beta1 or later of EDMarketConnector the logging setup
happens in the core code and will include the extra logfile destinations. If
your plugin is run under a pre-4.1.0 version of EDMarketConnector then the
above will set up basic logging only to the console (and thus redirected to
the log file).
If you're certain your plugin will only be run under EDMC 4.1.0 or newer then
you can remove the `if` clause.
If you're certain your plugin will only be run under EDMarketConnector 4.1.0
or newer then you can remove the `if` clause.
Replace all `print(...)` statements with one of the following:
@ -226,26 +230,26 @@ from config import appversion
# Yes, just blow up if config.appverison is neither str or callable
logger.info(f'Core EDMC version: {core_version}')
logger.info(f'Core EDMarketConnector version: {core_version}')
# And then compare like this
if core_version < semantic_version.Version('5.0.0-beta1'):
logger.info('EDMC core version is before 5.0.0-beta1')
logger.info('EDMarketConnector core version is before 5.0.0-beta1')
else:
logger.info('EDMC core version is at least 5.0.0-beta1')
logger.info('EDMarketConnector core version is at least 5.0.0-beta1')
```
---
## Startup
EDMC will import the `load.py` file as a module and then call the
EDMarketConnector will import the `load.py` file as a module and then call the
`plugin_start3()` function.
```python
def plugin_start3(plugin_dir: str) -> str:
"""
Load this plugin into EDMC
Load this plugin into EDMarketConnector
"""
print(f"I am loaded! My plugin folder is {plugin_dir}")
return "Test"
@ -267,8 +271,8 @@ Mac, and `$TMP/EDMarketConnector.log` on Linux.
## Avoiding potential pitfalls
There are a number of things that your code should either do or avoiding
doing so as to play nicely with the core EDMC code and not risk causing
application crashes or hangs.
doing so as to play nicely with the core EDMarketConnector code and not risk
causing application crashes or hangs.
### Be careful about the name of your plugin directory
@ -344,15 +348,17 @@ So instead use:
### Configuration
If you want your plugin to be configurable via the GUI you can define a frame
(panel) to be displayed on its own tab in EDMC's settings dialog. The tab
title will be the value that you returned from `plugin_start3`. Use widgets
from EDMC's myNotebook.py for the correct look-and-feel. You can be notified
when the settings dialog is closed so you can save your settings.
(panel) to be displayed on its own tab in EDMarketConnector's settings dialog.
The tab title will be the value that you returned from `plugin_start3`. Use
widgets from EDMarketConnector's myNotebook.py for the correct look-and-feel.
You can be notified when the settings dialog is closed, so you can save your
settings.
You can use `set()` and `get_$type()` (where type is one of: `int`, `bool`,
`str`, `list`) from EDMC's `config.config` object to retrieve your plugin's
settings in a platform-independent way. Previously this was done with a single
set and two get methods, the new methods provide better type safety.
`str`, `list`) from EDMarketConnector's `config.config` object to retrieve
your plugin's settings in a platform-independent way. Previously this was done
with a single set and two get methods, the new methods provide better type
safety.
If you want to maintain compatibility with pre-5.0.0 versions of this
application (please encourage plugin users to update!) then you'll need to
@ -377,12 +383,12 @@ if not hasattr(config, 'get_list'):
```
**Be sure to use a unique prefix for any settings you save so as not to clash
with core EDMC or other plugins.**
with core EDMarketConnector or other plugins.**
Use `number_from_string()` from EDMC's `l10n.Locale` object to parse input
numbers in a locale-independent way. NB: the old CamelCase versions of
`number_from_string` and `string_from_number` do still exist, but are
deprecated. They will continue to work, but will throw warnings.
Use `number_from_string()` from EDMarketConnector's `l10n.Locale` object to
parse input numbers in a locale-independent way. NB: the old CamelCase
versions of `number_from_string` and `string_from_number` do still exist, but
are deprecated. They will continue to work, but will throw warnings.
Note that in the following example the function signature defines that it
returns `Optional[tk.Frame]` only because we need to allow for `None` if
@ -401,7 +407,7 @@ my_setting: Optional[tk.IntVar] = None
def plugin_prefs(parent: nb.Notebook, cmdr: str, is_beta: bool) -> Optional[tk.Frame]:
"""
Return a TK Frame for adding to the EDMC settings dialog.
Return a TK Frame for adding to the EDMarketConnector settings dialog.
"""
global my_setting
my_setting = tk.IntVar(value=config.get_int("MyPluginSetting")) # Retrieve saved value from config
@ -438,15 +444,15 @@ def prefs_changed(cmdr: str, is_beta: bool) -> None:
### Display
You can also have your plugin add an item to the EDMC main window and update
from your event hooks. This works in the same way as `plugin_prefs()`. For a
simple one-line item return a `tk.Label` widget or a 2 tuple of widgets.
For a more complicated item create a tk.Frame widget and populate it with other
ttk widgets. Return `None` if you just want to use this as a callback after the
main window and all other plugins are initialised.
You can also have your plugin add an item to the EDMarketConnector main window
and update from your event hooks. This works in the same way as
`plugin_prefs()`. For a simple one-line item return a `tk.Label` widget or a 2
tuple of widgets. For a more complicated item create a tk.Frame widget and
populate it with other ttk widgets. Return `None` if you just want to use this
as a callback after the main window and all other plugins are initialised.
You can use `string_from_number()` from EDMC's `l10n.Locale` object to format
numbers in your widgets in a locale-independent way.
You can use `string_from_number()` from EDMarketConnector's `l10n.Locale`
object to format numbers in your widgets in a locale-independent way.
```python
from typing import Optional, Tuple
@ -457,7 +463,7 @@ status: Optional[tk.Label]
def plugin_app(parent: tk.Frame) -> Tuple[tk.Label, tk.Label]:
"""
Create a pair of TK widgets for the EDMC main window
Create a pair of TK widgets for the EDMarketConnector main window
"""
global status
label = tk.Label(parent, text="Status:") # By default widgets inherit the current theme's colors
@ -473,7 +479,7 @@ def some_other_function() -> None:
| Parameter | Type | Description |
| :-------- | :---------------------------------------------: | :---------------------------------------------------------- |
| `parent` | `tk.Frame` | The root EDMC window |
| `parent` | `tk.Frame` | The root EDMarketConnector window |
| `RETURN` | `Union[tk.Widget, Tuple[tk.Widget, tk.Widget]]` | A widget to add to the main window. See below for more info |
The return from `plugin_app()` can either be any widget (`Frame`, `Label`,
@ -496,7 +502,7 @@ frame: Optional[tk.Frame] = None
def plugin_app(parent: tk.Frame) -> tk.Frame:
"""
Create a frame for the EDMC main window
Create a frame for the EDMarketConnector main window
"""
global frame
frame = tk.Frame(parent)
@ -519,9 +525,10 @@ See [Avoiding potential pitfalls](#avoiding-potential-pitfalls).
### Events
Once you have created your plugin and EDMC has loaded it there are four other
functions you can define to be notified by EDMC when something happens:
`journal_entry()`, `journal_entry_cqc()`, `dashboard_entry()` and `cmdr_data()`.
Once you have created your plugin and EDMarketConnector has loaded it there
are four other functions you can define to be notified by EDMarketConnector
when something happens: `journal_entry()`, `journal_entry_cqc()`,
`dashboard_entry()` and `cmdr_data()`.
Your events all get called on the main Tkinter loop so be sure not to block for
very long or the app will appear to freeze. If you have a long running
@ -551,7 +558,7 @@ def journal_entry(
logger.info(f'Arrived at {entry["StarSystem"]}')
```
This gets called when EDMC sees a new entry in the game's journal.
This gets called when EDMarketConnector sees a new entry in the game's journal.
| Parameter | Type | Description |
| :-------- | :--------------: | :--------------------------------------------------------------------- |
@ -682,10 +689,10 @@ ___
##### Synthetic Events
A special "StartUp" entry is sent if EDMC is started while the game is already
running. In this case you won't receive initial events such as "LoadGame",
"Rank", "Location", etc. However the `state` dictionary will reflect the
cumulative effect of these missed events.
A special "StartUp" entry is sent if EDMarketConnector is started while the
game is already running. In this case you won't receive initial events such as
"LoadGame", "Rank", "Location", etc. However the `state` dictionary will
reflect the cumulative effect of these missed events.
**NB: Any of the values in this might be `None` if the Cmdr has loaded into
Arena (CQC) from the Main Menu.**
@ -702,7 +709,7 @@ between the two scenarios.
**NB: Any of these events are passing to `journal_entry_cqc` rather than to
`journal_entry` if player has loaded into Arena (CQC).**
This event is not sent when EDMC is running on a different
This event is not sent when EDMarketConnector is running on a different
machine so you should not *rely* on receiving this event.
---
@ -782,7 +789,7 @@ This gets called when the user closes the program:
```python
def plugin_stop() -> None:
"""
EDMC is closing
EDMarketConnector is closing
"""
print("Farewell cruel world!")
```
@ -926,23 +933,23 @@ time after the corresponding `journal_entry()` event.
## Error messages
You can display an error in EDMC's status area by returning a string from your
`journal_entry()`, `dashboard_entry()` or `cmdr_data()` function, or
asynchronously (e.g. from a "worker" thread that is performing a long running
operation) by calling `plug.show_error()`. Either method will cause the "bad"
sound to be played (unless the user has muted sound).
You can display an error in EDMarketConnector's status area by returning a
string from your `journal_entry()`, `dashboard_entry()` or `cmdr_data()`
function, or asynchronously (e.g. from a "worker" thread that is performing a
long-running operation) by calling `plug.show_error()`. Either method will
cause the "bad" sound to be played (unless the user has muted sound).
The status area is shared between EDMC itself and all other plugins, so your
message won't be displayed for very long. Create a dedicated widget if you need
to display routine status information.
The status area is shared between EDMarketConnector itself and all other
plugins, so your message won't be displayed for very long. Create a dedicated
widget if you need to display routine status information.
---
## Localisation
You can localise your plugin to one of the languages that EDMC itself supports.
Add the following boilerplate near the top of each source file that contains
strings that needs translating:
You can localise your plugin to one of the languages that EDMarketConnector
itself supports. Add the following boilerplate near the top of each source
file that contains strings that needs translating:
```python
import l10n
@ -956,9 +963,9 @@ Wrap each string that needs translating with the `_()` function, e.g.:
status["text"] = _('Happy!') # Main window status
```
If you display localized strings in EDMC's main window you should refresh them
in your `prefs_changed` function in case the user has changed their preferred
language.
If you display localized strings in EDMarketConnector's main window you should
refresh them in your `prefs_changed` function in case the user has changed
their preferred language.
Translation files should reside in folder named `L10n` inside your plugin's
folder. Files must be in macOS/iOS ".strings" format, encoded as UTF-8. You can
@ -967,7 +974,7 @@ in your plugin's folder. This extracts all the translatable strings from Python
files in your plugin's folder and places them in a file named `en.template` in
the `L10n` folder. Rename this file as `<language_code>.strings` and edit it.
See EDMC's own [`L10n`](https://github.com/EDCD/EDMarketConnector/tree/main/L10n)
See EDMarketConnector's own [`L10n`](https://github.com/EDCD/EDMarketConnector/tree/main/L10n)
folder for the list of supported language codes and for example translation
files.
@ -976,9 +983,9 @@ files.
## Python Package Plugins
A _Package Plugin_ is both a standard Python package (i.e. contains an
`__init__.py` file) and an EDMC plugin (i.e. contains a `load.py` file
providing at minimum a `plugin_start3()` function). These plugins are loaded
before any non-Package plugins.
`__init__.py` file) and an EDMarketConnector plugin (i.e. contains a `load.py`
file providing at minimum a `plugin_start3()` function). These plugins are
loaded before any non-Package plugins.
Other plugins can access features in a Package Plugin by `import`ing the
package by name in the usual way.
@ -1085,16 +1092,20 @@ step for the extra module(s) until it works.
You can debug your http post requests using the builtin debug webserver.
To add support for said debug webserver to your plugin, you need to check `config.debug_senders` (`list[str]`) for
some indicator string for your plugin. `debug_senders` is generated from args to `--debug-sender` on the invocation
command line.
To add support for said debug webserver to your plugin, you need to check
`config.debug_senders` (`list[str]`) for some indicator string for your
plugin. `debug_senders` is generated from args to `--debug-sender` on the
invocation command line.
If said string exists, `DEBUG_WEBSERVER_HOST` and `DEBUG_WEBSERVER_PORT` in
`edmc_data` will contain the host and port for the currently running local webserver. Simply redirect your requests
there, and your requests will be logged to disk. For organisation, rewrite your request path to simply be `/pluginname`.
`edmc_data` will contain the host and port for the currently running local
webserver. Simply redirect your requests there, and your requests will be
logged to disk. For organisation, rewrite your request path to simply be
`/pluginname`.
Logs exist in `$TEMP/EDMarketConnector/http_debug/$path.log`. If somehow you manage to cause a directory traversal, your
data will not be saved to disk at all. You will see this in EDMCs log.
Logs exist in `$TEMP/EDMarketConnector/http_debug/$path.log`. If somehow you
manage to cause a directory traversal, your data will not be saved to disk at
all. You will see this in EDMarketConnectors log.
The simplest way to go about adding support is:
@ -1110,15 +1121,16 @@ if 'my_plugin' in debug_senders:
```
For returned data, you can modify `debug_webserver.DEFAULT_RESPONSES` (`dict[str, Union[Callable[[str], str]], str])`
with either a function that accepts a single string (the raw post data) and returns a single string
(the response to send), or with a string if your required response is simple.
For returned data, you can modify `debug_webserver.DEFAULT_RESPONSES`
(`dict[str, Union[Callable[[str], str]], str])` with either a function that
accepts a single string (the raw post data) and returns a single string (the
response to send), or with a string if your required response is simple.
## Disable a plugin
EDMC now lets you disable a plugin without deleting it, simply rename the
plugin folder to append ".disabled". Eg,
"SuperSpaceHelper" -> "SuperSpaceHelper.disabled"
EDMarketConnector now lets you disable a plugin without deleting it, simply
rename the plugin folder to append ".disabled". Eg, "SuperSpaceHelper" ->
"SuperSpaceHelper.disabled"
Disabled and enabled plugins are listed on the "Plugins" Settings tab
@ -1126,18 +1138,19 @@ Disabled and enabled plugins are listed on the "Plugins" Settings tab
## Migration from Python 2.7
Starting with pre-release 3.5 EDMC used Python 3.7. The first full
release under Python 3.7 was 4.0.0.0. The 4.2.x series was the last to use
Python 3.7, with releases moving on to the latest Python 3.9.x after that.
Starting with pre-release 3.5 EDMarketConnector used Python 3.7. The first
full release under Python 3.7 was 4.0.0.0. The 4.2.x series was the last to
use Python 3.7, with releases moving on to the latest Python 3.9.x after that.
This is a brief outline of the steps required to migrate a plugin from earlier
versions of EDMC:
versions of EDMarketConnector:
- Rename the function `plugin_start` to `plugin_start3(plugin_dir)`.
Plugins without a `plugin_start3` function are listed as disabled on EDMC's
"Plugins" tab and a message like "plugin SuperSpaceHelper needs migrating"
appears in the log. Such plugins are also listed in a section "Plugins
Without Python 3.x Support:" on the Settings > Plugins tab.
Plugins without a `plugin_start3` function are listed as disabled on
EDMarketConnector's "Plugins" tab and a message like "plugin
SuperSpaceHelper needs migrating" appears in the log. Such plugins are
also listed in a section "Plugins Without Python 3.x Support:" on the
`Settings` > `Plugins` tab.
- Check that callback functions `plugin_prefs`, `prefs_changed`,
`journal_entry`, `dashboard_entry` and `cmdr_data`, if used, are declared
@ -1150,5 +1163,5 @@ versions of EDMC:
We advise *against* making any attempt to have a plugin's code work under
both Python 2.7 and 3.x. We no longer maintain the Python 2.7-based
versions of this application and you shouldn't support use of them with
versions of this application, and you shouldn't support use of them with
your plugin.