From 70e2aebf95b14bb763d68af56c101ac45318a4be Mon Sep 17 00:00:00 2001 From: Luci Stanescu <luci@ag-projects.com> Date: Tue, 18 May 2010 12:41:14 +0000 Subject: [PATCH] Added a model for accounts and improved the identity combobox --- blink/accounts.py | 210 ++++++++++++++++++++++++++++++++++++++++++++ blink/mainwindow.py | 60 ++----------- resources/blink.ui | 7 +- 3 files changed, 225 insertions(+), 52 deletions(-) create mode 100644 blink/accounts.py diff --git a/blink/accounts.py b/blink/accounts.py new file mode 100644 index 0000000..36fd6b2 --- /dev/null +++ b/blink/accounts.py @@ -0,0 +1,210 @@ +# Copyright (C) 2010 AG Projects. See LICENSE for details. +# + +__all__ = ['AccountModel', 'ActiveAccountModel', 'AccountSelector'] + +from PyQt4.QtCore import Qt, QAbstractListModel, QModelIndex +from PyQt4.QtGui import QComboBox, QIcon, QPalette, QPixmap, QSortFilterProxyModel, QStyledItemDelegate + +from application.notification import IObserver, NotificationCenter +from application.python.util import Null +from zope.interface import implements + +from sipsimple.account import Account, AccountManager, BonjourAccount + +from blink.resources import Resources +from blink.util import run_in_gui_thread + + +class AccountInfo(object): + def __init__(self, name, account, icon=None): + self.name = name + self.account = account + self.icon = icon + self.registration_state = None + + def __eq__(self, other): + if isinstance(other, basestring): + return self.name == other + elif isinstance(other, (Account, BonjourAccount)): + return self.account == other + return False + + def __ne__(self, other): + return not self.__eq__(other) + + +class AccountModel(QAbstractListModel): + implements(IObserver) + + def __init__(self, parent=None): + super(AccountModel, self).__init__(parent) + self.accounts = [] + + notification_center = NotificationCenter() + notification_center.add_observer(self, name='SIPAccountDidActivate') + notification_center.add_observer(self, name='SIPAccountDidDeactivate') + notification_center.add_observer(self, name='SIPAccountWillRegister') + notification_center.add_observer(self, name='SIPAccountRegistrationDidSucceed') + notification_center.add_observer(self, name='SIPAccountRegistrationDidFail') + notification_center.add_observer(self, name='SIPAccountRegistrationDidEnd') + notification_center.add_observer(self, name='BonjourAccountWillRegister') + notification_center.add_observer(self, name='BonjourAccountRegistrationDidSucceed') + notification_center.add_observer(self, name='BonjourAccountRegistrationDidFail') + notification_center.add_observer(self, name='BonjourAccountRegistrationDidEnd') + notification_center.add_observer(self, sender=AccountManager()) + + def rowCount(self, parent=QModelIndex()): + return len(self.accounts) + + def data(self, index, role=Qt.DisplayRole): + if not index.isValid(): + return None + account_info = self.accounts[index.row()] + if role == Qt.DisplayRole: + return account_info.name + elif role == Qt.DecorationRole: + return account_info.icon + elif role == Qt.UserRole: + return account_info + return None + + @run_in_gui_thread + def handle_notification(self, notification): + handler = getattr(self, '_NH_%s' % notification.name, Null) + handler(notification) + + def _NH_SIPAccountManagerDidAddAccount(self, notification): + account = notification.data.account + name = u'Bonjour' if account is BonjourAccount() else unicode(account.id) + icon = None + if account is BonjourAccount(): + pixmap = QPixmap() + if pixmap.load(Resources.get('icons/bonjour.png')): + pixmap = pixmap.scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation) + icon = QIcon(pixmap) + self.beginInsertRows(QModelIndex(), len(self.accounts), len(self.accounts)) + self.accounts.append(AccountInfo(name, account, icon)) + self.endInsertRows() + + def _NH_SIPAccountManagerDidRemoveAccount(self, notification): + position = self.accounts.index(notification.data.account) + self.beginRemoveRows(QModelIndex(), position, position) + del self.accounts[position] + self.endRemoveRows() + + def _NH_SIPAccountDidActivate(self, notification): + position = self.accounts.index(notification.sender) + self.dataChanged.emit(self.index(position), self.index(position)) + + def _NH_SIPAccountDidDeactivate(self, notification): + position = self.accounts.index(notification.sender) + self.dataChanged.emit(self.index(position), self.index(position)) + + def _NH_SIPAccountWillRegister(self, notification): + position = self.accounts.index(notification.sender) + self.accounts[position].registration_state = 'started' + self.dataChanged.emit(self.index(position), self.index(position)) + + def _NH_SIPAccountRegistrationDidSucceed(self, notification): + position = self.accounts.index(notification.sender) + self.accounts[position].registration_state = 'succeeded' + self.dataChanged.emit(self.index(position), self.index(position)) + + def _NH_SIPAccountRegistrationDidFail(self, notification): + position = self.accounts.index(notification.sender) + self.accounts[position].registration_state = 'failed' + self.dataChanged.emit(self.index(position), self.index(position)) + + def _NH_SIPAccountRegistrationDidEnd(self, notification): + position = self.accounts.index(notification.sender) + self.accounts[position].registration_state = 'ended' + self.dataChanged.emit(self.index(position), self.index(position)) + + _NH_BonjourAccountWillRegister = _NH_SIPAccountWillRegister + _NH_BonjourAccountRegistrationDidSucceed = _NH_SIPAccountRegistrationDidSucceed + _NH_BonjourAccountRegistrationDidFail = _NH_SIPAccountRegistrationDidFail + _NH_BonjourAccountRegistrationDidEnd = _NH_SIPAccountRegistrationDidEnd + + +class ActiveAccountModel(QSortFilterProxyModel): + def __init__(self, model, parent=None): + super(ActiveAccountModel, self).__init__(parent) + self.setSourceModel(model) + self.setDynamicSortFilter(True) + + def filterAcceptsRow(self, source_row, source_parent): + source_model = self.sourceModel() + source_index = source_model.index(source_row, 0, source_parent) + account_info = source_model.data(source_index, Qt.UserRole) + return account_info.account.enabled + + +class AccountDelegate(QStyledItemDelegate): + def paint(self, painter, option, index): + account_info = index.data(Qt.UserRole).toPyObject() + if account_info.registration_state == 'succeeded': + option.palette.setColor(QPalette.Text, Qt.black) + else: + option.palette.setColor(QPalette.Text, Qt.gray) + super(AccountDelegate, self).paint(painter, option, index) + + +class AccountSelector(QComboBox): + implements(IObserver) + + def __init__(self, parent=None): + super(AccountSelector, self).__init__(parent) + self.currentIndexChanged[int].connect(self.selection_changed) + self.model().dataChanged.connect(self.data_changed) + self.view().setItemDelegate(AccountDelegate(self.view())) + + notification_center = NotificationCenter() + notification_center.add_observer(self, name="SIPAccountManagerDidChangeDefaultAccount") + notification_center.add_observer(self, name="SIPAccountManagerDidStart") + + def setModel(self, model): + self.model().dataChanged.disconnect(self.data_changed) + model.dataChanged.connect(self.data_changed) + super(AccountSelector, self).setModel(model) + + def data_changed(self, topLeft, bottomRight): + index = self.currentIndex() + if topLeft.row() <= index <= bottomRight.row(): + account_info = self.itemData(index).toPyObject() + palette = self.palette() + if account_info.registration_state == 'succeeded': + palette.setColor(QPalette.Text, Qt.black) + else: + palette.setColor(QPalette.Text, Qt.gray) + self.setPalette(palette) + + def selection_changed(self, index): + account_info = self.itemData(index).toPyObject() + palette = self.palette() + if account_info.registration_state == 'succeeded': + palette.setColor(QPalette.Text, Qt.black) + else: + palette.setColor(QPalette.Text, Qt.gray) + self.setPalette(palette) + + @run_in_gui_thread + def handle_notification(self, notification): + handler = getattr(self, '_NH_%s' % notification.name, Null) + handler(notification) + + def _NH_SIPAccountManagerDidStart(self, notification): + account = AccountManager().default_account + if account is not None: + model = self.model() + source_model = model.sourceModel() + account_index = source_model.accounts.index(account) + self.setCurrentIndex(model.mapFromSource(source_model.index(account_index)).row()) + + def _NH_SIPAccountManagerDidChangeDefaultAccount(self, notification): + account = notification.data.account + if account is not None: + model = self.model() + source_model = model.sourceModel() + account_index = source_model.accounts.index(account) + self.setCurrentIndex(model.mapFromSource(source_model.index(account_index)).row()) diff --git a/blink/mainwindow.py b/blink/mainwindow.py index 00b37d2..cf65ab0 100644 --- a/blink/mainwindow.py +++ b/blink/mainwindow.py @@ -6,25 +6,19 @@ from __future__ import with_statement __all__ = ['MainWindow'] from PyQt4 import uic -from PyQt4.QtCore import Qt, QVariant -from PyQt4.QtGui import QBrush, QColor, QIcon, QPainter, QPen, QPixmap +from PyQt4.QtCore import Qt +from PyQt4.QtGui import QBrush, QColor, QPainter, QPen, QPixmap -from application.notification import IObserver, NotificationCenter -from application.python.util import Null -from zope.interface import implements - -from sipsimple.account import AccountManager, BonjourAccount +from sipsimple.account import AccountManager +from blink.accounts import AccountModel, ActiveAccountModel from blink.contacts import Contact, ContactGroup, ContactEditorDialog, ContactModel, ContactSearchModel from blink.resources import Resources -from blink.util import run_in_gui_thread ui_class, base_class = uic.loadUiType(Resources.get('blink.ui')) class MainWindow(base_class, ui_class): - implements(IObserver) - def __init__(self, parent=None): super(MainWindow, self).__init__(parent) @@ -37,6 +31,10 @@ class MainWindow(base_class, ui_class): self.set_user_icon(Resources.get("icons/default-avatar.png")) # ":/resources/icons/default-avatar.png" self.enable_call_buttons(False) + self.account_model = AccountModel(self) + self.enabled_account_model = ActiveAccountModel(self.account_model, self) + self.identity.setModel(self.enabled_account_model) + self.contact_model = ContactModel(self) self.contact_search_model = ContactSearchModel(self.contact_model, self) self.contact_list.setModel(self.contact_model) @@ -73,12 +71,6 @@ class MainWindow(base_class, ui_class): #self.connect(self.contact_list, QtCore.SIGNAL("doubleClicked(const QModelIndex &)"), self.double_click_action) - notification_center = NotificationCenter() - notification_center.add_observer(self, name="SIPAccountManagerDidChangeDefaultAccount") - notification_center.add_observer(self, name="SIPAccountManagerDidStart") - notification_center.add_observer(self, name="SIPAccountDidActivate") - notification_center.add_observer(self, name="SIPAccountDidDeactivate") - def add_contact(self, clicked): model = self.contact_model selected_items = ((index.row(), model.data(index)) for index in self.contact_list.selectionModel().selectedIndexes()) @@ -116,7 +108,7 @@ class MainWindow(base_class, ui_class): def set_identity(self, index): account_manager = AccountManager() - account_manager.default_account = self.identity.itemData(index).toPyObject() + account_manager.default_account = self.identity.itemData(index).toPyObject().account def search_box_text_changed(self, text): if text: @@ -156,40 +148,6 @@ class MainWindow(base_class, ui_class): self.main_view.setCurrentWidget(widget) self.switch_view_button.setText(widget.sibling_name) - @run_in_gui_thread - def handle_notification(self, notification): - handler = getattr(self, '_NH_%s' % notification.name, Null) - handler(notification) - - def _NH_SIPAccountDidActivate(self, notification): - account = notification.sender - name = u'Bonjour' if account is BonjourAccount() else account.id - icon = None - if account is BonjourAccount(): - pixmap = QPixmap() - if pixmap.load(Resources.get('icons/bonjour.png')): - pixmap = pixmap.scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation) - icon = QIcon(pixmap) - if icon is not None: - self.identity.addItem(icon, name, QVariant(account)) - else: - self.identity.addItem(name, QVariant(account)) - - def _NH_SIPAccountDidDeactivate(self, notification): - account = notification.sender - name = u'Bonjour' if account is BonjourAccount() else account.id - self.identity.removeItem(self.identity.findText(name)) - - def _NH_SIPAccountManagerDidStart(self, notification): - account = AccountManager().default_account - name = u'Bonjour' if account is BonjourAccount() else account.id - self.identity.setCurrentIndex(self.identity.findText(name)) - - def _NH_SIPAccountManagerDidChangeDefaultAccount(self, notification): - account = notification.data.account - name = u'Bonjour' if account is BonjourAccount() else account.id - self.identity.setCurrentIndex(self.identity.findText(name)) - del ui_class, base_class diff --git a/resources/blink.ui b/resources/blink.ui index eaf3a6b..5c4ebe1 100644 --- a/resources/blink.ui +++ b/resources/blink.ui @@ -41,7 +41,7 @@ <number>1</number> </property> <item> - <widget class="QComboBox" name="identity"> + <widget class="AccountSelector" name="identity"> <property name="maximumSize"> <size> <width>16777215</width> @@ -825,6 +825,11 @@ buttons below.</string> <extends>QListView</extends> <header>blink.contacts</header> </customwidget> + <customwidget> + <class>AccountSelector</class> + <extends>QComboBox</extends> + <header>blink.accounts</header> + </customwidget> </customwidgets> <tabstops> <tabstop>search_box</tabstop> -- 2.21.0