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,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>

View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Events</class>
<widget class="QWidget" name="Events">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>497</width>
<height>318</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="typeBox"/>
</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="QTreeWidget" name="eventsList">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="animated">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Contact</string>
</property>
</column>
<column>
<property name="text">
<string>Date</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Message</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>763</width>
<height>580</height>
</rect>
</property>
<property name="windowTitle">
<string>QHaveDate</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>763</width>
<height>24</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionBackends"/>
<addaction name="separator"/>
<addaction name="actionQuit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionBackends"/>
</widget>
<action name="actionBackends">
<property name="text">
<string>Backends</string>
</property>
</action>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
<property name="toolTip">
<string>Quit</string>
</property>
</action>
</widget>
<resources/>
<connections>
<connection>
<sender>actionQuit</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>381</x>
<y>289</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Notes</class>
<widget class="QWidget" name="Notes">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>430</width>
<height>323</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextEdit" name="textEdit"/>
</item>
<item>
<widget class="QPushButton" name="saveButton">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Profile</class>
<widget class="QWidget" name="Profile">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>755</width>
<height>647</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>755</width>
<height>647</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="headFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<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>
<widget class="QLabel" name="nicknameLabel">
<property name="text">
<string>&lt;h1&gt;Loading...&lt;/h1&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="contactUrlLabel">
<property name="text">
<string>&lt;b&gt;URL:&lt;/b&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="headWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_photo">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="previousButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>&lt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="photoLabel">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="nextButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="photoUrlLabel">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>13</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QTextEdit" name="descriptionEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<widget class="QTabWidget" name="profileTab">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ThreadMessage</class>
<widget class="QFrame" name="ThreadMessage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>552</width>
<height>76</height>
</rect>
</property>
<property name="windowTitle">
<string>Frame</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="imageLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="nameLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="headerLabel">
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="contentLabel">
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>