mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-14 00:07:14 +03:00
plugin support for EDMC
This commit is contained in:
parent
12da74d413
commit
477e60a64f
@ -39,6 +39,7 @@ import flightlog
|
||||
import eddb
|
||||
import stats
|
||||
import prefs
|
||||
import plug
|
||||
from hotkey import hotkeymgr
|
||||
from monitor import monitor
|
||||
|
||||
@ -63,6 +64,8 @@ class AppWindow:
|
||||
self.w.rowconfigure(0, weight=1)
|
||||
self.w.columnconfigure(0, weight=1)
|
||||
|
||||
plug.load_plugins()
|
||||
|
||||
if platform == 'win32':
|
||||
self.w.wm_iconbitmap(default='EDMarketConnector.ico')
|
||||
elif platform == 'linux2':
|
||||
@ -84,13 +87,28 @@ class AppWindow:
|
||||
|
||||
frame = ttk.Frame(self.w, name=appname.lower())
|
||||
frame.grid(sticky=tk.NSEW)
|
||||
rows = 4
|
||||
|
||||
plugin_items = list()
|
||||
for plugname in plug.PLUGINS:
|
||||
appitem = plug.get_plugin_app(plugname, frame)
|
||||
if appitem:
|
||||
plugin_items.append(appitem)
|
||||
|
||||
rows += len(plugin_items)
|
||||
|
||||
frame.columnconfigure(1, weight=1)
|
||||
frame.rowconfigure(4, weight=1)
|
||||
frame.rowconfigure(rows, weight=1)
|
||||
|
||||
ttk.Label(frame, text=_('Cmdr')+':').grid(row=0, column=0, sticky=tk.W) # Main window
|
||||
ttk.Label(frame, text=_('System')+':').grid(row=1, column=0, sticky=tk.W) # Main window
|
||||
ttk.Label(frame, text=_('Station')+':').grid(row=2, column=0, sticky=tk.W) # Main window
|
||||
|
||||
nextrow = 3
|
||||
for plugin_item in plugin_items:
|
||||
plugin_item.grid(row=nextrow, column=0, sticky=tk.W)
|
||||
nextrow += 1
|
||||
|
||||
self.cmdr = ttk.Label(frame, width=-21)
|
||||
self.system = HyperlinkLabel(frame, compound=tk.RIGHT, url = self.system_url, popup_copy = True)
|
||||
self.station = HyperlinkLabel(frame, url = self.station_url, popup_copy = lambda x: x!=self.STATION_UNDOCKED)
|
||||
@ -275,10 +293,13 @@ class AppWindow:
|
||||
self.status['text'] = _("What are you flying?!") # Shouldn't happen
|
||||
|
||||
else:
|
||||
|
||||
if __debug__: # Recording
|
||||
with open('%s%s.%s.json' % (data['lastSystem']['name'], data['commander'].get('docked') and '.'+data['lastStarport']['name'] or '', strftime('%Y-%m-%dT%H.%M.%S', localtime())), 'wt') as h:
|
||||
h.write(json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True).encode('utf-8'))
|
||||
|
||||
plug.notify_newdata(data)
|
||||
|
||||
self.cmdr['text'] = data.get('commander') and data.get('commander').get('name') or ''
|
||||
self.system['text'] = data.get('lastSystem') and data.get('lastSystem').get('name') or ''
|
||||
self.station['text'] = data.get('commander') and data.get('commander').get('docked') and data.get('lastStarport') and data.get('lastStarport').get('name') or (EDDB.system(self.system['text']) and self.STATION_UNDOCKED or '')
|
||||
@ -434,6 +455,9 @@ class AppWindow:
|
||||
|
||||
if self.system['text'] != system:
|
||||
self.system['text'] = system
|
||||
|
||||
plug.notify_system_changed(timestamp, system)
|
||||
|
||||
self.system['image'] = ''
|
||||
self.station['text'] = EDDB.system(system) and self.STATION_UNDOCKED or ''
|
||||
if config.getint('output') & config.OUT_LOG_FILE:
|
||||
|
96
PLUGINS.md
Normal file
96
PLUGINS.md
Normal file
@ -0,0 +1,96 @@
|
||||
# EDMC Plugins
|
||||
|
||||
Market Connector Plugins allow you to customise and extend the behavior of EDMC.
|
||||
|
||||
# Writing a Plugin
|
||||
|
||||
Plugins are loaded when EDMC starts up.
|
||||
|
||||
Plugins are python files. Each plugin has it's own folder in the `plugins` directory. The plugin must have a file named `load.py` that must provide one module level function and optionally provide a few others.
|
||||
|
||||
EDMC will import the `load.py` file as a module and then call the `plugin_start()` function.
|
||||
|
||||
```
|
||||
def plugin_start():
|
||||
"""
|
||||
Load this plugin into EDMC
|
||||
"""
|
||||
print "I am loaded!"
|
||||
```
|
||||
|
||||
# Plugin Hooks
|
||||
## Configuration
|
||||
|
||||
If you want your plugin to be configurable via the GUI you can define form to be used by EDMC's settings window.
|
||||
|
||||
```
|
||||
import Tkinter as tk
|
||||
|
||||
def plugin_prefs(parent):
|
||||
"""
|
||||
Return a TK Frame for adding to the EDMC settings dialog.
|
||||
"""
|
||||
prefs = tk.Frame(parent)
|
||||
prefs.columnconfigure(1, weight=1)
|
||||
prefs.rowconfigure(2, weight=1)
|
||||
|
||||
tk.Label(prefs, text="Hello").grid(row=0)
|
||||
tk.Label(prefs, text="Commander").grid(row=1)
|
||||
|
||||
return prefs
|
||||
```
|
||||
|
||||
## Display
|
||||
|
||||
You can also have your plugin add an item to the EDMC main window and update it if you need to from your event hooks. This works in the same way as `plugin_prefs()`.
|
||||
|
||||
```
|
||||
def plugin_app(parent):
|
||||
"""
|
||||
Create a TK frame for the main window
|
||||
"""
|
||||
status = tk.Frame(parent)
|
||||
status.columnconfigure(2, weight=1)
|
||||
status.rowconfigure(1, weight=1)
|
||||
|
||||
tk.Label(status, text="Status:").grid(row=0, column=0)
|
||||
|
||||
# after this your even functions can directly update plugin_app.status["text"]
|
||||
plugin_app.status = tk.Label(status, text="Happy!")
|
||||
plugin_app.status.grid(row=0, column=1)
|
||||
plugin_app.status = None
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
Once you have created your plugin and EDMC has loaded it there in addition to the `plugin_prefs()` and `plugin_app()` functions there are two other functions you can define to be notified by EDMC when something happens.
|
||||
`system_changed()` and `cmdr_data()`.
|
||||
|
||||
Your events all get called on the main tkinter loop so be sure not to block for very long or the EDMC will appear to freeze. If you have a long running operation or wish to update your plugin_app frame from the cmdr_data() event then
|
||||
you should take a look at how to do background updates in tkinter - http://effbot.org/zone/tkinter-threads.htm
|
||||
|
||||
### Arriving in a System
|
||||
|
||||
This gets called when EDMC uses the netlog to notice that you have arrived at a new star system.
|
||||
|
||||
```
|
||||
def system_changed(timestamp, system):
|
||||
"""
|
||||
We arrived at a new system!
|
||||
"""
|
||||
print "{} {}".format(timestamp, system)
|
||||
```
|
||||
|
||||
### Getting Commander Data
|
||||
|
||||
This gets called when EDMC has just fetched fresh data from Frontier's servers.
|
||||
|
||||
```
|
||||
def cmdr_data(data):
|
||||
"""
|
||||
We have new data on our commander
|
||||
"""
|
||||
print data.get('commander') and data.get('commander').get('name') or ''
|
||||
```
|
||||
|
||||
The data is a dictionary and full of lots of wonderful stuff!
|
115
plug.py
Normal file
115
plug.py
Normal file
@ -0,0 +1,115 @@
|
||||
"""
|
||||
Plugin hooks for EDMC - Ian Norton
|
||||
"""
|
||||
import os
|
||||
import imp
|
||||
|
||||
"""
|
||||
Dictionary of loaded plugin modules.
|
||||
"""
|
||||
PLUGINS = dict()
|
||||
|
||||
|
||||
def find_plugins():
|
||||
"""
|
||||
Look for plugin entry points.
|
||||
:return:
|
||||
"""
|
||||
found = dict()
|
||||
plug_folders = os.listdir("plugins")
|
||||
for name in plug_folders:
|
||||
loadfile = os.path.join("plugins", name, "load.py")
|
||||
if os.path.isfile(loadfile):
|
||||
found[name] = loadfile
|
||||
return found
|
||||
|
||||
|
||||
def load_plugins():
|
||||
"""
|
||||
Load all found plugins
|
||||
:return:
|
||||
"""
|
||||
found = find_plugins()
|
||||
imp.acquire_lock()
|
||||
for plugname in found:
|
||||
try:
|
||||
with open(found[plugname], "rb") as plugfile:
|
||||
plugmod = imp.load_module(plugname, plugfile, found[plugname],
|
||||
(".py", "r", imp.PY_SOURCE))
|
||||
if "plugin_start" in dir(plugmod):
|
||||
plugmod.plugin_start()
|
||||
PLUGINS[plugname] = plugmod
|
||||
|
||||
except Exception as plugerr:
|
||||
print plugerr
|
||||
|
||||
imp.release_lock()
|
||||
|
||||
|
||||
def _get_plugin_func(plugname, funcname):
|
||||
"""
|
||||
Get a function from a plugin, else return None if it isn't implemented.
|
||||
:param plugname:
|
||||
:param funcname:
|
||||
:return:
|
||||
"""
|
||||
if funcname in dir(PLUGINS[plugname]):
|
||||
return getattr(PLUGINS[plugname], funcname)
|
||||
return None
|
||||
|
||||
|
||||
def get_plugin_app(plugname, parent):
|
||||
"""
|
||||
If the plugin provides mainwindow content create and return it.
|
||||
:param plugname: name of the plugin
|
||||
:param parent: the parent frame for this entry.
|
||||
:return:
|
||||
"""
|
||||
plugin_app = _get_plugin_func(plugname, "plugin_app")
|
||||
if plugin_app:
|
||||
return plugin_app(parent)
|
||||
return None
|
||||
|
||||
|
||||
def get_plugin_pref(plugname, parent):
|
||||
"""
|
||||
If the plugin provides a prefs frame, create and return it.
|
||||
:param plugname: name of the plugin
|
||||
:param parent: the parent frame for this preference tab.
|
||||
:return:
|
||||
"""
|
||||
plugin_prefs = _get_plugin_func(plugname, "plugin_prefs")
|
||||
if plugin_prefs:
|
||||
return plugin_prefs(parent)
|
||||
return None
|
||||
|
||||
|
||||
def notify_system_changed(timestamp, system):
|
||||
"""
|
||||
Send notification data to each plugin when we arrive at a new system.
|
||||
:param timestamp:
|
||||
:param system:
|
||||
:return:
|
||||
"""
|
||||
for plugname in PLUGINS:
|
||||
system_changed = _get_plugin_func(plugname, "system_changed")
|
||||
if system_changed:
|
||||
try:
|
||||
system_changed(timestamp, system)
|
||||
except Exception as plugerr:
|
||||
print plugerr
|
||||
|
||||
|
||||
def notify_newdata(data):
|
||||
"""
|
||||
Send the latest EDMC data from the FD servers to each plugin
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
for plugname in PLUGINS:
|
||||
cmdr_data = _get_plugin_func(plugname, "cmdr_data")
|
||||
if cmdr_data:
|
||||
try:
|
||||
cmdr_data(data)
|
||||
except Exception as plugerr:
|
||||
print plugerr
|
62
plugins/About/load.py
Normal file
62
plugins/About/load.py
Normal file
@ -0,0 +1,62 @@
|
||||
"""
|
||||
A Skeleton EDMC Plugin
|
||||
"""
|
||||
import Tkinter as tk
|
||||
|
||||
|
||||
def plugin_start():
|
||||
"""
|
||||
Start this plugin
|
||||
:return:
|
||||
"""
|
||||
print "example plugin started"
|
||||
|
||||
|
||||
def plugin_prefs(parent):
|
||||
"""
|
||||
Return a TK Frame for adding to the EDMC settings dialog.
|
||||
"""
|
||||
prefs = tk.Frame(parent)
|
||||
prefs.columnconfigure(1, weight=1)
|
||||
prefs.rowconfigure(4, weight=1)
|
||||
|
||||
tk.Label(prefs, text="Elite Dangerous Market Connector").grid(row=0, column=0, sticky=tk.W)
|
||||
tk.Label(prefs, text="Fly Safe!").grid(row=2, column=0, sticky=tk.W)
|
||||
|
||||
if cmdr_data.last is not None:
|
||||
datalen = len(str(cmdr_data.last))
|
||||
tk.Label(prefs, text="FD sent {} chars".format(datalen)).grid(row=3, column=0, sticky=tk.W)
|
||||
|
||||
return prefs
|
||||
|
||||
|
||||
def plugin_app(parent):
|
||||
"""
|
||||
Return a TK Frame for adding to the EDMC main window.
|
||||
:param parent:
|
||||
:return:
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
def system_changed(timestamp, system):
|
||||
"""
|
||||
Arrived in a new System
|
||||
:param timestamp: when we arrived
|
||||
:param system: the name of the system
|
||||
:return:
|
||||
"""
|
||||
print "Arrived at {}".format(system)
|
||||
|
||||
|
||||
def cmdr_data(data):
|
||||
"""
|
||||
Obtained new data from Frontier about our commander, location and ships
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
cmdr_data.last = data
|
||||
print "Got new data ({} chars)".format(len(str(data)))
|
||||
|
||||
cmdr_data.last = None
|
||||
|
8
prefs.py
8
prefs.py
@ -14,6 +14,7 @@ from config import applongname, config
|
||||
from hotkey import hotkeymgr
|
||||
from monitor import monitor
|
||||
|
||||
import plug
|
||||
|
||||
if platform == 'darwin':
|
||||
import objc
|
||||
@ -218,6 +219,7 @@ class PreferencesDialog(tk.Toplevel):
|
||||
_('Hotkey')) # Tab heading in settings on Windows
|
||||
|
||||
|
||||
|
||||
if platform=='darwin':
|
||||
self.protocol("WM_DELETE_WINDOW", self.apply) # close button applies changes
|
||||
else:
|
||||
@ -236,6 +238,12 @@ class PreferencesDialog(tk.Toplevel):
|
||||
# disable hotkey for the duration
|
||||
hotkeymgr.unregister()
|
||||
|
||||
# build plugin prefs tabs
|
||||
for plugname in plug.PLUGINS:
|
||||
plugframe = plug.get_plugin_pref(plugname, notebook)
|
||||
if plugframe:
|
||||
notebook.add(plugframe, text=plugname)
|
||||
|
||||
# wait for window to appear on screen before calling grab_set
|
||||
self.wait_visibility()
|
||||
self.grab_set()
|
||||
|
Loading…
x
Reference in New Issue
Block a user