[Plugins] Update create script and add GTK3 how-to doc

- Updated create_plugin script to create a GTK3 plugin.
- Added a document for updating a 1.3 plugin to be compatible with
  2.0.
This commit is contained in:
Calum Lind 2018-11-01 18:34:52 +00:00
parent 3d24998577
commit a01481b26f
3 changed files with 204 additions and 74 deletions

View File

@ -112,11 +112,11 @@ def create_plugin():
write_file(deluge_namespace, '__init__.py', NAMESPACE_INIT, False)
write_file(plugins_namespace, '__init__.py', NAMESPACE_INIT, False)
write_file(src, '__init__.py', INIT)
write_file(src, 'gtkui.py', GTKUI)
write_file(src, 'gtk3ui.py', GTK3UI)
write_file(src, 'webui.py', WEBUI)
write_file(src, 'core.py', CORE)
write_file(src, 'common.py', COMMON)
write_file(data_dir, 'config.glade', GLADE)
write_file(data_dir, 'config.ui', GLADE)
write_file(data_dir, '%s.js' % safe_name, DEFAULT_JS)
# add an input parameter for this?
@ -127,8 +127,7 @@ def create_plugin():
os.system(dev_link_path)
CORE = """
from __future__ import unicode_literals
CORE = """from __future__ import unicode_literals
import logging
@ -145,7 +144,8 @@ DEFAULT_PREFS = {
class Core(CorePluginBase):
def enable(self):
self.config = deluge.configmanager.ConfigManager('%(safe_name)s.conf', DEFAULT_PREFS)
self.config = deluge.configmanager.ConfigManager(
'%(safe_name)s.conf', DEFAULT_PREFS)
def disable(self):
pass
@ -166,34 +166,32 @@ class Core(CorePluginBase):
return self.config.config
"""
INIT = """
from deluge.plugins.init import PluginInitBase
INIT = """from deluge.plugins.init import PluginInitBase
class CorePlugin(PluginInitBase):
def __init__(self, plugin_name):
from core import Core as PluginClass
from .core import Core as PluginClass
self._plugin_cls = PluginClass
super(CorePlugin, self).__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
class Gtk3UIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from gtkui import GtkUI as PluginClass
from .gtk3ui import Gtk3UI as PluginClass
self._plugin_cls = PluginClass
super(GtkUIPlugin, self).__init__(plugin_name)
super(Gtk3UIPlugin, self).__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from webui import WebUI as PluginClass
from .webui import WebUI as PluginClass
self._plugin_cls = PluginClass
super(WebUIPlugin, self).__init__(plugin_name)
"""
SETUP = """
from setuptools import find_packages, setup
SETUP = """from setuptools import find_packages, setup
__plugin_name__ = '%(name)s'
__author__ = '%(author_name)s'
@ -222,16 +220,15 @@ setup(
entry_points=\"\"\"
[deluge.plugin.core]
%%s = deluge.plugins.%%s:CorePlugin
[deluge.plugin.gtkui]
%%s = deluge.plugins.%%s:GtkUIPlugin
[deluge.plugin.gtk3ui]
%%s = deluge.plugins.%%s:Gtk3UIPlugin
[deluge.plugin.web]
%%s = deluge.plugins.%%s:WebUIPlugin
\"\"\" %% ((__plugin_name__, __plugin_name__.lower()) * 3)
)
"""
COMMON = """
from __future__ import unicode_literals
COMMON = """from __future__ import unicode_literals
import os.path
@ -239,17 +236,18 @@ from pkg_resources import resource_filename
def get_resource(filename):
return resource_filename('deluge.plugins.%(safe_name)s', os.path.join('data', filename))
return resource_filename(
'deluge.plugins.%(safe_name)s', os.path.join('data', filename))
"""
GTKUI = """
from __future__ import unicode_literals
GTK3UI = """from __future__ import unicode_literals
import gtk
import logging
from gi.repository import Gtk
import deluge.component as component
from deluge.plugins.pluginbase import GtkPluginBase
from deluge.plugins.pluginbase import Gtk3PluginBase
from deluge.ui.client import client
from .common import get_resource
@ -257,23 +255,29 @@ from .common import get_resource
log = logging.getLogger(__name__)
class GtkUI(GtkPluginBase):
class Gtk3UI(Gtk3PluginBase):
def enable(self):
self.glade = gtk.glade.XML(get_resource('config.glade'))
self.builder = Gtk.Builder()
self.builder.add_from_file(get_resource('config.glade'))
component.get('Preferences').add_page('%(name)s', self.glade.get_widget('prefs_box'))
component.get('PluginManager').register_hook('on_apply_prefs', self.on_apply_prefs)
component.get('PluginManager').register_hook('on_show_prefs', self.on_show_prefs)
component.get('Preferences').add_page(
'%(name)s', self.builder.get_object('prefs_box'))
component.get('PluginManager').register_hook(
'on_apply_prefs', self.on_apply_prefs)
component.get('PluginManager').register_hook(
'on_show_prefs', self.on_show_prefs)
def disable(self):
component.get('Preferences').remove_page('%(name)s')
component.get('PluginManager').deregister_hook('on_apply_prefs', self.on_apply_prefs)
component.get('PluginManager').deregister_hook('on_show_prefs', self.on_show_prefs)
component.get('PluginManager').deregister_hook(
'on_apply_prefs', self.on_apply_prefs)
component.get('PluginManager').deregister_hook(
'on_show_prefs', self.on_show_prefs)
def on_apply_prefs(self):
log.debug('applying prefs for %(name)s')
config = {
'test': self.glade.get_widget('txt_test').get_text()
'test': self.builder.get_object('txt_test').get_text()
}
client.%(safe_name)s.set_config(config)
@ -282,45 +286,43 @@ class GtkUI(GtkPluginBase):
def cb_get_config(self, config):
\"\"\"callback for on show_prefs\"\"\"
self.glade.get_widget('txt_test').set_text(config['test'])
self.builder.get_object('txt_test').set_text(config['test'])
"""
GLADE = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.5 on Fri Aug 8 23:34:44 2008 -->
<glade-interface>
<widget class="GtkWindow" id="window1">
GLADE = """<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<object class="GtkWindow" id="window1">
<child>
<widget class="GtkHBox" id="prefs_box">
<object class="GtkBox" id="prefs_box">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label1">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Test config value:</property>
</widget>
</object>
</child>
<child>
<widget class="GtkEntry" id="txt_test">
<object class="GtkEntry" id="txt_test">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</object>
</child>
</widget>
</glade-interface>
</object>
</interface>
"""
WEBUI = """
from __future__ import unicode_literals
WEBUI = """from __future__ import unicode_literals
import logging
from deluge.plugins.pluginbase import WebPluginBase
from deluge.ui.client import client
from .common import get_resource
@ -338,17 +340,17 @@ class WebUI(WebPluginBase):
pass
"""
DEFAULT_JS = """/*
Script: %(filename)s
The client-side javascript code for the %(name)s plugin.
Copyright:
(C) %(author_name)s %(current_year)s <%(author_email)s>
This file is part of %(name)s and is licensed under GNU General Public License 3.0, or later, with
the additional special exception to link portions of this program with the OpenSSL library.
See LICENSE for more details.
*/
DEFAULT_JS = """/**
* Script: %(filename)s
* The client-side javascript code for the %(name)s plugin.
*
* Copyright:
* (C) %(author_name)s %(current_year)s <%(author_email)s>
*
* This file is part of %(name)s and is licensed under GNU GPL 3.0, or
* later, with the additional special exception to link portions of this
* program with the OpenSSL library. See LICENSE for more details.
*/
%(name)sPlugin = Ext.extend(Deluge.Plugin, {
constructor: function(config) {
@ -363,29 +365,21 @@ Copyright:
},
onEnable: function() {
this.prefsPage = deluge.preferences.addPage(new Deluge.ux.preferences.%(name)sPage());
this.prefsPage = deluge.preferences.addPage(
new Deluge.ux.preferences.%(name)sPage());
}
});
new %(name)sPlugin();
"""
GPL = """#
# -*- coding: utf-8 -*-#
GPL = """# -*- coding: utf-8 -*-
# Copyright (C) %(current_year)d %(author_name)s <%(author_email)s>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# 2007-2009 Andrew Resch <andrewresch@gmail.com>
# 2009 Damien Churchill <damoxc@gmail.com>
# 2010 Pedro Algarvio <pedro@algarvio.me>
# 2017 Calum Lind <calumlind+deluge@gmail.com>
# Basic plugin template created by the Deluge Team.
#
# This file is part of %(name)s and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
# This file is part of %(name)s and is licensed under GNU GPL 3.0, or later,
# with the additional special exception to link portions of this program with
# the OpenSSL library. See LICENSE for more details.
"""
NAMESPACE_INIT = """__import__('pkg_resources').declare_namespace(__name__)

View File

@ -6,3 +6,7 @@ the tutorials.
## Web JSON-RPC
- [Connect to JSON-RPC using curl](curl-jsonrpc.md)
## Plugins
- [Update 1.3 plugin for 2.0](update-1.3-plugin.md)

View File

@ -0,0 +1,132 @@
# How to update a Deluge 1.3 plugin for 2.0
With the new code in Deluge 2.0 there are changes that require authors of
existing plugins to update their plugins to work on Deluge 2.0.
The main changes are with Python 3 support and the new GTK3 user interface with
the dropping of GTK2. However it is still possible for a 1.3 plugin to be made
compatible with 2.0 and this guide aims to helps with that process.
Note that the Deluge 2.0 plugins now use namespace packaging which is not
compatible with Deluge 1.3.
## Python
### Python version matching
Ensure your code is both Python 2.7 and Python >=3.5 compatible.
In `1.3-stable` the plugins that were built with a specfific version of Python
could on be loaded if the system Python also matched.
This has change in Deluge 2.0 and it will load any Python version of plugin
eggs so compatibility is essential for end-users not to encounter issues.
### Six
Use [six] to assist with compatibility.
[six]: https://pythonhosted.org/six/
### Unicode literals
Add this to files to ensure strings and bytes separatation so there are no
surprises when running on Python 3.
## GTK 3 addition
In order to support both Deluge 1.3 and 2.0 all existing plugin GTK UI files
must be copied and then converted to contain only GTK3 code with the old files
still using PyGTK e.g.:
cp gtkui.py gtk3ui.py
### Convert from libglade to GtkBuilder
With PyGTK there were two library options for creating the user interface from
XML files by default Deluge plugins used libglade but that has been deprecated
and removed in GTK3. So the libglade `.glade` files will need converted to
GtkBuilder `.ui` files and the Python code updated.
https://developer.gnome.org/gtk2/stable/gtk-migrating-GtkBuilder.html
#### gtk-builder-convert
Install the `gtk-builder-convert` converter on Ubuntu with:
sudo apt install libgtk2.0-dev
To convert your GTK run it like so:
gtk-builder-convert data/config.glade data/config.ui
#### Glade UI designer for GTK2
The above conversion can be done in Glade UI designer (version <=3.8), ensuring
that the minimum Gtk version is set to 2.24 and any deprecated widgets are
fixed. The updated file should be saved with file extension `.ui`.
#### Python code changes
The code needs to replace `gtk.glade` references with `gtk.Builder` and the
first step is updating how the files are loaded:
```diff
- self.glade = gtk.glade.XML(get_resource("config.glade"))
+ self.builder = gtk.Builder()
+ self.builder.add_from_file(get_resource("config.ui"))
```
The next stage is to replace every occurange of these `glade` methods with
the `builder` equivalents:
glade.signal_autoconnect -> builder.connect_signals
glade.get_widget -> builder.get_object
### Migrate XML files to GTK3
If you open and save the file it will update with the new requirement header:
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.0"/>
You can fix deprecated widgets but keep the minimum GTK version to <= 3.10 for
desktop compatiblity.
An example of migrating a Deluge plugin to GtkBuilder: [AutoAdd GtkBuilder]
### Gtk import rename
Move from PyGTK to GTK3 using Python bindings.
https://pygobject.readthedocs.io/en/latest/guide/porting.html
wget https://gitlab.gnome.org/GNOME/pygobject/raw/master/tools/pygi-convert.sh
cp gtkui.py gtk3ui.py
sh pygi-convert.sh gtk3ui.py
```diff
-import gtk
+from gi.repository import Gtk
```
```diff
- self.builder = gtk.Builder()
+ self.builder = Gtk.Builder()
```
### Deluge GTK3
Imports will need updated from `deluge.ui.gtkui` to `deluge.ui.gtk3`.
### PluginBase
```diff
-from deluge.plugins.pluginbase import GtkPluginBase
+from deluge.plugins.pluginbase import Gtk3PluginBase
-class GtkUI(GtkPluginBase):
+class Gtk3UI(Gtk3PluginBase):
```
[autoadd gtkbuilder]: https://git.deluge-torrent.org/deluge/commit/?h=develop&id=510a8b50b213cab804d693a5f122f9c0d9dd1fb3