change vocabulary

s/module/backend
s/backend/configured_backend
s/frontend/application
This commit is contained in:
Christophe Benz 2010-07-12 03:07:10 +02:00
commit 5c2ab81e16
27 changed files with 347 additions and 347 deletions

View file

@ -35,7 +35,7 @@ class Boobank(ConsoleApplication):
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv): def main(self, argv):
self.load_backends(ICapBank) self.load_configured_backends(ICapBank)
return self.process_command(*argv[1:]) return self.process_command(*argv[1:])
@ConsoleApplication.command('List every available accounts') @ConsoleApplication.command('List every available accounts')

View file

@ -32,7 +32,7 @@ class Chatoob(ConsoleApplication):
COPYRIGHT = 'Copyright(C) 2010 Christophe Benz' COPYRIGHT = 'Copyright(C) 2010 Christophe Benz'
def main(self, argv): def main(self, argv):
self.load_backends(ICapChat) self.load_configured_backends(ICapChat)
#for backend, result in self.weboob.do('start_chat_polling', self.on_new_chat_message): #for backend, result in self.weboob.do('start_chat_polling', self.on_new_chat_message):
#logging.info(u'Polling chat messages for backend %s' % backend) #logging.info(u'Polling chat messages for backend %s' % backend)
return self.process_command(*argv[1:]) return self.process_command(*argv[1:])

View file

@ -38,7 +38,7 @@ class HaveSex(PromptApplication):
def main(self, argv): def main(self, argv):
self.load_config() self.load_config()
self.load_backends(ICapDating, storage=self.create_storage(self.STORAGE_FILENAME)) self.load_configured_backends(ICapDating, storage=self.create_storage(self.STORAGE_FILENAME))
self.weboob.do('init_optimizations').wait() self.weboob.do('init_optimizations').wait()

View file

@ -233,7 +233,6 @@ class Masstransit(BaseApplication):
COPYRIGHT = 'Copyright(C) 2010 Julien Hébert' COPYRIGHT = 'Copyright(C) 2010 Julien Hébert'
def main(self, argv): def main(self, argv):
"main fonction" self.load_backends(ICapTravel)
self.load_modules(ICapTravel)
MasstransitHildon(self.weboob) MasstransitHildon(self.weboob)
gtk.main() gtk.main()

View file

@ -93,7 +93,7 @@ class Monboob(ConsoleApplication):
def main(self, argv): def main(self, argv):
self.load_config() self.load_config()
self.load_backends(ICapMessages, storage=self.create_storage()) self.load_configured_backends(ICapMessages, storage=self.create_storage())
return self.process_command(*argv[1:]) return self.process_command(*argv[1:])

View file

@ -36,10 +36,10 @@ class MainWindow(QtMainWindow):
self.setCentralWidget(self.manager) self.setCentralWidget(self.manager)
self.connect(self.ui.actionModules, SIGNAL("triggered()"), self.modulesConfig) self.connect(self.ui.actionBackends, SIGNAL("triggered()"), self.backendsConfig)
self.connect(self.ui.actionRefresh, SIGNAL("triggered()"), self.refresh) self.connect(self.ui.actionRefresh, SIGNAL("triggered()"), self.refresh)
def modulesConfig(self): def backendsConfig(self):
bckndcfg = BackendCfg(self.weboob, (ICapMessages,), self) bckndcfg = BackendCfg(self.weboob, (ICapMessages,), self)
bckndcfg.show() bckndcfg.show()

View file

@ -27,7 +27,7 @@ class QBoobMsg(QtApplication):
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv): def main(self, argv):
self.load_backends(ICapMessages, storage=self.create_storage()) self.load_configured_backends(ICapMessages, storage=self.create_storage())
self.main_window = MainWindow(self.config, self.weboob) self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show() self.main_window.show()

View file

@ -29,7 +29,7 @@
<property name="title"> <property name="title">
<string>File</string> <string>File</string>
</property> </property>
<addaction name="actionModules"/> <addaction name="actionBackends"/>
<addaction name="actionRefresh"/> <addaction name="actionRefresh"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionQuit"/> <addaction name="actionQuit"/>
@ -47,12 +47,12 @@
<attribute name="toolBarBreak"> <attribute name="toolBarBreak">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
<addaction name="actionModules"/> <addaction name="actionBackends"/>
<addaction name="actionRefresh"/> <addaction name="actionRefresh"/>
</widget> </widget>
<action name="actionModules"> <action name="actionBackends">
<property name="text"> <property name="text">
<string>Modules</string> <string>Backends</string>
</property> </property>
</action> </action>
<action name="actionQuit"> <action name="actionQuit">

View file

@ -44,10 +44,10 @@ class MainWindow(QtMainWindow):
self.ui.tabWidget.addTab(ContactsWidget(self.weboob), self.tr('Contacts')) self.ui.tabWidget.addTab(ContactsWidget(self.weboob), self.tr('Contacts'))
self.ui.tabWidget.addTab(QWidget(), self.tr('Calendar')) self.ui.tabWidget.addTab(QWidget(), self.tr('Calendar'))
self.connect(self.ui.actionModules, SIGNAL("triggered()"), self.modulesConfig) self.connect(self.ui.actionBackends, SIGNAL("triggered()"), self.backendsConfig)
self.connect(self.ui.tabWidget, SIGNAL('currentChanged(int)'), self.tabChanged) self.connect(self.ui.tabWidget, SIGNAL('currentChanged(int)'), self.tabChanged)
def modulesConfig(self): def backendsConfig(self):
bckndcfg = BackendCfg(self.weboob, (ICapDating,), self) bckndcfg = BackendCfg(self.weboob, (ICapDating,), self)
bckndcfg.show() bckndcfg.show()

View file

@ -28,7 +28,7 @@ class QHaveSex(QtApplication):
STORAGE_FILENAME = 'dating.storage' STORAGE_FILENAME = 'dating.storage'
def main(self, argv): def main(self, argv):
self.load_backends(ICapDating, storage=self.create_storage()) self.load_configured_backends(ICapDating, storage=self.create_storage())
self.main_window = MainWindow(self.config, self.weboob) self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show() self.main_window.show()

View file

@ -37,7 +37,7 @@
<property name="title"> <property name="title">
<string>File</string> <string>File</string>
</property> </property>
<addaction name="actionModules"/> <addaction name="actionBackends"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionQuit"/> <addaction name="actionQuit"/>
</widget> </widget>
@ -54,11 +54,11 @@
<attribute name="toolBarBreak"> <attribute name="toolBarBreak">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
<addaction name="actionModules"/> <addaction name="actionBackends"/>
</widget> </widget>
<action name="actionModules"> <action name="actionBackends">
<property name="text"> <property name="text">
<string>Modules</string> <string>Backends</string>
</property> </property>
</action> </action>
<action name="actionQuit"> <action name="actionQuit">

View file

@ -32,7 +32,7 @@ class QVideoob(QtApplication):
} }
} }
def main(self, argv): def main(self, argv):
self.load_modules(ICapVideo) self.load_backends(ICapVideo)
self.load_config() self.load_config()
self.main_window = MainWindow(self.config, self.weboob) self.main_window = MainWindow(self.config, self.weboob)

View file

@ -27,7 +27,7 @@ class QWeboobCfg(QtApplication):
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv): def main(self, argv):
self.load_backends() self.load_configured_backends()
self.dlg = BackendCfg(self.weboob) self.dlg = BackendCfg(self.weboob)
self.dlg.show() self.dlg.show()

View file

@ -29,8 +29,7 @@ class Travel(ConsoleApplication):
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv): def main(self, argv):
self.load_modules(ICapTravel) self.load_backends(ICapTravel)
return self.process_command(*argv[1:]) return self.process_command(*argv[1:])
@ConsoleApplication.command('Search stations') @ConsoleApplication.command('Search stations')

View file

@ -41,7 +41,7 @@ class Videoob(ConsoleApplication):
def command_info(self, _id): def command_info(self, _id):
_id, backend_name = self.parse_id(_id) _id, backend_name = self.parse_id(_id)
names = (backend_name,) if backend_name is not None else None names = (backend_name,) if backend_name is not None else None
self.load_modules(ICapVideo, names=names) self.load_backends(ICapVideo, names=names)
for backend, video in self.weboob.do('get_video', _id): for backend, video in self.weboob.do('get_video', _id):
if video is None: if video is None:
continue continue
@ -49,7 +49,7 @@ class Videoob(ConsoleApplication):
@ConsoleApplication.command('Search for videos') @ConsoleApplication.command('Search for videos')
def command_search(self, pattern=None): def command_search(self, pattern=None):
self.load_modules(ICapVideo) self.load_backends(ICapVideo)
self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Last videos') self.set_formatter_header(u'Search pattern: %s' % pattern if pattern else u'Last videos')
for backend, video in self.do('iter_search_results', pattern=pattern, nsfw=self.options.nsfw): for backend, video in self.do('iter_search_results', pattern=pattern, nsfw=self.options.nsfw):
self.format(video, backend.name) self.format(video, backend.name)

View file

@ -72,7 +72,7 @@ class VideoobWeb(BaseApplication):
def main(self, argv): def main(self, argv):
self.load_config() self.load_config()
self.weboob.load_modules(ICapVideo) self.weboob.load_backends(ICapVideo)
print 'Web server created. Listening on http://%s:%s' % ( print 'Web server created. Listening on http://%s:%s' % (
self.config.get('host'), int(self.config.get('port'))) self.config.get('host'), int(self.config.get('port')))
srv = make_server(self.config.get('host'), int(self.config.get('port')), self.make_app) srv = make_server(self.config.get('host'), int(self.config.get('port')), self.make_app)

View file

@ -44,65 +44,10 @@ class WeboobCfg(ConsoleApplication):
return False return False
return True return True
@ConsoleApplication.command('List backends')
def command_backends(self, *caps):
self.set_default_formatter('table')
self.weboob.modules_loader.load()
for name, backend in self.weboob.modules_loader.modules.iteritems():
if caps and not self.caps_included(backend.iter_caps(), caps):
continue
row = OrderedDict([('Name', name),
('Capabilities', ', '.join(cap.__name__ for cap in backend.iter_caps())),
('Description', backend.get_description()),
])
self.format(row)
@ConsoleApplication.command('List applications')
def command_applications(self, *caps):
applications = set()
import weboob.applications
for path in weboob.applications.__path__:
regexp = re.compile('^%s/([\w\d_]+)$' % path)
for root, dirs, files in os.walk(path):
m = regexp.match(root)
if m and '__init__.py' in files:
applications.add(m.group(1))
print ' '.join(sorted(applications)).encode('utf-8')
@ConsoleApplication.command('Display information about a backend')
def command_info(self, name):
try:
backend = self.weboob.modules_loader.get_or_load_module(name)
except KeyError:
logging.error('No such backend: "%s"' % name)
return 1
print '.------------------------------------------------------------------------------.'
print '| Backend %-68s |' % backend.get_name()
print "+-----------------.------------------------------------------------------------'"
print '| Version | %s' % backend.get_version()
print '| Maintainer | %s' % backend.get_maintainer()
print '| License | %s' % backend.get_license()
print '| Description | %s' % backend.get_description()
print '| Capabilities | %s' % ', '.join([cap.__name__ for cap in backend.iter_caps()])
first = True
for key, field in backend.get_config().iteritems():
value = field.description
if not field.default is None:
value += ' (default: %s)' % field.default
if first:
print '| | '
print '| Configuration | %s: %s' % (key, value)
first = False
else:
print '| | %s: %s' % (key, value)
print "'-----------------'"
@ConsoleApplication.command('Add a configured backend') @ConsoleApplication.command('Add a configured backend')
def command_add(self, name, *options): def command_add(self, name, *options):
self.weboob.modules_loader.load() self.weboob.backends_loader.load_all()
if name not in [module_name for module_name, module in self.weboob.modules_loader.modules.iteritems()]: if name not in [name for name, backend in self.weboob.backends_loader.loaded.iteritems()]:
logging.error(u'Backend "%s" does not exist.' % name) logging.error(u'Backend "%s" does not exist.' % name)
return 1 return 1
@ -116,9 +61,9 @@ class WeboobCfg(ConsoleApplication):
return 1 return 1
params[key] = value params[key] = value
# ask for params non-specified on command-line arguments # ask for params non-specified on command-line arguments
module = self.weboob.modules_loader.get_or_load_module(name) backend = self.weboob.backends_loader.get_or_load_backend(name)
asked_config = False asked_config = False
for key, value in module.get_config().iteritems(): for key, value in backend.config.iteritems():
if not asked_config: if not asked_config:
asked_config = True asked_config = True
print u'Configuration of backend' print u'Configuration of backend'
@ -155,16 +100,74 @@ class WeboobCfg(ConsoleApplication):
except ConfigParser.DuplicateSectionError: except ConfigParser.DuplicateSectionError:
print u'Instance "%s" already exists for backend "%s".' % (new_name, name) print u'Instance "%s" already exists for backend "%s".' % (new_name, name)
@ConsoleApplication.command('List configured backends') @ConsoleApplication.command('Show applications')
def command_applications(self, *caps):
applications = set()
import weboob.applications
for path in weboob.applications.__path__:
regexp = re.compile('^%s/([\w\d_]+)$' % path)
for root, dirs, files in os.walk(path):
m = regexp.match(root)
if m and '__init__.py' in files:
applications.add(m.group(1))
print ' '.join(sorted(applications)).encode('utf-8')
@ConsoleApplication.command('Show available backends')
def command_backends(self, *caps):
self.set_default_formatter('table')
self.weboob.backends_loader.load_all()
for name, backend in sorted(self.weboob.backends_loader.loaded.iteritems()):
if caps and not self.caps_included(backend.iter_caps(), caps):
continue
row = OrderedDict([('Name', name),
('Capabilities', ', '.join(cap.__name__ for cap in backend.iter_caps())),
('Description', backend.description),
])
self.format(row)
@ConsoleApplication.command('Show configured backends')
def command_configured(self): def command_configured(self):
self.set_default_formatter('table') self.set_default_formatter('table')
for instance_name, name, params in self.weboob.backends_config.iter_backends(): for instance_name, name, params in sorted(self.weboob.backends_config.iter_backends()):
row = OrderedDict([('Instance name', instance_name), row = OrderedDict([('Instance name', instance_name),
('Backend name', name), ('Backend name', name),
('Configuration', ', '.join('%s=%s' % (key, value) for key, value in params.iteritems())), ('Configuration', ', '.join('%s=%s' % (key, value) for key, value in params.iteritems())),
]) ])
self.format(row) self.format(row)
@ConsoleApplication.command('Edit configuration file')
def command_edit(self):
subprocess.call([os.environ.get('EDITOR', 'vi'), self.weboob.backends_config.confpath])
@ConsoleApplication.command('Display information about a backend')
def command_info(self, name):
try:
backend = self.weboob.backends_loader.get_or_load_backend(name)
except KeyError:
logging.error('No such backend: "%s"' % name)
return 1
print '.------------------------------------------------------------------------------.'
print '| Backend %-68s |' % backend.name
print "+-----------------.------------------------------------------------------------'"
print '| Version | %s' % backend.version
print '| Maintainer | %s' % backend.maintainer
print '| License | %s' % backend.license
print '| Description | %s' % backend.description
print '| Capabilities | %s' % ', '.join([cap.__name__ for cap in backend.iter_caps()])
first = True
for key, field in backend.config.iteritems():
value = field.description
if not field.default is None:
value += ' (default: %s)' % field.default
if first:
print '| | '
print '| Configuration | %s: %s' % (key, value)
first = False
else:
print '| | %s: %s' % (key, value)
print "'-----------------'"
@ConsoleApplication.command('Remove a configured backend') @ConsoleApplication.command('Remove a configured backend')
def command_remove(self, instance_name): def command_remove(self, instance_name):
try: try:
@ -172,7 +175,3 @@ class WeboobCfg(ConsoleApplication):
except ConfigParser.NoSectionError: except ConfigParser.NoSectionError:
logging.error('Backend instance "%s" does not exist' % instance_name) logging.error('Backend instance "%s" does not exist' % instance_name)
return 1 return 1
@ConsoleApplication.command('Edit configuration file')
def command_edit(self):
subprocess.call([os.environ.get('EDITOR', 'vi'), self.weboob.backends_config.confpath])

View file

@ -32,14 +32,14 @@ class WeboobDebug(ConsoleApplication):
@ConsoleApplication.command('Debug backend') @ConsoleApplication.command('Debug backend')
def command_shell(self, backend_name): def command_shell(self, backend_name):
try: try:
backend = self.weboob.load_modules(names=[backend_name])[backend_name] backend = self.weboob.load_backends(names=[backend_name])[backend_name]
except KeyError: except KeyError:
logging.error(u'Unable to load backend "%s"' % backend_name) logging.error(u'Unable to load backend "%s"' % backend_name)
return 1 return 1
browser = backend.browser browser = backend.browser
from IPython.Shell import IPShellEmbed from IPython.Shell import IPShellEmbed
shell = IPShellEmbed(argv=[]) shell = IPShellEmbed(argv=[])
locs = dict(backend=backend, browser=browser, frontend=self, weboob=self.weboob) locs = dict(backend=backend, browser=browser, application=self, weboob=self.weboob)
banner = 'Weboob debug shell\nBackend "%s" loaded.\nAvailable variables: %s' % (backend_name, locs) banner = 'Weboob debug shell\nBackend "%s" loaded.\nAvailable variables: %s' % (backend_name, locs)
shell.set_banner(shell.IP.BANNER + '\n\n' + banner) shell.set_banner(shell.IP.BANNER + '\n\n' + banner)
shell(local_ns=locs, global_ns={}) shell(local_ns=locs, global_ns={})

View file

@ -15,6 +15,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from nose import run from nose import run
from weboob.tools.application.console import ConsoleApplication from weboob.tools.application.console import ConsoleApplication
@ -32,13 +33,13 @@ class WeboobTests(ConsoleApplication):
@ConsoleApplication.command('Run tests') @ConsoleApplication.command('Run tests')
def command_run(self): def command_run(self):
self.load_modules()
self.load_backends() self.load_backends()
self.load_configured_backends()
suite = [] suite = []
for backend in self.weboob.iter_backends(): for backend in self.weboob.iter_backends():
t = backend.get_test() test = backend.get_test()
if t: if test:
suite.append(t) suite.append(test)
return run(suite=suite) return run(suite=suite)

View file

@ -33,7 +33,7 @@ class Weboorrents(ConsoleApplication):
CONFIG = {} CONFIG = {}
def main(self, argv): def main(self, argv):
self.load_backends(ICapTorrent) self.load_configured_backends(ICapTorrent)
return self.process_command(*argv[1:]) return self.process_command(*argv[1:])
@ConsoleApplication.command('Get information about a torrent') @ConsoleApplication.command('Get information about a torrent')

View file

@ -32,7 +32,7 @@ class WetBoobs(ConsoleApplication):
COPYRIGHT = 'Copyright(C) 2010 Romain Bignon' COPYRIGHT = 'Copyright(C) 2010 Romain Bignon'
def main(self, argv): def main(self, argv):
self.load_modules(ICapWeather) self.load_backends(ICapWeather)
return self.process_command(*argv[1:]) return self.process_command(*argv[1:])

View file

@ -26,45 +26,50 @@ import re
import stat import stat
import weboob.backends import weboob.backends
from weboob.core.backend import BaseBackend
from weboob.capabilities.cap import ICap from weboob.capabilities.cap import ICap
from weboob.tools.backend import BaseBackend
__all__ = ['Module'] __all__ = ['Backend', 'BackendsConfig', 'BackendsLoader']
class Module(object): class Backend(object):
def __init__(self, name, module): def __init__(self, package):
self.name = name self.package = package
self.module = module
self.klass = None self.klass = None
for attrname in dir(self.module): for attrname in dir(self.package):
attr = getattr(self.module, attrname) attr = getattr(self.package, attrname)
if isinstance(attr, type) and issubclass(attr, BaseBackend) and attr != BaseBackend: if isinstance(attr, type) and issubclass(attr, BaseBackend) and attr != BaseBackend:
self.klass = attr self.klass = attr
if not self.klass: if not self.klass:
raise ImportError("This is not a backend module (no BaseBackend class found)") raise ImportError('%s is not a backend (no BaseBackend class found)' % package)
def get_name(self): @property
def name(self):
return self.klass.NAME return self.klass.NAME
def get_maintainer(self): @property
def maintainer(self):
return '%s <%s>' % (self.klass.MAINTAINER, self.klass.EMAIL) return '%s <%s>' % (self.klass.MAINTAINER, self.klass.EMAIL)
def get_version(self): @property
def version(self):
return self.klass.VERSION return self.klass.VERSION
def get_description(self): @property
def description(self):
return self.klass.DESCRIPTION return self.klass.DESCRIPTION
def get_license(self): @property
def license(self):
return self.klass.LICENSE return self.klass.LICENSE
def get_config(self): @property
def config(self):
return self.klass.CONFIG return self.klass.CONFIG
def get_icon_path(self): @property
def icon_path(self):
return self.klass.ICON return self.klass.ICON
def iter_caps(self): def iter_caps(self):
@ -78,9 +83,11 @@ class Module(object):
return True return True
return False return False
def create_backend(self, weboob, name, config, storage): def create_instance(self, weboob, name, config, storage):
debug('Created backend "%s"' % name) backend_instance = self.klass(weboob, name, config, storage)
return self.klass(weboob, name, config, storage) debug('Created backend instance "%s"' % name)
return backend_instance
class BackendsConfig(object): class BackendsConfig(object):
class WrongPermissions(Exception): class WrongPermissions(Exception):
@ -128,7 +135,7 @@ class BackendsConfig(object):
config = SafeConfigParser() config = SafeConfigParser()
config.read(self.confpath) config.read(self.confpath)
if not config.has_section(name): if not config.has_section(name):
raise KeyError(u'Backend "%s" not found' % name) raise KeyError(u'Backend instance "%s" not found' % name)
items = dict(config.items(name, raw=True)) items = dict(config.items(name, raw=True))
try: try:
@ -141,19 +148,20 @@ class BackendsConfig(object):
config = SafeConfigParser() config = SafeConfigParser()
config.read(self.confpath) config.read(self.confpath)
config.remove_section(name) config.remove_section(name)
with open(self.confpath, 'wb') as f: with open(self.confpath, 'w') as f:
config.write(f) config.write(f)
class ModulesLoader(object):
class BackendsLoader(object):
def __init__(self): def __init__(self):
self.modules = {} self.loaded = {}
def get_or_load_module(self, name): def get_or_load_backend(self, name):
if name not in self.modules: if name not in self.loaded:
self.load_module('weboob.backends.%s' % name) self.load_backend(name)
return self.modules[name] return self.loaded[name]
def iter_existing_module_names(self): def iter_existing_backend_names(self):
for path in weboob.backends.__path__: for path in weboob.backends.__path__:
regexp = re.compile('^%s/([\w\d_]+)$' % path) regexp = re.compile('^%s/([\w\d_]+)$' % path)
for root, dirs, files in os.walk(path): for root, dirs, files in os.walk(path):
@ -161,23 +169,24 @@ class ModulesLoader(object):
if m and '__init__.py' in files: if m and '__init__.py' in files:
yield m.group(1) yield m.group(1)
def load(self): def load_all(self):
for existing_module_name in self.iter_existing_module_names(): for existing_backend_name in self.iter_existing_backend_names():
self.load_module('weboob.backends.%s' % existing_module_name) self.load_backend(existing_backend_name)
def load_module(self, name): def load_backend(self, name):
try: try:
module = Module(name, __import__(name, fromlist=[str(name)])) package_name = 'weboob.backends.%s' % name
backend = Backend(__import__(package_name, fromlist=[str(package_name)]))
except ImportError, e: except ImportError, e:
msg = 'Unable to load module "%s": %s' % (name, e) msg = u'Unable to load backend "%s": %s' % (name, e)
if logging.root.level == logging.DEBUG: if logging.root.level == logging.DEBUG:
exception(msg) exception(msg)
return return
else: else:
error(msg) error(msg)
return return
if module.get_name() in self.modules: if backend.name in self.loaded:
warning('Module "%s" is already loaded (%s)' % (self.modules[module.get_name()].module, name)) debug('Backend "%s" is already loaded from %s' % (name, backend.package.__path__[0]))
return return
self.modules[module.get_name()] = module self.loaded[backend.name] = backend
debug('Loaded module "%s" from %s' % (name, module.module.__path__)) debug('Loaded backend "%s" from %s' % (name, backend.package.__path__[0]))

View file

@ -23,9 +23,9 @@ import os
import sys import sys
from weboob.core.bcall import BackendsCall from weboob.core.bcall import BackendsCall
from weboob.core.modules import ModulesLoader, BackendsConfig from weboob.core.backends import BackendsConfig, BackendsLoader
from weboob.core.backend import BaseBackend
from weboob.core.scheduler import Scheduler from weboob.core.scheduler import Scheduler
from weboob.tools.backend import BaseBackend
if sys.version_info[:2] <= (2, 5): if sys.version_info[:2] <= (2, 5):
import weboob.tools.property import weboob.tools.property
@ -40,7 +40,7 @@ class Weboob(object):
def __init__(self, workdir=WORKDIR, backends_filename=None, scheduler=None): def __init__(self, workdir=WORKDIR, backends_filename=None, scheduler=None):
self.workdir = workdir self.workdir = workdir
self.backends = {} self.backend_instances = {}
# Scheduler # Scheduler
if scheduler is None: if scheduler is None:
@ -53,10 +53,10 @@ class Weboob(object):
elif not os.path.isdir(self.workdir): elif not os.path.isdir(self.workdir):
warning('"%s" is not a directory' % self.workdir) warning('"%s" is not a directory' % self.workdir)
# Modules loader # Backends loader
self.modules_loader = ModulesLoader() self.backends_loader = BackendsLoader()
# Backends config # Backend instances config
if not backends_filename: if not backends_filename:
backends_filename = os.path.join(self.workdir, self.BACKENDS_FILENAME) backends_filename = os.path.join(self.workdir, self.BACKENDS_FILENAME)
elif not backends_filename.startswith('/'): elif not backends_filename.startswith('/'):
@ -64,41 +64,26 @@ class Weboob(object):
self.backends_config = BackendsConfig(backends_filename) self.backends_config = BackendsConfig(backends_filename)
def load_backends(self, caps=None, names=None, storage=None): def load_backends(self, caps=None, names=None, storage=None):
loaded_backends = {} loaded = {}
for name, _type, params in self.backends_config.iter_backends(): self.backends_loader.load_all()
try: for backend_name, backend in self.backends_loader.loaded.iteritems():
module = self.modules_loader.get_or_load_module(_type) if caps is not None and not backend.has_caps(caps) or \
except KeyError: names is not None and backend_name not in names:
warning(u'Unable to find module "%s" for backend "%s"' % (_type, name))
continue continue
backend_instance = backend.create_instance(self, backend_name, {}, storage)
self.backend_instances[backend_name] = loaded[backend_name] = backend_instance
return loaded
# Check conditions def load_configured_backends(self, caps=None, names=None, storage=None):
if caps is not None and not module.has_caps(caps) or \ loaded = {}
names is not None and name not in names: for instance_name, backend_name, params in self.backends_config.iter_backends():
backend = self.backends_loader.get_or_load_backend(backend_name)
if caps is not None and not backend.has_caps(caps) or \
names is not None and instance_name not in names:
continue continue
backend_instance = backend.create_instance(self, instance_name, params, storage)
try: self.backend_instances[instance_name] = loaded[instance_name] = backend_instance
self.backends[name] = module.create_backend(self, name, params, storage) return loaded
loaded_backends[name] = self.backends[name]
except Exception, e:
warning(u'Unable to load "%s" backend: %s. filename=%s' % (name, e, self.backends_config.confpath))
return loaded_backends
def load_modules(self, caps=None, names=None, storage=None):
loaded_backends = {}
self.modules_loader.load()
for name, module in self.modules_loader.modules.iteritems():
if caps is not None and not module.has_caps(caps) or \
names is not None and name not in names:
continue
try:
name = module.get_name()
self.backends[name] = module.create_backend(self, name, {}, storage)
loaded_backends[name] = self.backends[name]
except Exception, e:
warning(u'Unable to load "%s" module as backend with no config: %s' % (name, e))
return loaded_backends
def iter_backends(self, caps=None): def iter_backends(self, caps=None):
""" """
@ -109,7 +94,7 @@ class Weboob(object):
@param caps Optional list of capabilities to select backends @param caps Optional list of capabilities to select backends
@return iterator on selected backends. @return iterator on selected backends.
""" """
for name, backend in sorted(self.backends.iteritems()): for name, backend in sorted(self.backend_instances.iteritems()):
if caps is None or backend.has_caps(caps): if caps is None or backend.has_caps(caps):
with backend: with backend:
yield backend yield backend
@ -134,7 +119,7 @@ class Weboob(object):
def do_caps(self, caps, function, *args, **kwargs): def do_caps(self, caps, function, *args, **kwargs):
""" """
Do calls on loaded modules with the specified capabilities, in Do calls on loaded backends with the specified capabilities, in
separated threads. separated threads.
See also documentation of the 'do' method. See also documentation of the 'do' method.
@ -156,14 +141,14 @@ class Weboob(object):
elif isinstance(backends, (list,tuple)): elif isinstance(backends, (list,tuple)):
old_backends = backends old_backends = backends
backends = [] backends = []
for b in old_backends: for backend in old_backends:
if isinstance(b, (str,unicode)): if isinstance(backend, (str,unicode)):
try: try:
backends.append(self.backends[self.backends.index(b)]) backends.append(self.backends[self.backends.index(backend)])
except ValueError: except ValueError:
pass pass
else: else:
backends.append(b) backends.append(backend)
return BackendsCall(backends, function, *args, **kwargs) return BackendsCall(backends, function, *args, **kwargs)
def schedule(self, interval, function, *args): def schedule(self, interval, function, *args):

View file

@ -32,28 +32,28 @@ class BackendNotFound(Exception):
pass pass
class FrontendStorage(object): class ApplicationStorage(object):
def __init__(self, name, storage): def __init__(self, name, storage):
self.name = name self.name = name
self.storage = storage self.storage = storage
def set(self, *args): def set(self, *args):
if self.storage: if self.storage:
return self.storage.set('frontends', self.name, *args) return self.storage.set('applications', self.name, *args)
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
if self.storage: if self.storage:
return self.storage.get('frontends', self.name, *args, **kwargs) return self.storage.get('applications', self.name, *args, **kwargs)
else: else:
return kwargs.get('default', None) return kwargs.get('default', None)
def load(self, default): def load(self, default):
if self.storage: if self.storage:
return self.storage.load('frontends', self.name, default) return self.storage.load('applications', self.name, default)
def save(self): def save(self):
if self.storage: if self.storage:
return self.storage.save('frontends', self.name) return self.storage.save('applications', self.name)
class BaseApplication(object): class BaseApplication(object):
# Application name # Application name
@ -107,7 +107,7 @@ class BaseApplication(object):
path = os.path.join(self.CONFDIR, path) path = os.path.join(self.CONFDIR, path)
storage = klass(path) storage = klass(path)
self.storage = FrontendStorage(self.APPNAME, storage) self.storage = ApplicationStorage(self.APPNAME, storage)
self.storage.load(self.STORAGE) self.storage.load(self.STORAGE)
return storage return storage
@ -138,18 +138,18 @@ class BaseApplication(object):
def load_backends(self, caps=None, names=None, *args, **kwargs): def load_backends(self, caps=None, names=None, *args, **kwargs):
if names is None: if names is None:
names = self.requested_backends names = self.requested_backends
loaded_backends = self.weboob.load_backends(caps, names, *args, **kwargs) loaded = self.weboob.load_backends(caps, names, *args, **kwargs)
if not loaded_backends: if not loaded:
logging.warning(u'No backend loaded') logging.warning(u'No backend loaded')
return loaded_backends return loaded
def load_modules(self, caps=None, names=None, *args, **kwargs): def load_configured_backends(self, caps=None, names=None, *args, **kwargs):
if names is None: if names is None:
names = self.requested_backends names = self.requested_backends
loaded_modules = self.weboob.load_modules(caps, names, *args, **kwargs) loaded = self.weboob.load_configured_backends(caps, names, *args, **kwargs)
if not loaded_modules: if not loaded:
logging.warning(u'No module loaded') logging.warning(u'No configured backend loaded')
return loaded_modules return loaded
def _get_completions(self): def _get_completions(self):
""" """
@ -201,9 +201,9 @@ class BaseApplication(object):
logging.basicConfig(stream=sys.stdout, level=level, format=log_format) logging.basicConfig(stream=sys.stdout, level=level, format=log_format)
app.requested_backends = app.options.backends.split(',') if app.options.backends else None app.requested_backends = app.options.backends.split(',') if app.options.backends else None
if app.requested_backends: if app.requested_backends:
existing_module_names = list(app.weboob.modules_loader.iter_existing_module_names()) existing_backend_names = list(app.weboob.backends_loader.iter_existing_backend_names())
for requested_backend in app.requested_backends: for requested_backend in app.requested_backends:
if requested_backend not in existing_module_names: if requested_backend not in existing_backend_names:
raise BackendNotFound(u'Unknown backend: "%s"' % requested_backend) raise BackendNotFound(u'Unknown backend: "%s"' % requested_backend)
app._handle_app_options() app._handle_app_options()

View file

@ -24,7 +24,7 @@ import re
import sys import sys
from weboob.core import CallErrors from weboob.core import CallErrors
from weboob.core.modules import BackendsConfig from weboob.core.backends import BackendsConfig
from .base import BackendNotFound, BaseApplication from .base import BackendNotFound, BaseApplication
from .formatters.load import formatters, load_formatter from .formatters.load import formatters, load_formatter

View file

@ -35,50 +35,50 @@ class BackendCfg(QDialog):
self.caps = caps self.caps = caps
self.config_widgets = {} self.config_widgets = {}
self.weboob.modules_loader.load() self.weboob.backends_loader.load_all()
self.ui.backendsList.header().setResizeMode(QHeaderView.ResizeToContents) self.ui.configuredBackendsList.header().setResizeMode(QHeaderView.ResizeToContents)
self.ui.configFrame.hide() self.ui.configFrame.hide()
for name, module in self.weboob.modules_loader.modules.iteritems(): for name, backend in self.weboob.backends_loader.loaded.iteritems():
if not self.caps or module.has_caps(*self.caps): if not self.caps or backend.has_caps(*self.caps):
item = QListWidgetItem(name.capitalize()) item = QListWidgetItem(name.capitalize())
if module.get_icon_path(): if backend.icon_path:
img = QImage(module.get_icon_path()) img = QImage(backend.icon_path)
item.setIcon(QIcon(QPixmap.fromImage(img))) item.setIcon(QIcon(QPixmap.fromImage(img)))
self.ui.modulesList.addItem(item) self.ui.backendsList.addItem(item)
self.loadBackendsList() self.loadConfiguredBackendsList()
self.connect(self.ui.backendsList, SIGNAL('itemClicked(QTreeWidgetItem *, int)'), self.backendClicked) self.connect(self.ui.configuredBackendsList, SIGNAL('itemClicked(QTreeWidgetItem *, int)'), self.configuredBackendClicked)
self.connect(self.ui.modulesList, SIGNAL('itemSelectionChanged()'), self.modulesSelectionChanged) self.connect(self.ui.backendsList, SIGNAL('itemSelectionChanged()'), self.backendSelectionChanged)
self.connect(self.ui.proxyBox, SIGNAL('toggled(bool)'), self.proxyEditEnabled) self.connect(self.ui.proxyBox, SIGNAL('toggled(bool)'), self.proxyEditEnabled)
self.connect(self.ui.addButton, SIGNAL('clicked()'), self.addEvent) self.connect(self.ui.addButton, SIGNAL('clicked()'), self.addEvent)
self.connect(self.ui.removeButton, SIGNAL('clicked()'), self.removeEvent) self.connect(self.ui.removeButton, SIGNAL('clicked()'), self.removeEvent)
self.connect(self.ui.configButtonBox, SIGNAL('accepted()'), self.acceptBackend) self.connect(self.ui.configButtonBox, SIGNAL('accepted()'), self.acceptBackend)
self.connect(self.ui.configButtonBox, SIGNAL('rejected()'), self.rejectBackend) self.connect(self.ui.configButtonBox, SIGNAL('rejected()'), self.rejectBackend)
def loadBackendsList(self): def loadConfiguredBackendsList(self):
self.ui.backendsList.clear() self.ui.configuredBackendsList.clear()
for instance_name, name, params in self.weboob.backends_config.iter_backends(): for instance_name, name, params in self.weboob.backends_config.iter_backends():
module = self.weboob.modules_loader.modules[name] backend = self.weboob.backends_loader.loaded[name]
if self.caps and not module.has_caps(*self.caps): if self.caps and not backend.has_caps(*self.caps):
continue continue
item = QTreeWidgetItem(None, [instance_name, name]) item = QTreeWidgetItem(None, [instance_name, name])
if module.get_icon_path(): if backend.icon_path:
img = QImage(module.get_icon_path()) img = QImage(backend.icon_path)
item.setIcon(0, QIcon(QPixmap.fromImage(img))) item.setIcon(0, QIcon(QPixmap.fromImage(img)))
self.ui.backendsList.addTopLevelItem(item) self.ui.configuredBackendsList.addTopLevelItem(item)
def closeEvent(self, event): def closeEvent(self, event):
event.accept() event.accept()
def backendClicked(self, item, col): def configuredBackendClicked(self, item, col):
bname = unicode(item.text(0)) bname = unicode(item.text(0))
self.editBackend(bname) self.editBackend(bname)
@ -87,7 +87,7 @@ class BackendCfg(QDialog):
self.editBackend() self.editBackend()
def removeEvent(self): def removeEvent(self):
item = self.ui.backendsList.currentItem() item = self.ui.configuredBackendsList.currentItem()
if not item: if not item:
return return
@ -100,7 +100,7 @@ class BackendCfg(QDialog):
return return
self.weboob.backends_config.remove_backend(bname) self.weboob.backends_config.remove_backend(bname)
self.loadBackendsList() self.loadConfiguredBackendsList()
def editBackend(self, bname=None): def editBackend(self, bname=None):
self.ui.configFrame.show() self.ui.configFrame.show()
@ -108,12 +108,12 @@ class BackendCfg(QDialog):
if bname is not None: if bname is not None:
mname, params = self.weboob.backends_config.get_backend(bname) mname, params = self.weboob.backends_config.get_backend(bname)
items = self.ui.modulesList.findItems(mname, Qt.MatchFixedString) items = self.ui.backendsList.findItems(mname, Qt.MatchFixedString)
if not items: if not items:
print 'Module not found' print 'Backend not found'
else: else:
self.ui.modulesList.setCurrentItem(items[0]) self.ui.backendsList.setCurrentItem(items[0])
self.ui.modulesList.setEnabled(False) self.ui.backendsList.setEnabled(False)
self.ui.nameEdit.setText(bname) self.ui.nameEdit.setText(bname)
self.ui.nameEdit.setEnabled(False) self.ui.nameEdit.setEnabled(False)
@ -137,19 +137,19 @@ class BackendCfg(QDialog):
self.ui.nameEdit.setEnabled(True) self.ui.nameEdit.setEnabled(True)
self.ui.proxyBox.setChecked(False) self.ui.proxyBox.setChecked(False)
self.ui.proxyEdit.clear() self.ui.proxyEdit.clear()
self.ui.modulesList.setEnabled(True) self.ui.backendsList.setEnabled(True)
self.ui.modulesList.setCurrentRow(-1) self.ui.backendsList.setCurrentRow(-1)
def acceptBackend(self): def acceptBackend(self):
bname = unicode(self.ui.nameEdit.text()) bname = unicode(self.ui.nameEdit.text())
selection = self.ui.modulesList.selectedItems() selection = self.ui.backendsList.selectedItems()
if not selection: if not selection:
QMessageBox.critical(self, self.tr('Unable to add a backend'), QMessageBox.critical(self, self.tr('Unable to add a configured backend'),
self.tr('Please select a module')) self.tr('Please select a backend'))
return return
module = self.weboob.modules_loader.modules[unicode(selection[0].text()).lower()] backend = self.weboob.backends_loader.loaded[unicode(selection[0].text()).lower()]
params = {} params = {}
missing = [] missing = []
@ -162,7 +162,7 @@ class BackendCfg(QDialog):
if not params['_proxy']: if not params['_proxy']:
missing.append(self.tr('Proxy')) missing.append(self.tr('Proxy'))
for key, field in module.get_config().iteritems(): for key, field in backend.config.iteritems():
label, value = self.config_widgets[key] label, value = self.config_widgets[key]
if isinstance(value, QLineEdit): if isinstance(value, QLineEdit):
@ -189,49 +189,49 @@ class BackendCfg(QDialog):
unicode(self.tr('Please set a value in this fields:\n%s')) % ('\n'.join(['- %s' % s for s in missing]))) unicode(self.tr('Please set a value in this fields:\n%s')) % ('\n'.join(['- %s' % s for s in missing])))
return return
self.weboob.backends_config.add_backend(bname, module.get_name(), params, edit=not self.ui.nameEdit.isEnabled()) self.weboob.backends_config.add_backend(bname, backend.name, params, edit=not self.ui.nameEdit.isEnabled())
self.ui.configFrame.hide() self.ui.configFrame.hide()
self.loadBackendsList() self.loadConfiguredBackendsList()
def rejectBackend(self): def rejectBackend(self):
self.ui.configFrame.hide() self.ui.configFrame.hide()
def modulesSelectionChanged(self): def backendSelectionChanged(self):
for key, (label, value) in self.config_widgets.iteritems(): for key, (label, value) in self.config_widgets.iteritems():
label.hide() label.hide()
value.hide() value.hide()
self.ui.configLayout.removeWidget(label) self.ui.configLayout.removeWidget(label)
self.ui.configLayout.removeWidget(value) self.ui.configLayout.removeWidget(value)
self.config_widgets.clear() self.config_widgets.clear()
self.ui.moduleInfo.clear() self.ui.backendInfo.clear()
selection = self.ui.modulesList.selectedItems() selection = self.ui.backendsList.selectedItems()
if not selection: if not selection:
return return
module = self.weboob.modules_loader.modules[unicode(selection[0].text()).lower()] backend = self.weboob.backends_loader.loaded[unicode(selection[0].text()).lower()]
if module.get_icon_path(): if backend.icon_path:
img = QImage(module.get_icon_path()) img = QImage(backend.icon_path)
self.ui.moduleInfo.document().addResource(QTextDocument.ImageResource, QUrl('mydata://logo.png'), QVariant(img)) self.ui.backendInfo.document().addResource(QTextDocument.ImageResource, QUrl('mydata://logo.png'), QVariant(img))
self.ui.moduleInfo.setText(unicode(self.tr( self.ui.backendInfo.setText(unicode(self.tr(
'<h1>%s Module %s</h1>' '<h1>%s Backend %s</h1>'
'<b>Version</b>: %s<br />' '<b>Version</b>: %s<br />'
'<b>Maintainer</b>: %s<br />' '<b>Maintainer</b>: %s<br />'
'<b>License</b>: %s<br />' '<b>License</b>: %s<br />'
'<b>Description</b>: %s<br />' '<b>Description</b>: %s<br />'
'<b>Capabilities</b>: %s<br />')) '<b>Capabilities</b>: %s<br />'))
% ('<img src="mydata://logo.png" />' if module.get_icon_path() else '', % ('<img src="mydata://logo.png" />' if backend.icon_path else '',
module.get_name().capitalize(), backend.name.capitalize(),
module.get_version(), backend.version,
module.get_maintainer().replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;'), backend.maintainer.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;'),
module.get_license(), backend.license,
module.get_description(), backend.description,
', '.join([cap.__name__ for cap in module.iter_caps()]))) ', '.join([cap.__name__ for cap in backend.iter_caps()])))
for key, field in module.get_config().iteritems(): for key, field in backend.config.iteritems():
label = QLabel(u'%s:' % field.description) label = QLabel(u'%s:' % field.description)
if isinstance(field.default, bool): if isinstance(field.default, bool):
value = QCheckBox() value = QCheckBox()

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>626</width> <width>645</width>
<height>614</height> <height>652</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -22,7 +22,7 @@
<widget class="QWidget" name="horizontalLayoutWidget"> <widget class="QWidget" name="horizontalLayoutWidget">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QTreeWidget" name="backendsList"> <widget class="QTreeWidget" name="configuredBackendsList">
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
</property> </property>
@ -72,7 +72,7 @@
</column> </column>
<column> <column>
<property name="text"> <property name="text">
<string>Module</string> <string>Backend</string>
</property> </property>
</column> </column>
</widget> </widget>
@ -125,99 +125,107 @@
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Raised</enum> <enum>QFrame::Raised</enum>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QSplitter" name="splitter"> <layout class="QVBoxLayout" name="verticalLayout_2">
<property name="orientation"> <item>
<enum>Qt::Horizontal</enum> <widget class="QLabel" name="label_2">
<property name="text">
<string>Available backends</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="backendsList">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="spacing">
<number>1</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<widget class="QListWidget" name="modulesList"> <property name="frameShape">
<property name="sizePolicy"> <enum>QFrame::NoFrame</enum>
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> </property>
<horstretch>0</horstretch> <property name="frameShadow">
<verstretch>0</verstretch> <enum>QFrame::Plain</enum>
</sizepolicy> </property>
</property> <layout class="QVBoxLayout" name="verticalLayout_4">
<property name="iconSize"> <item>
<size> <widget class="QTextEdit" name="backendInfo">
<width>32</width> <property name="sizePolicy">
<height>32</height> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
</size> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<property name="spacing"> </sizepolicy>
<number>1</number> </property>
</property> <property name="readOnly">
<property name="sortingEnabled"> <bool>true</bool>
<bool>true</bool> </property>
</property> </widget>
</widget> </item>
<widget class="QFrame" name="frame_2"> <item>
<property name="sizePolicy"> <layout class="QFormLayout" name="configLayout">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <item row="0" column="0">
<horstretch>1</horstretch> <widget class="QLabel" name="label">
<verstretch>0</verstretch> <property name="text">
</sizepolicy> <string>Name:</string>
</property> </property>
<property name="frameShape"> </widget>
<enum>QFrame::NoFrame</enum> </item>
</property> <item row="0" column="1">
<property name="frameShadow"> <widget class="QLineEdit" name="nameEdit"/>
<enum>QFrame::Plain</enum> </item>
</property> <item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_4"> <widget class="QCheckBox" name="proxyBox">
<item> <property name="text">
<layout class="QFormLayout" name="configLayout"> <string>Proxy:</string>
<item row="0" column="0"> </property>
<widget class="QLabel" name="label"> </widget>
<property name="text"> </item>
<string>Name:</string> <item row="1" column="1">
</property> <widget class="QLineEdit" name="proxyEdit">
</widget> <property name="enabled">
</item> <bool>false</bool>
<item row="0" column="1"> </property>
<widget class="QLineEdit" name="nameEdit"/> </widget>
</item> </item>
<item row="1" column="0"> </layout>
<widget class="QCheckBox" name="proxyBox"> </item>
<property name="text"> <item>
<string>Proxy:</string> <widget class="QDialogButtonBox" name="configButtonBox">
</property> <property name="orientation">
</widget> <enum>Qt::Horizontal</enum>
</item> </property>
<item row="1" column="1"> <property name="standardButtons">
<widget class="QLineEdit" name="proxyEdit"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<property name="enabled"> </property>
<bool>false</bool> </widget>
</property> </item>
</widget> </layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="configButtonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="moduleInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>