rename havesex and QHaveSex to havedate and QHaveDate
This commit is contained in:
parent
76040343bc
commit
33a9bf71a8
26 changed files with 41 additions and 41 deletions
9
desktop/qhavedate.desktop
Normal file
9
desktop/qhavedate.desktop
Normal 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
BIN
icons/havedate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
BIN
icons/qhavedate.png
Normal file
BIN
icons/qhavedate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.8 KiB |
231
man/havedate.1
Normal file
231
man/havedate.1
Normal 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
56
man/qhavedate.1
Normal 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
27
scripts/havedate
Executable 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
27
scripts/qhavedate
Executable 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()
|
||||||
8
setup.py
8
setup.py
|
|
@ -92,7 +92,7 @@ def build_qt():
|
||||||
extraMakeFlag = []
|
extraMakeFlag = []
|
||||||
|
|
||||||
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qboobmsg/ui'], env=env )
|
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':
|
if sys.platform != 'win32':
|
||||||
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qvideoob/ui'], env=env )
|
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qvideoob/ui'], env=env )
|
||||||
subprocess.check_call(['make']+extraMakeFlag+['-C','weboob/applications/qwebcontentedit/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())
|
packages = set(find_packages())
|
||||||
|
|
||||||
hildon_scripts = set(('masstransit',))
|
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:
|
if not options.hildon:
|
||||||
scripts = scripts - hildon_scripts
|
scripts = scripts - hildon_scripts
|
||||||
|
|
@ -161,8 +161,8 @@ hildon_packages = set((
|
||||||
qt_packages = set((
|
qt_packages = set((
|
||||||
'weboob.applications.qboobmsg',
|
'weboob.applications.qboobmsg',
|
||||||
'weboob.applications.qboobmsg.ui',
|
'weboob.applications.qboobmsg.ui',
|
||||||
'weboob.applications.qhavesex',
|
'weboob.applications.qhavedate',
|
||||||
'weboob.applications.qhavesex.ui',
|
'weboob.applications.qhavedate.ui',
|
||||||
'weboob.applications.qvideoob',
|
'weboob.applications.qvideoob',
|
||||||
'weboob.applications.qvideoob.ui',
|
'weboob.applications.qvideoob.ui',
|
||||||
'weboob.applications.qweboobcfg',
|
'weboob.applications.qweboobcfg',
|
||||||
|
|
|
||||||
23
weboob/applications/havedate/__init__.py
Normal file
23
weboob/applications/havedate/__init__.py
Normal 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']
|
||||||
291
weboob/applications/havedate/havedate.py
Normal file
291
weboob/applications/havedate/havedate.py
Normal 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
|
||||||
3
weboob/applications/qhavedate/__init__.py
Normal file
3
weboob/applications/qhavedate/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .qhavedate import QHaveDate
|
||||||
|
|
||||||
|
__all__ = ['QHaveDate']
|
||||||
593
weboob/applications/qhavedate/contacts.py
Normal file
593
weboob/applications/qhavedate/contacts.py
Normal 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('&', '&').replace('<', '<').replace('>', '>').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)
|
||||||
|
|
||||||
|
|
||||||
155
weboob/applications/qhavedate/events.py
Normal file
155
weboob/applications/qhavedate/events.py
Normal 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)
|
||||||
87
weboob/applications/qhavedate/main_window.py
Normal file
87
weboob/applications/qhavedate/main_window.py
Normal 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)
|
||||||
40
weboob/applications/qhavedate/qhavedate.py
Normal file
40
weboob/applications/qhavedate/qhavedate.py
Normal 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()
|
||||||
133
weboob/applications/qhavedate/status.py
Normal file
133
weboob/applications/qhavedate/status.py
Normal 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('&', '&').replace('<', '<').replace('>', '>')
|
||||||
|
|
||||||
|
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()
|
||||||
13
weboob/applications/qhavedate/ui/Makefile
Normal file
13
weboob/applications/qhavedate/ui/Makefile
Normal 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)
|
||||||
|
|
||||||
0
weboob/applications/qhavedate/ui/__init__.py
Normal file
0
weboob/applications/qhavedate/ui/__init__.py
Normal file
143
weboob/applications/qhavedate/ui/contact_thread.ui
Normal file
143
weboob/applications/qhavedate/ui/contact_thread.ui
Normal 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>
|
||||||
118
weboob/applications/qhavedate/ui/contacts.ui
Normal file
118
weboob/applications/qhavedate/ui/contacts.ui
Normal 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
Loading…
Reference in a new issue