diff --git a/weboob/tools/application/console.py b/weboob/tools/application/console.py index a6c978d0..c5f6fdc0 100644 --- a/weboob/tools/application/console.py +++ b/weboob/tools/application/console.py @@ -18,7 +18,7 @@ # along with weboob. If not, see . -from copy import deepcopy +from copy import copy import getpass import logging import sys @@ -64,8 +64,12 @@ class ConsoleApplication(BaseApplication): def __init__(self, option_parser=None): BaseApplication.__init__(self, option_parser) + self.weboob.callbacks['login'] = self.login_cb self.enabled_backends = set() + def login_cb(self, backend_name, value): + return self.ask('[%s] Password' % backend_name, masked=True, default='', regexp=value.regexp) + def unload_backends(self, *args, **kwargs): unloaded = self.weboob.unload_backends(*args, **kwargs) for backend in unloaded.itervalues(): @@ -221,8 +225,8 @@ class ConsoleApplication(BaseApplication): asked_config = True print 'Configuration of new account %s' % website print '-----------------------------%s' % ('-' * len(website)) - p = deepcopy(prop) - p.set_value(self.ask(prop, default=account.properties[key].value if (key in account.properties) else prop.default)) + p = copy(prop) + p.set(self.ask(prop, default=account.properties[key].get() if (key in account.properties) else prop.default)) account.properties[key] = p if asked_config: print '-----------------------------%s' % ('-' * len(website)) @@ -239,7 +243,7 @@ class ConsoleApplication(BaseApplication): backend_config = {} for key, value in account.properties.iteritems(): if key in backend.config: - backend_config[key] = value.value + backend_config[key] = value.get() if ask_add and self.ask('Do you want to add the new register account?', default=True): return self.add_backend(name, backend_config, ask_register=False) @@ -253,9 +257,12 @@ class ConsoleApplication(BaseApplication): if params is None: params = {} + backend = None + config = None if not edit: try: backend = self.weboob.modules_loader.get_or_load_module(name) + config = backend.config except ModuleLoadError: backend = None else: @@ -264,15 +271,17 @@ class ConsoleApplication(BaseApplication): backend = self.weboob.modules_loader.get_or_load_module(bname) except ModuleLoadError: backend = None - items.update(params) - params = items + else: + items.update(params) + params = items + config = backend.config.load(self.weboob, bname, name, params, nofail=True) if not backend: print >>sys.stderr, 'Backend "%s" does not exist. Hint: use the "backends" command.' % name return 1 # ask for params non-specified on command-line arguments asked_config = False - for key, value in backend.config.iteritems(): + for key, value in config.iteritems(): if not asked_config: asked_config = True print 'Configuration of backend' @@ -284,21 +293,23 @@ class ConsoleApplication(BaseApplication): if asked_config: print '------------------------' + while not edit and self.weboob.backends_config.backend_exists(name): + print >>sys.stderr, 'Backend instance "%s" already exists in "%s"' % (name, self.weboob.backends_config.confpath) + if not self.ask('Add new instance of "%s" backend?' % backend.name, default=False): + return 1 + + name = self.ask('Please give new instance name (could be "%s_1")' % backend.name, regexp=r'^[\w\-_]+$') + try: - self.weboob.backends_config.add_backend(name, name, params, edit=edit) - print 'Backend "%s" successfully %s.' % (name, 'updated' if edit else 'added') + config = config.load(self.weboob, backend.name, name, params, nofail=True) + for key, value in params.iteritems(): + config[key].set(value) + config.save(edit=edit) + print 'Backend "%s" successfully added.' % name return name except BackendAlreadyExists: - print >>sys.stderr, 'Backend "%s" is already configured in file "%s"' % (name, self.weboob.backends_config.confpath) - while self.ask('Add new instance of "%s" backend?' % name, default=False): - new_name = self.ask('Please give new instance name (could be "%s_1")' % name, regexp=r'^[\w\-_]+$') - try: - self.weboob.backends_config.add_backend(new_name, name, params) - print 'Backend "%s" successfully added.' % new_name - return new_name - except BackendAlreadyExists: - print >>sys.stderr, 'Instance "%s" already exists for backend "%s".' % (new_name, name) - return 1 + print >>sys.stderr, 'Instance "%s" already exists.' % name + return 1 def ask(self, question, default=None, masked=False, regexp=None, choices=None): """ @@ -313,7 +324,7 @@ class ConsoleApplication(BaseApplication): """ if isinstance(question, Value): - v = deepcopy(question) + v = copy(question) if default: v.default = default if masked: @@ -388,13 +399,13 @@ class ConsoleApplication(BaseApplication): line = aliases[line] try: - v.set_value(line) + v.set(line) except ValueError, e: print >>sys.stderr, 'Error: %s' % e else: break - return v.value + return v.get() def bcall_error_handler(self, backend, error, backtrace): """ diff --git a/weboob/tools/application/qt/backendcfg.py b/weboob/tools/application/qt/backendcfg.py index 7990bb95..6e11f6ba 100644 --- a/weboob/tools/application/qt/backendcfg.py +++ b/weboob/tools/application/qt/backendcfg.py @@ -62,13 +62,14 @@ class BackendCfg(QDialog): self.ui.configuredBackendsList.header().setResizeMode(QHeaderView.ResizeToContents) self.ui.configFrame.hide() + self.icon_cache = {} + for name, backend in self.weboob.modules_loader.loaded.iteritems(): if not self.caps or backend.has_caps(*self.caps): item = QListWidgetItem(name.capitalize()) if backend.icon_path: - img = QImage(backend.icon_path) - item.setIcon(QIcon(QPixmap.fromImage(img))) + item.setIcon(self.get_icon_cache(backend.icon_path)) self.ui.backendsList.addItem(item) @@ -86,6 +87,12 @@ class BackendCfg(QDialog): self.connect(self.ui.configButtonBox, SIGNAL('accepted()'), self.acceptBackend) self.connect(self.ui.configButtonBox, SIGNAL('rejected()'), self.rejectBackend) + def get_icon_cache(self, path): + if not path in self.icon_cache: + img = QImage(path) + self.icon_cache[path] = QIcon(QPixmap.fromImage(img)) + return self.icon_cache[path] + def loadConfiguredBackendsList(self): self.ui.configuredBackendsList.clear() for instance_name, name, params in self.weboob.backends_config.iter_backends(): @@ -102,8 +109,7 @@ class BackendCfg(QDialog): else Qt.Unchecked) if backend.icon_path: - img = QImage(backend.icon_path) - item.setIcon(0, QIcon(QPixmap.fromImage(img))) + item.setIcon(0, self.get_icon_cache(backend.icon_path)) self.ui.configuredBackendsList.addTopLevelItem(item) @@ -159,22 +165,23 @@ class BackendCfg(QDialog): self.ui.configFrame.hide() self.loadConfiguredBackendsList() - def editBackend(self, bname=None): + def editBackend(self, name=None): self.ui.registerButton.hide() self.ui.configFrame.show() - if bname is not None: - mname, params = self.weboob.backends_config.get_backend(bname) + if name is not None: + bname, params = self.weboob.backends_config.get_backend(name) - items = self.ui.backendsList.findItems(mname, Qt.MatchFixedString) + items = self.ui.backendsList.findItems(bname, Qt.MatchFixedString) if not items: warning('Backend not found') else: self.ui.backendsList.setCurrentItem(items[0]) self.ui.backendsList.setEnabled(False) - self.ui.nameEdit.setText(bname) + self.ui.nameEdit.setText(name) self.ui.nameEdit.setEnabled(False) + if '_proxy' in params: self.ui.proxyBox.setChecked(True) self.ui.proxyEdit.setText(params.pop('_proxy')) @@ -184,13 +191,14 @@ class BackendCfg(QDialog): params.pop('_enabled', None) - for key, value in params.iteritems(): + backend = self.weboob.modules_loader.loaded[bname] + for key, value in backend.config.load(self.weboob, bname, name, params, nofail=True).iteritems(): try: l, widget = self.config_widgets[key] except KeyError: warning('Key "%s" is not found' % key) else: - widget.set_data(value) + widget.set_value(value) else: self.ui.nameEdit.clear() self.ui.nameEdit.setEnabled(True) @@ -199,69 +207,6 @@ class BackendCfg(QDialog): self.ui.backendsList.setEnabled(True) self.ui.backendsList.setCurrentRow(-1) - def acceptBackend(self): - bname = unicode(self.ui.nameEdit.text()) - selection = self.ui.backendsList.selectedItems() - - if not selection: - QMessageBox.critical(self, self.tr('Unable to add a configured backend'), - self.tr('Please select a backend')) - return - - try: - backend = self.weboob.modules_loader.get_or_load_module(unicode(selection[0].text()).lower()) - except ModuleLoadError: - backend = None - - if not backend: - QMessageBox.critical(self, self.tr('Unable to add a configured backend'), - self.tr('The selected backend does not exist.')) - return - - params = {} - - if not bname: - QMessageBox.critical(self, self.tr('Missing field'), self.tr('Please specify a backend name')) - return - - if self.ui.nameEdit.isEnabled() and not re.match(r'^[\w\-_]+$', bname): - QMessageBox.critical(self, self.tr('Invalid value'), - self.tr('The backend name can only contain letters and digits')) - return - - if self.ui.proxyBox.isChecked(): - params['_proxy'] = unicode(self.ui.proxyEdit.text()) - if not params['_proxy']: - QMessageBox.critical(self, self.tr('Missing field'), self.tr('Please specify a proxy URL')) - return - - for key, field in backend.config.iteritems(): - label, qtvalue = self.config_widgets[key] - - try: - value = qtvalue.get_value() - except ValueError, e: - QMessageBox.critical(self, self.tr('Invalid value'), - unicode(self.tr('Invalid value for field "%s":

%s')) % (field.label, e)) - return - - params[key] = value.value - - try: - self.weboob.backends_config.add_backend(bname, backend.name, params, edit=not self.ui.nameEdit.isEnabled()) - except BackendAlreadyExists, e: - QMessageBox.critical(self, self.tr('Unable to create backend'), - unicode(self.tr('Unable to create backend "%s": it already exists')) % bname) - return - - self.to_load.add(bname) - self.ui.configFrame.hide() - - self.loadConfiguredBackendsList() - - def rejectBackend(self): - self.ui.configFrame.hide() - def backendSelectionChanged(self): for key, (label, value) in self.config_widgets.iteritems(): label.hide() @@ -288,6 +233,7 @@ class BackendCfg(QDialog): self.ui.nameEdit.setText(backend.name) else: self.ui.nameEdit.setText('') + self.ui.backendInfo.setText(unicode(self.tr( '

%s Backend %s

' 'Version: %s
' @@ -313,13 +259,82 @@ class BackendCfg(QDialog): for key, field in backend.config.iteritems(): label = QLabel(u'%s:' % field.label) - value = QtValue(field) - self.ui.configLayout.addRow(label, value) - self.config_widgets[key] = (label, value) + qvalue = QtValue(field) + self.ui.configLayout.addRow(label, qvalue) + self.config_widgets[key] = (label, qvalue) def proxyEditEnabled(self, state): self.ui.proxyEdit.setEnabled(state) + def acceptBackend(self): + bname = unicode(self.ui.nameEdit.text()) + selection = self.ui.backendsList.selectedItems() + + if not selection: + QMessageBox.critical(self, self.tr('Unable to add a configured backend'), + self.tr('Please select a backend')) + return + + try: + backend = self.weboob.modules_loader.get_or_load_module(unicode(selection[0].text()).lower()) + except ModuleLoadError: + backend = None + + if not backend: + QMessageBox.critical(self, self.tr('Unable to add a configured backend'), + self.tr('The selected backend does not exist.')) + return + + params = {} + + if not bname: + QMessageBox.critical(self, self.tr('Missing field'), self.tr('Please specify a backend name')) + return + + if self.ui.nameEdit.isEnabled(): + if not re.match(r'^[\w\-_]+$', bname): + QMessageBox.critical(self, self.tr('Invalid value'), + self.tr('The backend name can only contain letters and digits')) + return + if self.weboob.backends_config.backend_exists(bname): + QMessageBox.critical(self, self.tr('Unable to create backend'), + unicode(self.tr('Unable to create backend "%s": it already exists')) % bname) + return + + if self.ui.proxyBox.isChecked(): + params['_proxy'] = unicode(self.ui.proxyEdit.text()) + if not params['_proxy']: + QMessageBox.critical(self, self.tr('Missing field'), self.tr('Please specify a proxy URL')) + return + + config = backend.config.load(self.weboob, backend.name, bname, {}, nofail=True) + for key, field in config.iteritems(): + label, qtvalue = self.config_widgets[key] + + try: + value = qtvalue.get_value() + except ValueError, e: + QMessageBox.critical(self, self.tr('Invalid value'), + unicode(self.tr('Invalid value for field "%s":

%s')) % (field.label, e)) + return + + field.set(value.get()) + + try: + config.save(edit=not self.ui.nameEdit.isEnabled(), params=params) + except BackendAlreadyExists: + QMessageBox.critical(self, self.tr('Unable to create backend'), + unicode(self.tr('Unable to create backend "%s": it already exists')) % bname) + return + + self.to_load.add(bname) + self.ui.configFrame.hide() + + self.loadConfiguredBackendsList() + + def rejectBackend(self): + self.ui.configFrame.hide() + def registerEvent(self): selection = self.ui.backendsList.selectedItems() if not selection: @@ -380,7 +395,7 @@ class BackendCfg(QDialog): else: for key, value in account.properties.iteritems(): if key in self.config_widgets: - self.config_widgets[key][1].set_data(value.value) + self.config_widgets[key][1].set_value(value) def run(self): self.exec_() diff --git a/weboob/tools/application/qt/qt.py b/weboob/tools/application/qt/qt.py index 7d9bf7d1..730f283a 100644 --- a/weboob/tools/application/qt/qt.py +++ b/weboob/tools/application/qt/qt.py @@ -20,16 +20,18 @@ import sys import logging import re -from copy import deepcopy -from PyQt4.QtCore import QTimer, SIGNAL, QObject, QString, QSize, QVariant +from threading import Event +from copy import copy +from PyQt4.QtCore import QTimer, SIGNAL, QObject, QString, QSize, QVariant, QMutex from PyQt4.QtGui import QMainWindow, QApplication, QStyledItemDelegate, \ QStyleOptionViewItemV4, QTextDocument, QStyle, \ QAbstractTextDocumentLayout, QPalette, QMessageBox, \ - QSpinBox, QLineEdit, QComboBox, QCheckBox + QSpinBox, QLineEdit, QComboBox, QCheckBox, QInputDialog, \ + QLineEdit from weboob.core.ouiboube import Weboob from weboob.core.scheduler import IScheduler -from weboob.tools.value import ValueInt, ValueBool +from weboob.tools.value import ValueInt, ValueBool, ValueBackendPassword from ..base import BaseApplication @@ -77,12 +79,63 @@ class QtScheduler(IScheduler): def run(self): self.app.exec_() +class QCallbacksManager(QObject): + class Request(object): + def __init__(self): + self.event = Event() + self.answer = None + + def __call__(self): + raise NotImplementedError() + + class LoginRequest(Request): + def __init__(self, backend_name, value): + QCallbacksManager.Request.__init__(self) + self.backend_name = backend_name + self.value = value + + def __call__(self): + password, ok = QInputDialog.getText(None, + 'Password request', + 'Please enter password for %s' % self.backend_name, + QLineEdit.Password) + return password + + def __init__(self, weboob, parent=None): + QObject.__init__(self, parent) + self.weboob = weboob + self.weboob.callbacks['login'] = self.callback(self.LoginRequest) + self.mutex = QMutex() + self.requests = [] + self.connect(self, SIGNAL('new_request'), self.do_request) + + def callback(self, klass): + def cb(*args, **kwargs): + return self.add_request(klass(*args, **kwargs)) + return cb + + def do_request(self): + self.mutex.lock() + request = self.requests.pop() + request.answer = request() + request.event.set() + self.mutex.unlock() + + def add_request(self, request): + self.mutex.lock() + self.requests.append(request) + self.mutex.unlock() + self.emit(SIGNAL('new_request')) + request.event.wait() + return request.answer + class QtApplication(QApplication, BaseApplication): def __init__(self): QApplication.__init__(self, sys.argv) self.setApplicationName(self.APPNAME) BaseApplication.__init__(self) + self.cbmanager = QCallbacksManager(self.weboob, self) def create_weboob(self): return Weboob(scheduler=QtScheduler(self)) @@ -196,14 +249,19 @@ class _QtValueStr(QLineEdit): if value.masked: self.setEchoMode(self.Password) - def set_data(self, text): - self._value.set_value(unicode(text)) - self.setText(self._value.value) + def set_value(self, value): + self._value = value + self.setText(self._value.get()) def get_value(self): - self._value.set_value(unicode(self.text())) + self._value.set(unicode(self.text())) return self._value +class _QtValueBackendPassword(_QtValueStr): + def get_value(self): + self._value._domain = None + return _QtValueStr.get_value(self) + class _QtValueBool(QCheckBox): def __init__(self, value): QCheckBox.__init__(self) @@ -211,12 +269,12 @@ class _QtValueBool(QCheckBox): if value.default: self.setChecked(True) - def set_data(self, b): - self._value.set_value(b) - self.setChecked(self._value.value) + def set_value(self, value): + self._value = value + self.setChecked(self._value.get()) def get_value(self): - self._value.set_value(self.isChecked()) + self._value.set(self.isChecked()) return self._value class _QtValueInt(QSpinBox): @@ -226,12 +284,12 @@ class _QtValueInt(QSpinBox): if value.default: self.setValue(int(value.default)) - def set_data(self, i): - self._value.set_value(i) - self.setValue(self._value.value) + def set_value(self, value): + self._value = value + self.setValue(self._value.get()) def get_value(self): - self._value.set_value(self.getValue()) + self._value.set(self.getValue()) return self._value class _QtValueChoices(QComboBox): @@ -243,15 +301,15 @@ class _QtValueChoices(QComboBox): if value.default == k: self.setCurrentIndex(self.count()-1) - def set_data(self, c): - self._value.set_value(c) + def set_value(self, value): + self._value = value for i in xrange(self.count()): - if unicode(self.itemData(i).toString()) == self._value.value: + if unicode(self.itemData(i).toString()) == self._value.get(): self.setCurrentIndex(i) return def get_value(self): - self._value.set_value(unicode(self.itemData(self.currentIndex()).toString())) + self._value.set(unicode(self.itemData(self.currentIndex()).toString())) return self._value def QtValue(value): @@ -259,9 +317,11 @@ def QtValue(value): klass = _QtValueBool elif isinstance(value, ValueInt): klass = _QtValueInt + elif isinstance(value, ValueBackendPassword): + klass = _QtValueBackendPassword elif value.choices is not None: klass = _QtValueChoices else: klass = _QtValueStr - return klass(deepcopy(value)) + return klass(copy(value))