rename havesex and QHaveSex to havedate and QHaveDate

This commit is contained in:
Romain Bignon 2012-03-03 22:56:51 +01:00
commit 33a9bf71a8
26 changed files with 41 additions and 41 deletions

View file

@ -0,0 +1,9 @@
[Desktop Entry]
Name=QHaveDate
Comment=Optimize your probabilities to have sex on dating websites
Exec=qhavedate
Icon=qhavedate
Terminal=false
Type=Application
StartupNotify=true
Categories=Network;Chat;ContactManagement;Qt;

BIN
icons/havedate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
icons/qhavedate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

231
man/havedate.1 Normal file
View file

@ -0,0 +1,231 @@
.TH HAVEDATE 1 "03 March 2012" "havedate 0\&.b"
.SH NAME
havedate
.SH SYNOPSIS
.B havedate
[\-dqv] [\-b \fIbackends\fR] [\-cnfs] [\fIcommand\fR [\fIarguments\fR..]]
.br
.B havedate
[\-\-help] [\-\-version]
.SH DESCRIPTION
.LP
Console application allowing to interact with various dating websites and to optimize seduction algorithmically.
.SS Supported websites:
* aum ("Adopte un Mec" French dating website)
.SH HAVEDATE COMMANDS
.TP
\fBoptim\fR [list | start | edit | stop] \fIBACKEND\fR [\fIOPTIM\fR1 [\fIOPTIM\fR2 ...]]
.br
All dating backends offer optimization services. This command can be
.br
manage them.
.br
Use * us BACKEND value to apply command to all backends.
.br
.br
Commands:
.br
* list list all available optimizations of a backend
.br
* start start optimization services on a backend
.br
* edit configure an optimization service for a backend
.br
* stop stop optimization services on a backend
.TP
\fBprofile\fR \fIID\fR
.br
Display a profile
.TP
\fBquery\fR \fIID\fR
.br
Send a query to someone.
.SH WEBOOB COMMANDS
.TP
\fBbackends\fR [\fIACTION\fR] [\fIBACKEND_NAME\fR]...
.br
Select used backends.
.br
.br
ACTION is one of the following (default: list):
.br
* enable enable given backends
.br
* disable disable given backends
.br
* only enable given backends and disable the others
.br
* list display enabled and available backends
.br
* add add a backend
.br
* register register a new account on a website
.br
* edit edit a backend
.br
* remove remove a backend
.TP
\fBcd\fR [\fIPATH\fR]
.br
Follow a path.
.br
If empty, return home.
.TP
\fBcondition\fR [\fIEXPRESSION\fR | off]
.br
If an argument is given, set the condition expression used to filter the results.
.br
If the "off" value is given, conditional filtering is disabled.
.br
.br
If no argument is given, print the current condition expression.
.TP
\fBcount\fR [\fINUMBER\fR | off]
.br
If an argument is given, set the maximum number of results fetched.
.br
NUMBER must be at least 1.
.br
"off" value disables counting, and allows infinite searches.
.br
.br
If no argument is given, print the current count value.
.TP
\fBformatter\fR [list | \fIFORMATTER\fR [\fICOMMAND\fR] | option \fIOPTION_NAME\fR [on | off]]
.br
If a FORMATTER is given, set the formatter to use.
.br
You can add a COMMAND to apply the formatter change only to
.br
a given command.
.br
.br
If the argument is "list", print the available formatters.
.br
.br
If the argument is "option", set the formatter options.
.br
Valid options are: header, keys.
.br
If on/off value is given, set the value of the option.
.br
If not, print the current value for the option.
.br
.br
If no argument is given, print the current formatter.
.TP
\fBinspect\fR \fIBACKEND_NAME\fR
.br
Display the HTML string of the current page of the specified backend's browser.
.br
.br
If webkit_mechanize_browser Python module is installed, HTML is displayed in a WebKit GUI.
.TP
\fBlogging\fR [\fILEVEL\fR]
.br
Set logging level.
.br
.br
Availables: debug, info, warning, error.
.br
* quiet is an alias for error
.br
* default is an alias for warning
.TP
\fBls\fR
.br
List objects in current path.
.TP
\fBquit\fR
.br
Quit the application.
.TP
\fBselect\fR [\fIFIELD_NAME\fR]... | "$direct" | "$full"
.br
If an argument is given, set the selected fields.
.br
$direct selects all fields loaded in one http request.
.br
$full selects all fields using as much http requests as necessary.
.br
.br
If no argument is given, print the currently selected fields.
.SH OPTIONS
.TP
\fB\-\-version\fR
show program's version number and exit
.TP
\fB\-h\fR, \fB\-\-help\fR
show this help message and exit
.TP
\fB\-b BACKENDS\fR, \fB\-\-backends=BACKENDS\fR
what backend(s) to enable (comma separated)
.SH LOGGING OPTIONS
.TP
\fB\-d\fR, \fB\-\-debug\fR
display debug messages
.TP
\fB\-q\fR, \fB\-\-quiet\fR
display only error messages
.TP
\fB\-v\fR, \fB\-\-verbose\fR
display info messages
.TP
\fB\-\-logging\-file=LOGGING_FILE\fR
file to save logs
.TP
\fB\-a\fR, \fB\-\-save\-responses\fR
save every response
.SH RESULTS OPTIONS
.TP
\fB\-c CONDITION\fR, \fB\-\-condition=CONDITION\fR
filter result items to display given a boolean expression
.TP
\fB\-n COUNT\fR, \fB\-\-count=COUNT\fR
get a maximum number of results (all backends merged)
.TP
\fB\-s SELECT\fR, \fB\-\-select=SELECT\fR
select result item keys to display (comma separated)
.SH FORMATTING OPTIONS
.TP
\fB\-f FORMATTER\fR, \fB\-\-formatter=FORMATTER\fR
select output formatter (csv, htmltable, multiline, profile, simple, table,
webkit)
.TP
\fB\-\-no\-header\fR
do not display header
.TP
\fB\-\-no\-keys\fR
do not display item keys
.TP
\fB\-O OUTFILE\fR, \fB\-\-outfile=OUTFILE\fR
file to export result
.SH COPYRIGHT
Copyright(C) 2010-2012 Romain Bignon
.LP
For full COPYRIGHT see COPYING file with weboob package.
.LP
.RE
.SH FILES
"~/.weboob/backends"
.SH SEE ALSO
Home page: http://weboob.org/applications/havedate

56
man/qhavedate.1 Normal file
View file

@ -0,0 +1,56 @@
.TH QHAVEDATE 1 "03 March 2012" "qhavedate 0\&.b"
.SH NAME
qhavedate
.SH SYNOPSIS
.B qhavedate
[\-h] [\-dqv] [\-b \fIbackends\fR] ...
.br
.B qhavedate
[\-\-help] [\-\-version]
.SH DESCRIPTION
.LP
Qt application allowing to interact with various dating websites.
.SS Supported websites:
* aum ("Adopte un Mec" French dating website)
.SH OPTIONS
.TP
\fB\-\-version\fR
show program's version number and exit
.TP
\fB\-h\fR, \fB\-\-help\fR
show this help message and exit
.TP
\fB\-b BACKENDS\fR, \fB\-\-backends=BACKENDS\fR
what backend(s) to enable (comma separated)
.SH LOGGING OPTIONS
.TP
\fB\-d\fR, \fB\-\-debug\fR
display debug messages
.TP
\fB\-q\fR, \fB\-\-quiet\fR
display only error messages
.TP
\fB\-v\fR, \fB\-\-verbose\fR
display info messages
.TP
\fB\-\-logging\-file=LOGGING_FILE\fR
file to save logs
.TP
\fB\-a\fR, \fB\-\-save\-responses\fR
save every response
.SH COPYRIGHT
Copyright(C) 2010-2012 Romain Bignon
.LP
For full COPYRIGHT see COPYING file with weboob package.
.LP
.RE
.SH FILES
"~/.weboob/backends"
.SH SEE ALSO
Home page: http://weboob.org/applications/qhavedate

27
scripts/havedate Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ft=python et softtabstop=4 cinoptions=4 shiftwidth=4 ts=4 ai
# Copyright(C) 2010-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.applications.havedate import HaveDate
if __name__ == '__main__':
HaveDate.run()

27
scripts/qhavedate Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ft=python et softtabstop=4 cinoptions=4 shiftwidth=4 ts=4 ai
# Copyright(C) 2010-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.applications.qhavedate import QHaveDate
if __name__ == '__main__':
QHaveDate.run()

View file

@ -92,7 +92,7 @@ def build_qt():
extraMakeFlag = []
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qboobmsg/ui'], env=env )
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qhavesex/ui'], env=env )
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qhavedate/ui'], env=env )
if sys.platform != 'win32':
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qvideoob/ui'], env=env )
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qwebcontentedit/ui'], env=env )
@ -146,7 +146,7 @@ scripts = set(os.listdir('scripts'))
packages = set(find_packages())
hildon_scripts = set(('masstransit',))
qt_scripts = set(('qboobmsg', 'qhavesex', 'qvideoob', 'weboob-config-qt', 'qwebcontentedit', 'qflatboob'))
qt_scripts = set(('qboobmsg', 'qhavedate', 'qvideoob', 'weboob-config-qt', 'qwebcontentedit', 'qflatboob'))
if not options.hildon:
scripts = scripts - hildon_scripts
@ -161,8 +161,8 @@ hildon_packages = set((
qt_packages = set((
'weboob.applications.qboobmsg',
'weboob.applications.qboobmsg.ui',
'weboob.applications.qhavesex',
'weboob.applications.qhavesex.ui',
'weboob.applications.qhavedate',
'weboob.applications.qhavedate.ui',
'weboob.applications.qvideoob',
'weboob.applications.qvideoob.ui',
'weboob.applications.qweboobcfg',

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from .havedate import HaveDate
__all__ = ['HaveDate']

View file

@ -0,0 +1,291 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010-2012 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
import sys
import weboob
from weboob.tools.application.repl import ReplApplication
from weboob.tools.application.formatters.iformatter import IFormatter
from weboob.capabilities.dating import ICapDating, OptimizationNotFound
from weboob.capabilities.contact import Contact
__all__ = ['HaveDate']
class ProfileFormatter(IFormatter):
def flush(self):
pass
def print_node(self, node, level=1):
result = u''
if node.flags & node.SECTION:
result += u'\t' * level + node.label + '\n'
for sub in node.value.itervalues():
result += self.print_node(sub, level+1)
else:
if isinstance(node.value, (tuple,list)):
value = ', '.join(unicode(v) for v in node.value)
else:
value = node.value
result += u'\t' * level + u'%-20s %s\n' % (node.label + ':', value)
return result
def format_dict(self, item):
result = u'Nickname: %s\n' % item['name']
if item['status'] & Contact.STATUS_ONLINE:
s = 'online'
elif item['status'] & Contact.STATUS_OFFLINE:
s = 'offline'
elif item['status'] & Contact.STATUS_AWAY:
s = 'away'
else:
s = 'unknown'
result += u'Status: %s (%s)\n' % (s, item['status_msg'])
result += u'Photos:\n'
for name, photo in item['photos'].iteritems():
result += u'\t%s%s\n' % (photo, ' (hidden)' if photo.hidden else '')
result += u'Profile:\n'
for head in item['profile'].itervalues():
result += self.print_node(head)
result += u'Description:\n'
for s in item['summary'].split('\n'):
result += u'\t%s\n' % s
return result
class HaveDate(ReplApplication):
APPNAME = 'havedate'
VERSION = '0.b'
COPYRIGHT = 'Copyright(C) 2010-2012 Romain Bignon'
DESCRIPTION = 'Console application allowing to interact with various dating websites ' \
'and to optimize seduction algorithmically.'
STORAGE_FILENAME = 'dating.storage'
STORAGE = {'optims': {}}
CAPS = ICapDating
EXTRA_FORMATTERS = {'profile': ProfileFormatter}
COMMANDS_FORMATTERS = {'optim': 'table',
'profile': 'profile'}
def load_default_backends(self):
self.load_backends(ICapDating, storage=self.create_storage(self.STORAGE_FILENAME))
def main(self, argv):
self.load_config()
try:
self.do('init_optimizations').wait()
except weboob.core.CallErrors, e:
self.bcall_errors_handler(e)
optimizations = self.storage.get('optims')
for optim, backends in optimizations.iteritems():
self.optims('start', backends, optim, store=False)
return ReplApplication.main(self, argv)
def do_profile(self, id):
"""
profile ID
Display a profile
"""
_id, backend_name = self.parse_id(id, unique_backend=True)
found = 0
for backend, contact in self.do('get_contact', _id, backends=backend_name):
if contact:
self.format(contact)
found = 1
if not found:
self.logger.error(u'Profile not found')
else:
self.flush()
def do_query(self, id):
"""
query ID
Send a query to someone.
"""
_id, backend_name = self.parse_id(id, unique_backend=True)
for backend, query in self.do('send_query', _id, backends=backend_name):
print '%s' % query.message
def edit_optims(self, backend_names, optims_names, stop=False):
if optims_names is None:
print >>sys.stderr, 'Error: missing parameters.'
return 2
for optim_name in optims_names.split():
backends_optims = {}
for backend, optim in self.do('get_optimization', optim_name, backends=backend_names):
if optim:
backends_optims[backend.name] = optim
for backend_name, optim in backends_optims.iteritems():
if len(optim.CONFIG) == 0:
print '%s.%s does not require configuration.' % (backend_name, optim_name)
continue
was_running = optim.is_running()
if stop and was_running:
print 'Stopping %s: %s' % (optim_name, backend_name)
optim.stop()
params = optim.get_config()
if params is None:
params = {}
print 'Configuration of %s.%s' % (backend_name, optim_name)
print '-----------------%s-%s' % ('-' * len(backend_name), '-' * len(optim_name))
for key, value in optim.CONFIG.iteritems():
params[key] = self.ask(value, default=params[key] if (key in params) else value.default)
optim.set_config(params)
if stop and was_running:
print 'Starting %s: %s' % (optim_name, backend_name)
optim.start()
def optims(self, function, backend_names, optims, store=True):
if optims is None:
print >>sys.stderr, 'Error: missing parameters.'
return 2
for optim_name in optims.split():
try:
if store:
storage_optim = set(self.storage.get('optims', optim_name, default=[]))
sys.stdout.write('%sing %s:' % (function.capitalize(), optim_name))
for backend, optim in self.do('get_optimization', optim_name, backends=backend_names):
if optim:
# It's useless to start a started optim, or to stop a stopped one.
if (function == 'start' and optim.is_running()) or \
(function == 'stop' and not optim.is_running()):
continue
# Optim is not configured and would be, ask user to do it.
if function == 'start' and len(optim.CONFIG) > 0 and optim.get_config() is None:
self.edit_optims(backend.name, optim_name)
ret = getattr(optim, function)()
sys.stdout.write(' ' + backend.name)
if not ret:
sys.stdout.write('(failed)')
sys.stdout.flush()
if store:
if function == 'start' and ret:
storage_optim.add(backend.name)
elif function == 'stop':
try:
storage_optim.remove(backend.name)
except KeyError:
pass
sys.stdout.write('.\n')
except weboob.core.CallErrors, errors:
for backend, error, backtrace in errors:
if isinstance(error, OptimizationNotFound):
self.logger.error(u'Error(%s): Optimization "%s" not found' % (backend.name, optim_name))
else:
self.bcall_error_handler(backend, error, backtrace)
if store:
if len(storage_optim) > 0:
self.storage.set('optims', optim_name, list(storage_optim))
else:
self.storage.delete('optims', optim_name)
if store:
self.storage.save()
def complete_optim(self, text, line, *ignored):
args = line.split(' ')
if len(args) == 2:
return ['list', 'start', 'stop', 'edit']
elif len(args) == 3:
return [backend.name for backend in self.enabled_backends]
elif len(args) >= 4:
if args[2] == '*':
backend = None
else:
backend = args[2]
optims = set()
for backend, (name, optim) in self.do('iter_optimizations', backends=backend):
optims.add(name)
return sorted(optims - set(args[3:]))
def do_optim(self, line):
"""
optim [list | start | edit | stop] BACKEND [OPTIM1 [OPTIM2 ...]]
All dating backends offer optimization services. This command can be
manage them.
Use * us BACKEND value to apply command to all backends.
Commands:
* list list all available optimizations of a backend
* start start optimization services on a backend
* edit configure an optimization service for a backend
* stop stop optimization services on a backend
"""
cmd, backend_name, optims_names = self.parse_command_args(line, 3)
if backend_name == '*':
backend_name = None
elif backend_name is not None and not backend_name in [b.name for b in self.enabled_backends]:
print >>sys.stderr, 'Error: No such backend "%s"' % backend_name
return 1
if cmd == 'start':
return self.optims('start', backend_name, optims_names)
if cmd == 'stop':
return self.optims('stop', backend_name, optims_names)
if cmd == 'edit':
self.edit_optims(backend_name, optims_names, stop=True)
return
if cmd == 'list' or cmd is None:
if optims_names is not None:
optims_names = optims_names.split()
optims = {}
backends = set()
for backend, (name, optim) in self.do('iter_optimizations', backends=backend_name):
if optims_names is not None and not name in optims_names:
continue
if optim.is_running():
status = 'RUNNING'
else:
status = '-------'
if not name in optims:
optims[name] = {backend.name: status}
else:
optims[name][backend.name] = status
backends.add(backend.name)
backends = sorted(backends)
for name, backends_status in optims.iteritems():
line = [('name', name)]
for b in backends:
try:
status = backends_status[b]
except KeyError:
status = ''
line.append((b, status))
self.format(tuple(line))
self.flush()
return
print >>sys.stderr, "No such command '%s'" % cmd
return 1

View file

@ -0,0 +1,3 @@
from .qhavedate import QHaveDate
__all__ = ['QHaveDate']

View file

@ -0,0 +1,593 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
import time
import logging
from PyQt4.QtGui import QWidget, QListWidgetItem, QImage, QIcon, QPixmap, \
QFrame, QMessageBox, QTabWidget, QVBoxLayout, \
QFormLayout, QLabel, QPushButton
from PyQt4.QtCore import SIGNAL, Qt
from weboob.tools.application.qt import QtDo, HTMLDelegate
from weboob.tools.misc import to_unicode
from weboob.capabilities.contact import ICapContact, Contact
from weboob.capabilities.chat import ICapChat
from weboob.capabilities.messages import ICapMessages, ICapMessagesPost, Message
from weboob.capabilities.base import NotLoaded
from .ui.contacts_ui import Ui_Contacts
from .ui.contact_thread_ui import Ui_ContactThread
from .ui.thread_message_ui import Ui_ThreadMessage
from .ui.profile_ui import Ui_Profile
from .ui.notes_ui import Ui_Notes
class ThreadMessage(QFrame):
"""
This class represents a message in the thread tab.
"""
def __init__(self, message, parent=None):
QFrame.__init__(self, parent)
self.ui = Ui_ThreadMessage()
self.ui.setupUi(self)
self.set_message(message)
def set_message(self, message):
self.message = message
self.ui.nameLabel.setText(message.sender)
header = time.strftime('%Y-%m-%d %H:%M:%S', message.date.timetuple())
if message.flags & message.IS_NOT_RECEIVED:
header += u' — <font color=#ff0000>Unread</font>'
elif message.flags & message.IS_RECEIVED:
header += u' — <font color=#00ff00>Read</font>'
self.ui.headerLabel.setText(header)
if message.flags & message.IS_HTML:
content = message.content
else:
content = message.content.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('\n', '<br />')
self.ui.contentLabel.setText(content)
def __eq__(self, m):
return self.message == m.message
class ContactThread(QWidget):
"""
The thread of the selected contact.
"""
def __init__(self, weboob, contact, support_reply, parent=None):
QWidget.__init__(self, parent)
self.ui = Ui_ContactThread()
self.ui.setupUi(self)
self.weboob = weboob
self.contact = contact
self.thread = None
self.messages = []
self.process_msg = None
self.connect(self.ui.refreshButton, SIGNAL('clicked()'), self.refreshMessages)
if support_reply:
self.connect(self.ui.sendButton, SIGNAL('clicked()'), self.postReply)
else:
self.ui.frame.hide()
self.refreshMessages()
def refreshMessages(self, fillobj=False):
if self.process_msg:
return
self.ui.refreshButton.setEnabled(False)
self.process_msg = QtDo(self.weboob, self.gotThread, self.gotError)
if fillobj and self.thread:
self.process_msg.do('fillobj', self.thread, ['root'], backends=self.contact.backend)
else:
self.process_msg.do('get_thread', self.contact.id, backends=self.contact.backend)
def gotError(self, backend, error, backtrace):
self.ui.textEdit.setEnabled(False)
self.ui.sendButton.setEnabled(False)
self.ui.refreshButton.setEnabled(True)
def gotThread(self, backend, thread):
if not thread:
#v = self.ui.scrollArea.verticalScrollBar()
#print v.minimum(), v.value(), v.maximum(), v.sliderPosition()
#self.ui.scrollArea.verticalScrollBar().setValue(self.ui.scrollArea.verticalScrollBar().maximum())
self.process_msg = None
return
self.ui.textEdit.setEnabled(True)
self.ui.sendButton.setEnabled(True)
self.ui.refreshButton.setEnabled(True)
self.thread = thread
if thread.root is NotLoaded:
self._insert_load_button(0)
else:
for message in thread.iter_all_messages():
self._insert_message(message)
def _insert_message(self, message):
widget = ThreadMessage(message)
if widget in self.messages:
old_widget = self.messages[self.messages.index(widget)]
if old_widget.message.flags != widget.message.flags:
old_widget.set_message(widget.message)
return
for i, m in enumerate(self.messages):
if widget.message.date > m.message.date:
self.ui.scrollAreaContent.layout().insertWidget(i, widget)
self.messages.insert(i, widget)
if message.parent is NotLoaded:
self._insert_load_button(i)
return
self.ui.scrollAreaContent.layout().addWidget(widget)
self.messages.append(widget)
if message.parent is NotLoaded:
self._insert_load_button(-1)
def _insert_load_button(self, pos):
button = QPushButton(self.tr('More messages...'))
self.connect(button, SIGNAL('clicked()'), lambda: self._load_button_pressed(button))
if pos >= 0:
self.ui.scrollAreaContent.layout().insertWidget(pos, button)
else:
self.ui.scrollAreaContent.layout().addWidget(button)
def _load_button_pressed(self, button):
self.ui.scrollAreaContent.layout().removeWidget(button)
button.hide()
button.deleteLater()
self.refreshMessages(fillobj=True)
def postReply(self):
text = unicode(self.ui.textEdit.toPlainText())
self.ui.textEdit.setEnabled(False)
self.ui.sendButton.setEnabled(False)
m = Message(thread=self.thread,
id=0,
title=u'',
sender=None,
receivers=None,
content=text,
parent=self.messages[0].message if len(self.messages) > 0 else None)
self.process_reply = QtDo(self.weboob, self._postReply_cb, self._postReply_eb)
self.process_reply.do('post_message', m, backends=self.contact.backend)
def _postReply_cb(self, backend, ignored):
if not backend:
return
self.ui.textEdit.clear()
self.ui.textEdit.setEnabled(True)
self.ui.sendButton.setEnabled(True)
self.refreshMessages()
self.process_reply = None
def _postReply_eb(self, backend, error, backtrace):
content = unicode(self.tr('Unable to send message:\n%s\n')) % to_unicode(error)
if logging.root.level == logging.DEBUG:
content += '\n%s\n' % to_unicode(backtrace)
QMessageBox.critical(self, self.tr('Error while posting reply'),
content, QMessageBox.Ok)
self.process_reply = None
class ContactProfile(QWidget):
def __init__(self, weboob, contact, parent=None):
QWidget.__init__(self, parent)
self.ui = Ui_Profile()
self.ui.setupUi(self)
self.connect(self.ui.previousButton, SIGNAL('clicked()'), self.previousClicked)
self.connect(self.ui.nextButton, SIGNAL('clicked()'), self.nextClicked)
self.weboob = weboob
self.contact = contact
self.loaded_profile = False
self.displayed_photo_idx = 0
self.process_photo = {}
missing_fields = self.gotProfile(self.weboob.get_backend(contact.backend), contact)
if len(missing_fields) > 0:
self.process_contact = QtDo(self.weboob, self.gotProfile, self.gotError)
self.process_contact.do('fillobj', self.contact, missing_fields, backends=self.contact.backend)
def gotError(self, backend, error, backtrace):
self.ui.frame_photo.hide()
self.ui.descriptionEdit.setText('<h1>Unable to show profile</h1><p>%s</p>' % to_unicode(error))
def gotProfile(self, backend, contact):
if not backend:
return []
missing_fields = set()
self.display_photo()
self.ui.nicknameLabel.setText('<h1>%s</h1>' % contact.name)
if contact.status == Contact.STATUS_ONLINE:
status_color = 0x00aa00
elif contact.status == Contact.STATUS_OFFLINE:
status_color = 0xff0000
elif contact.status == Contact.STATUS_AWAY:
status_color = 0xffad16
else:
status_color = 0xaaaaaa
self.ui.statusLabel.setText('<font color="#%06X">%s</font>' % (status_color, contact.status_msg))
self.ui.contactUrlLabel.setText('<b>URL:</b> <a href="%s">%s</a>' % (contact.url, contact.url))
if contact.summary is NotLoaded:
self.ui.descriptionEdit.setText('<h1>Description</h1><p><i>Receiving...</i></p>')
missing_fields.add('summary')
else:
self.ui.descriptionEdit.setText('<h1>Description</h1><p>%s</p>' % contact.summary.replace('\n', '<br />'))
if not contact.profile:
missing_fields.add('profile')
elif not self.loaded_profile:
self.loaded_profile = True
for head in contact.profile.itervalues():
if head.flags & head.HEAD:
widget = self.ui.headWidget
else:
widget = self.ui.profileTab
self.process_node(head, widget)
return missing_fields
def process_node(self, node, widget):
# Set the value widget
value = None
if node.flags & node.SECTION:
value = QWidget()
value.setLayout(QFormLayout())
for sub in node.value.itervalues():
self.process_node(sub, value)
elif isinstance(node.value, list):
value = QLabel('<br />'.join(unicode(s) for s in node.value))
value.setWordWrap(True)
elif isinstance(node.value, tuple):
value = QLabel(', '.join(unicode(s) for s in node.value))
value.setWordWrap(True)
elif isinstance(node.value, (basestring,int,long,float)):
value = QLabel(unicode(node.value))
else:
logging.warning('Not supported value: %r' % node.value)
return
if isinstance(value, QLabel):
value.setTextInteractionFlags(Qt.TextSelectableByMouse|Qt.TextSelectableByKeyboard|Qt.LinksAccessibleByMouse)
# Insert the value widget into the parent widget, depending
# of its type.
if isinstance(widget, QTabWidget):
widget.addTab(value, node.label)
elif isinstance(widget.layout(), QFormLayout):
label = QLabel(u'<b>%s:</b> ' % node.label)
widget.layout().addRow(label, value)
elif isinstance(widget.layout(), QVBoxLayout):
widget.layout().addWidget(QLabel(u'<h3>%s</h3>' % node.label))
widget.layout().addWidget(value)
else:
logging.warning('Not supported widget: %r' % widget)
def previousClicked(self):
if len(self.contact.photos) == 0:
return
self.displayed_photo_idx = (self.displayed_photo_idx - 1) % len(self.contact.photos)
self.display_photo()
def nextClicked(self):
if len(self.contact.photos) == 0:
return
self.displayed_photo_idx = (self.displayed_photo_idx + 1) % len(self.contact.photos)
self.display_photo()
def display_photo(self):
if self.displayed_photo_idx >= len(self.contact.photos):
self.displayed_photo_idx = len(self.contact.photos) - 1
if self.displayed_photo_idx < 0:
self.ui.photoUrlLabel.setText('')
return
photo = self.contact.photos.values()[self.displayed_photo_idx]
if photo.data:
data = photo.data
if photo.id in self.process_photo:
self.process_photo.pop(photo.id)
else:
self.process_photo[photo.id] = QtDo(self.weboob, lambda b,p: self.display_photo())
self.process_photo[photo.id].do('fillobj', photo, ['data'], backends=self.contact.backend)
if photo.thumbnail_data:
data = photo.thumbnail_data
else:
return
img = QImage.fromData(data)
img = img.scaledToWidth(self.width()/3)
self.ui.photoLabel.setPixmap(QPixmap.fromImage(img))
if photo.url is not NotLoaded:
text = '<a href="%s">%s</a>' % (photo.url, photo.url)
if photo.hidden:
text += '<br /><font color=#ff0000><i>(Hidden photo)</i></font>'
self.ui.photoUrlLabel.setText(text)
class ContactNotes(QWidget):
""" Widget for storing notes about a contact """
def __init__(self, weboob, contact, parent=None):
QWidget.__init__(self, parent)
self.ui = Ui_Notes()
self.ui.setupUi(self)
self.weboob = weboob
self.contact = contact
self.ui.textEdit.setEnabled(False)
self.ui.saveButton.setEnabled(False)
self.process = QtDo(self.weboob, self._getNotes_cb, self._getNotes_eb)
self.process.do('get_notes', self.contact.id, backends=(self.contact.backend,))
self.connect(self.ui.saveButton, SIGNAL('clicked()'), self.saveNotes)
def _getNotes_cb(self, backend, data):
if not backend or not data:
self.process = None
self.ui.textEdit.setEnabled(True)
self.ui.saveButton.setEnabled(True)
return
self.ui.textEdit.setText(data)
def _getNotes_eb(self, backend, error, backtrace):
self.ui.textEdit.setEnabled(True)
self.ui.saveButton.setEnabled(True)
content = unicode(self.tr('Unable to load notes:\n%s\n')) % to_unicode(error)
if logging.root.level == logging.DEBUG:
content += '\n%s\n' % to_unicode(backtrace)
QMessageBox.critical(self, self.tr('Error while loading notes'),
content, QMessageBox.Ok)
def saveNotes(self):
text = unicode(self.ui.textEdit.toPlainText())
self.ui.saveButton.setEnabled(False)
self.ui.textEdit.setEnabled(False)
self.process = QtDo(self.weboob, self._saveNotes_cb, self._saveNotes_eb)
self.process.do('save_notes', self.contact.id, text, backends=(self.contact.backend,))
def _saveNotes_cb(self, backend, data):
self.ui.saveButton.setEnabled(True)
self.ui.textEdit.setEnabled(True)
pass
def _saveNotes_eb(self, backend, error, backtrace):
self.ui.saveButton.setEnabled(True)
self.ui.textEdit.setEnabled(True)
content = unicode(self.tr('Unable to save notes:\n%s\n')) % to_unicode(error)
if logging.root.level == logging.DEBUG:
content += '\n%s\n' % to_unicode(backtrace)
QMessageBox.critical(self, self.tr('Error while saving notes'),
content, QMessageBox.Ok)
class IGroup(object):
def __init__(self, weboob, id, name):
self.id = id
self.name = name
self.weboob = weboob
def iter_contacts(self, cb):
raise NotImplementedError()
class MetaGroup(IGroup):
def iter_contacts(self, cb):
if self.id == 'online':
status = Contact.STATUS_ONLINE|Contact.STATUS_AWAY
elif self.id == 'offline':
status = Contact.STATUS_OFFLINE
else:
status = Contact.STATUS_ALL
self.process = QtDo(self.weboob, lambda b, d: self.cb(cb, b, d))
self.process.do('iter_contacts', status, caps=ICapContact)
def cb(self, cb, backend, contact):
if contact:
cb(contact)
elif not backend:
self.process = None
cb(None)
class ContactsWidget(QWidget):
def __init__(self, weboob, parent=None):
QWidget.__init__(self, parent)
self.ui = Ui_Contacts()
self.ui.setupUi(self)
self.weboob = weboob
self.contact = None
self.ui.contactList.setItemDelegate(HTMLDelegate())
self.url_process = None
self.photo_processes = {}
self.ui.groupBox.addItem('All', MetaGroup(self.weboob, 'all', self.tr('All')))
self.ui.groupBox.addItem('Online', MetaGroup(self.weboob, 'online', self.tr('Online')))
self.ui.groupBox.addItem('Offline', MetaGroup(self.weboob, 'offline', self.tr('Offline')))
self.ui.groupBox.setCurrentIndex(1)
self.connect(self.ui.groupBox, SIGNAL('currentIndexChanged(int)'), self.groupChanged)
self.connect(self.ui.contactList, SIGNAL('itemClicked(QListWidgetItem*)'), self.contactChanged)
self.connect(self.ui.refreshButton, SIGNAL('clicked()'), self.refreshContactList)
self.connect(self.ui.urlButton, SIGNAL('clicked()'), self.urlClicked)
def load(self):
self.refreshContactList()
self.ui.backendsList.clear()
for backend in self.weboob.iter_backends():
self.ui.backendsList.addItem(backend.name)
def groupChanged(self, i):
self.refreshContactList()
def refreshContactList(self):
self.ui.contactList.clear()
self.ui.refreshButton.setEnabled(False)
i = self.ui.groupBox.currentIndex()
group = self.ui.groupBox.itemData(i).toPyObject()
group.iter_contacts(self.addContact)
def setPhoto(self, contact, item):
if not contact:
return False
try:
self.photo_processes.pop(contact.id, None)
except KeyError:
pass
img = None
for photo in contact.photos.itervalues():
if photo.thumbnail_data:
img = QImage.fromData(photo.thumbnail_data)
break
if img:
item.setIcon(QIcon(QPixmap.fromImage(img)))
return True
return False
def addContact(self, contact):
if not contact:
self.ui.refreshButton.setEnabled(True)
return
status = ''
if contact.status == Contact.STATUS_ONLINE:
status = u'Online'
status_color = 0x00aa00
elif contact.status == Contact.STATUS_OFFLINE:
status = u'Offline'
status_color = 0xff0000
elif contact.status == Contact.STATUS_AWAY:
status = u'Away'
status_color = 0xffad16
else:
status = u'Unknown'
status_color = 0xaaaaaa
if contact.status_msg:
status += u' — %s' % contact.status_msg
item = QListWidgetItem()
item.setText('<h2>%s</h2><font color="#%06X">%s</font><br /><i>%s</i>' % (contact.name, status_color, status, contact.backend))
item.setData(Qt.UserRole, contact)
if contact.photos is NotLoaded:
process = QtDo(self.weboob, lambda b, c: self.setPhoto(c, item))
process.do('fillobj', contact, ['photos'], backends=contact.backend)
self.photo_processes[contact.id] = process
elif len(contact.photos) > 0:
if not self.setPhoto(contact, item):
photo = contact.photos.values()[0]
process = QtDo(self.weboob, lambda b, p: self.setPhoto(contact, item))
process.do('fillobj', photo, ['thumbnail_data'], backends=contact.backend)
self.photo_processes[contact.id] = process
for i in xrange(self.ui.contactList.count()):
if self.ui.contactList.item(i).data(Qt.UserRole).toPyObject().status > contact.status:
self.ui.contactList.insertItem(i, item)
return
self.ui.contactList.addItem(item)
def contactChanged(self, current):
if not current:
return
contact = current.data(Qt.UserRole).toPyObject()
self.setContact(contact)
def setContact(self, contact):
if not contact or contact == self.contact:
return
if not isinstance(contact, Contact):
return self.retrieveContact(contact)
self.ui.tabWidget.clear()
self.contact = contact
backend = self.weboob.get_backend(self.contact.backend)
self.ui.tabWidget.addTab(ContactProfile(self.weboob, self.contact), self.tr('Profile'))
if backend.has_caps(ICapMessages):
self.ui.tabWidget.addTab(ContactThread(self.weboob, self.contact, backend.has_caps(ICapMessagesPost)), self.tr('Messages'))
if backend.has_caps(ICapChat):
self.ui.tabWidget.setTabEnabled(self.ui.tabWidget.addTab(QWidget(), self.tr('Chat')),
False)
self.ui.tabWidget.setTabEnabled(self.ui.tabWidget.addTab(QWidget(), self.tr('Calendar')),
False)
self.ui.tabWidget.addTab(ContactNotes(self.weboob, self.contact), self.tr('Notes'))
def urlClicked(self):
url = unicode(self.ui.urlEdit.text())
if not url:
return
self.retrieveContact(url)
def retrieveContact(self, url):
backend_name = unicode(self.ui.backendsList.currentText())
self.ui.urlButton.setEnabled(False)
self.url_process = QtDo(self.weboob, self.retrieveContact_cb, self.retrieveContact_eb)
self.url_process.do('get_contact', url, backends=backend_name)
def retrieveContact_cb(self, backend, contact):
if not backend:
self.url_process = None
self.ui.urlButton.setEnabled(True)
return
self.ui.urlEdit.clear()
self.setContact(contact)
def retrieveContact_eb(self, backend, error, backtrace):
content = unicode(self.tr('Unable to get contact:\n%s\n')) % to_unicode(error)
if logging.root.level == logging.DEBUG:
content += u'\n%s\n' % to_unicode(backtrace)
QMessageBox.critical(self, self.tr('Error while getting contact'),
content, QMessageBox.Ok)

View file

@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from PyQt4.QtGui import QWidget, QTreeWidgetItem, QImage, QIcon, QPixmap
from PyQt4.QtCore import SIGNAL, Qt
from weboob.capabilities.base import NotLoaded
from weboob.tools.application.qt import QtDo, HTMLDelegate
from .ui.events_ui import Ui_Events
class EventsWidget(QWidget):
def __init__(self, weboob, parent=None):
QWidget.__init__(self, parent)
self.ui = Ui_Events()
self.ui.setupUi(self)
self.weboob = weboob
self.photo_processes = {}
self.event_filter = None
self.connect(self.ui.eventsList, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.eventDoubleClicked)
self.connect(self.ui.typeBox, SIGNAL('currentIndexChanged(int)'), self.typeChanged)
self.connect(self.ui.refreshButton, SIGNAL('clicked()'), self.refreshEventsList)
self.ui.eventsList.setItemDelegate(HTMLDelegate())
self.ui.eventsList.sortByColumn(1, Qt.DescendingOrder)
def load(self):
self.refreshEventsList()
def typeChanged(self, i):
if self.ui.refreshButton.isEnabled():
self.refreshEventsList()
def refreshEventsList(self):
self.ui.eventsList.clear()
self.ui.refreshButton.setEnabled(False)
if self.ui.typeBox.currentIndex() >= 0:
# XXX strangely, in gotEvent() in the loop to check if there is already the
# event type to try to introduce it in list, itemData() returns the right value.
# But, I don't know why, here, it will ALWAYS return None...
# So the filter does not work currently.
self.events_filter = self.ui.typeBox.itemData(self.ui.typeBox.currentIndex())
else:
self.event_filter = None
self.ui.typeBox.setEnabled(False)
self.ui.typeBox.clear()
self.ui.typeBox.addItem('All', None)
self.process = QtDo(self.weboob, self.gotEvent)
self.process.do('iter_events')
def setPhoto(self, contact, item):
if not contact:
return False
try:
self.photo_processes.pop(contact.id, None)
except KeyError:
pass
img = None
for photo in contact.photos.itervalues():
if photo.thumbnail_data:
img = QImage.fromData(photo.thumbnail_data)
break
if img:
item.setIcon(0, QIcon(QPixmap.fromImage(img)))
self.ui.eventsList.resizeColumnToContents(0)
return True
return False
def gotEvent(self, backend, event):
if not backend:
self.ui.refreshButton.setEnabled(True)
self.ui.typeBox.setEnabled(True)
return
found = False
for i in xrange(self.ui.typeBox.count()):
s = self.ui.typeBox.itemData(i)
if s == event.type:
found = True
if not found:
print event.type
self.ui.typeBox.addItem(event.type.capitalize(), event.type)
if event.type == self.event_filter:
self.ui.typeBox.setCurrentIndex(self.ui.typeBox.count()-1)
if self.event_filter and self.event_filter != event.type:
return
contact = event.contact
contact.backend = event.backend
status = ''
if contact.status == contact.STATUS_ONLINE:
status = u'Online'
status_color = 0x00aa00
elif contact.status == contact.STATUS_OFFLINE:
status = u'Offline'
status_color = 0xff0000
elif contact.status == contact.STATUS_AWAY:
status = u'Away'
status_color = 0xffad16
else:
status = u'Unknown'
status_color = 0xaaaaaa
if contact.status_msg:
status += u' — %s' % contact.status_msg
name = '<h2>%s</h2><font color="#%06X">%s</font><br /><i>%s</i>' % (contact.name, status_color, status, event.backend)
date = event.date.strftime('%Y-%m-%d %H:%M')
type = event.type
message = event.message
item = QTreeWidgetItem(None, [name, date, type, message])
item.setData(0, Qt.UserRole, event)
if contact.photos is NotLoaded:
process = QtDo(self.weboob, lambda b, c: self.setPhoto(c, item))
process.do('fillobj', contact, ['photos'], backends=contact.backend)
self.photo_processes[contact.id] = process
elif len(contact.photos) > 0:
if not self.setPhoto(contact, item):
photo = contact.photos.values()[0]
process = QtDo(self.weboob, lambda b, p: self.setPhoto(contact, item))
process.do('fillobj', photo, ['thumbnail_data'], backends=contact.backend)
self.photo_processes[contact.id] = process
self.ui.eventsList.addTopLevelItem(item)
self.ui.eventsList.resizeColumnToContents(0)
self.ui.eventsList.resizeColumnToContents(1)
def eventDoubleClicked(self, item, col):
event = item.data(0, Qt.UserRole).toPyObject()
self.emit(SIGNAL('display_contact'), event.contact)

View file

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from PyQt4.QtGui import QWidget
from PyQt4.QtCore import SIGNAL
from weboob.tools.application.qt import QtMainWindow
from weboob.tools.application.qt.backendcfg import BackendCfg
from weboob.capabilities.dating import ICapDating
try:
from weboob.applications.qboobmsg.messages_manager import MessagesManager
HAVE_BOOBMSG = True
except ImportError:
HAVE_BOOBMSG = False
from .ui.main_window_ui import Ui_MainWindow
from .status import AccountsStatus
from .contacts import ContactsWidget
from .events import EventsWidget
class MainWindow(QtMainWindow):
def __init__(self, config, weboob, parent=None):
QtMainWindow.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.config = config
self.weboob = weboob
self.loaded_tabs = {}
self.connect(self.ui.actionBackends, SIGNAL("triggered()"), self.backendsConfig)
self.connect(self.ui.tabWidget, SIGNAL('currentChanged(int)'), self.tabChanged)
self.addTab(AccountsStatus(self.weboob), self.tr('Status'))
self.addTab(MessagesManager(self.weboob) if HAVE_BOOBMSG else None, self.tr('Messages'))
self.addTab(ContactsWidget(self.weboob), self.tr('Contacts'))
self.addTab(EventsWidget(self.weboob), self.tr('Events'))
self.addTab(None, self.tr('Calendar'))
self.addTab(None, self.tr('Optimizations'))
if self.weboob.count_backends() == 0:
self.backendsConfig()
def backendsConfig(self):
bckndcfg = BackendCfg(self.weboob, (ICapDating,), self)
if bckndcfg.run():
self.loaded_tabs.clear()
widget = self.ui.tabWidget.widget(self.ui.tabWidget.currentIndex())
widget.load()
def addTab(self, widget, title):
if widget:
self.connect(widget, SIGNAL('display_contact'), self.display_contact)
self.ui.tabWidget.addTab(widget, title)
else:
index = self.ui.tabWidget.addTab(QWidget(), title)
self.ui.tabWidget.setTabEnabled(index, False)
def tabChanged(self, i):
widget = self.ui.tabWidget.currentWidget()
if hasattr(widget, 'load') and not i in self.loaded_tabs:
widget.load()
self.loaded_tabs[i] = True
def display_contact(self, contact):
self.ui.tabWidget.setCurrentIndex(2)
widget = self.ui.tabWidget.currentWidget()
widget.setContact(contact)

View file

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010-2012 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from weboob.capabilities.dating import ICapDating
from weboob.tools.application.qt import QtApplication
from .main_window import MainWindow
class QHaveDate(QtApplication):
APPNAME = 'qhavedate'
VERSION = '0.b'
COPYRIGHT = 'Copyright(C) 2010-2012 Romain Bignon'
DESCRIPTION = 'Qt application allowing to interact with various dating websites.'
CAPS = ICapDating
STORAGE_FILENAME = 'dating.storage'
def main(self, argv):
self.create_storage(self.STORAGE_FILENAME)
self.load_backends(ICapDating)
self.main_window = MainWindow(self.config, self.weboob)
self.main_window.show()
return self.weboob.loop()

View file

@ -0,0 +1,133 @@
# -*- coding: utf-8 -*-
# Copyright(C) 2010-2011 Romain Bignon
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# weboob is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from PyQt4.QtGui import QScrollArea, QWidget, QHBoxLayout, QVBoxLayout, QFrame, QLabel, QImage, QPixmap
from weboob.capabilities.account import ICapAccount, StatusField
from weboob.tools.application.qt import QtDo
from weboob.tools.misc import to_unicode
class Account(QFrame):
def __init__(self, weboob, backend, parent=None):
QFrame.__init__(self, parent)
self.setFrameShape(QFrame.StyledPanel)
self.setFrameShadow(QFrame.Raised)
self.weboob = weboob
self.backend = backend
self.setLayout(QVBoxLayout())
self.timer = None
head = QHBoxLayout()
headw = QWidget()
headw.setLayout(head)
self.title = QLabel(u'<h1>%s — %s</h1>' % (backend.name, backend.DESCRIPTION))
self.body = QLabel()
if backend.ICON:
self.icon = QLabel()
img = QImage(backend.ICON)
self.icon.setPixmap(QPixmap.fromImage(img))
head.addWidget(self.icon)
head.addWidget(self.title)
head.addStretch()
self.layout().addWidget(headw)
if backend.has_caps(ICapAccount):
self.body.setText(u'<i>Waiting...</i>')
self.layout().addWidget(self.body)
self.timer = self.weboob.repeat(60, self.updateStats)
def deinit(self):
if self.timer is not None:
self.weboob.stop(self.timer)
def updateStats(self):
self.process = QtDo(self.weboob, self.updateStats_cb, self.updateStats_eb)
self.process.body = u''
self.process.in_p = False
self.process.do('get_account_status', backends=self.backend)
def updateStats_cb(self, backend, field):
if not field:
if self.process.in_p:
self.process.body += u"</p>"
self.body.setText(self.process.body)
self.process = None
return
if field.flags & StatusField.FIELD_HTML:
value = u'%s' % field.value
else:
value = (u'%s' % field.value).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
if field.flags & StatusField.FIELD_TEXT:
if self.process.in_p:
self.process.body += u'</p>'
self.process.body += u'<p>%s</p>' % value
self.process.in_p = False
else:
if not self.process.in_p:
self.process.body += u"<p>"
self.process.in_p = True
else:
self.process.body += u"<br />"
self.process.body += u'<b>%s</b>: %s' % (field.label, field.value)
def updateStats_eb(self, backend, err, backtrace):
self.body.setText(u'<b>Unable to connect:</b> %s' % to_unicode(err))
self.title.setText(u'<font color=#ff0000>%s</font>' % unicode(self.title.text()))
class AccountsStatus(QScrollArea):
def __init__(self, weboob, parent=None):
QScrollArea.__init__(self, parent)
self.weboob = weboob
self.setFrameShadow(self.Plain)
self.setFrameShape(self.NoFrame)
self.setWidgetResizable(True)
widget = QWidget(self)
widget.setLayout(QVBoxLayout())
widget.show()
self.setWidget(widget)
def load(self):
while self.widget().layout().count() > 0:
item = self.widget().layout().takeAt(0)
if item.widget():
item.widget().deinit()
item.widget().hide()
item.widget().deleteLater()
for backend in self.weboob.iter_backends():
account = Account(self.weboob, backend)
self.widget().layout().addWidget(account)
self.widget().layout().addStretch()

View file

@ -0,0 +1,13 @@
UI_FILES = $(wildcard *.ui)
UI_PY_FILES = $(UI_FILES:%.ui=%_ui.py)
PYUIC = pyuic4
all: $(UI_PY_FILES)
%_ui.py: %.ui
$(PYUIC) -o $@ $^
clean:
rm -f *.pyc
rm -f $(UI_PY_FILES)

View file

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ContactThread</class>
<widget class="QWidget" name="ContactThread">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>578</width>
<height>429</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QTextEdit" name="textEdit">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="sendButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>5</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="refreshButton">
<property name="icon">
<iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../usr/share/icons/oxygen/16x16/actions/view-refresh.png</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../usr/share/icons/oxygen/16x16/actions/view-refresh.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
<widget class="QWidget" name="scrollAreaContent">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>556</width>
<height>154</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">QWidget#scrollAreaContent {
background-color: rgb(255, 255, 255);
}</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Contacts</class>
<widget class="QWidget" name="Contacts">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>478</width>
<height>374</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="groupBox"/>
</item>
<item>
<widget class="QToolButton" name="refreshButton">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../usr/share/icons/oxygen/16x16/actions/view-refresh.png</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../usr/share/icons/oxygen/16x16/actions/view-refresh.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="contactList">
<property name="iconSize">
<size>
<width>120</width>
<height>120</height>
</size>
</property>
<property name="spacing">
<number>1</number>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="selectionRectVisible">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>From URL</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLineEdit" name="urlEdit"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="backendsList"/>
</item>
<item>
<widget class="QPushButton" name="urlButton">
<property name="text">
<string>Display</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

Some files were not shown because too many files have changed in this diff Show more