#!/usr/bin/env python3 # -*- coding: utf-8 -*- ''' PyQt4 conversion of Qt Address Book (Tabbed) Example The address book example shows how to use proxy models to display different views onto data from a single model. This example provides an address book that allows contacts to be grouped alphabetically into 9 groups: ABC, DEF, GHI, ... , VW, ..., XYZ. This is achieved by using multiple views on the same model, each of which is filtered using an instance of the QSortFilterProxyModel class. BEHAVIOUR: ========= Address tabs are visible and can be selected. NOTES: ===== Implemented initialisation methods in AddressWidget which required the implementation of the TableModel and NewAddressTab classes. The Qt version of TableModel uses a list of QPair objects; QPair is not implemented in PyQt4; a 'list of lists' in the form: [ [str, str], [str,str], etc.] is used instead. last modified: 2012-01-29 jg ref: http://developer.qt.nokia.com/doc/qt-4.8/itemviews-addressbook.html ''' from PyQt4.QtGui import (QApplication, QMainWindow, QWidget, QAction, QFileDialog, QLabel, QPushButton, QVBoxLayout, QSortFilterProxyModel, QTableView, QAbstractItemView, QTabWidget, QItemSelection) from PyQt4.QtCore import (pyqtSlot, pyqtSignal, Qt, QAbstractTableModel, QModelIndex, QRegExp) class NewAddressTab(QWidget): def __init__(self, parent=None): # initialise base class super(NewAddressTab, self).__init__(parent) descripLabel = QLabel(self.tr( "There are currently no contacts in your Address Book" "\nClick Add to add new contacts.")) addBtn = QPushButton(self.tr("Add")) addBtn.clicked.connect(self._addEntry) layout = QVBoxLayout() layout.addWidget(descripLabel) layout.addWidget(addBtn) self.setLayout(layout) def _addEntry(self): pass class TableModel(QAbstractTableModel): def __init__(self, parent=None, pairs=[['', '']]): # initialise base class super(TableModel, self).__init__(parent) self._listOfPairs = pairs # overridden methods ------------------------------------------------------ def rowCount(self, parent=QModelIndex()): return len(self._listOfPairs) def columnCount(self, parent=QModelIndex()): return 2 def data(self, index, role): if not index.isValid(): return None if (index.row() >= len(self._listOfPairs) or index.row() < 0): return None if role == Qt.DisplayRole: row = index.row() pair = self._listOfPairs[row] if index.column() == 0: return pair[0] elif index.column == 1: return pair[1] return None def headerData(self, section, orientation, role): if role != Qt.DisplayRole: return None if orientation == Qt.Horizontal: if section == 0: return self.tr("Name") elif section == 1: return self.tr("Address") else: return None return None def insertRows(self, position, rows, index=QModelIndex()): self.beginInsertRows(QModelIndex(), position, position + rows - 1) for r in range(rows): pair = [" ", " "] self._listOfPairs.insert(position, pair) self.endInsertRows() return True def removeRows(self, position, rows, index=QModelIndex()): self.beginRemoveRows(QModelIndex(), position, position + rows - 1) for r in range(rows): # use list slicing to remove rows self._listOfPairs = (self._listOfPairs[:position] + self._listOfPairs[position + rows:]) self.endRemoveRows() return True def setData(self, index, value, role=Qt.EditRole): if index.isValid() and role == Qt.EditRole: row = index.row() pair = self._listOfPairs(row) if index.column() == 0: pair[0] = value elif index.column() == 1: pair[1] = value else: return False self._listOfPairs[row] = pair return True return False def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) # public methods ---------------------------------------------------------- def getList(self): return self._listOfPairs class AddressWidget(QTabWidget): def __init__(self, parent=None): # initialise base class super(AddressWidget, self).__init__(parent) self._table = TableModel(self) newAddressTab = NewAddressTab(self) self.addTab(newAddressTab, "Address Book") self._setUpTabs() # signals -------------------------------------------------------------- selectionChanged = pyqtSignal(QItemSelection, name="selectionChanged") # public slots --------------------------------------------------------- @pyqtSlot(str, str) def addEntry(self, name='', address=''): pass @pyqtSlot() def editEntry(self): pass @pyqtSlot() def removeEntry(self): pass # public methods --------------------------------------------------------- def readFromFile(self, fname): pass def writeToFile(self, fname): pass # private methods --------------------------------------------------------- def _setUpTabs(self): groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"] for i in range(len(groups)): tabName = groups[i] # 'str' is a reserved word in Python proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self._table) proxyModel.setDynamicSortFilter(True) tableView = QTableView() tableView.setModel(proxyModel) tableView.setSortingEnabled(True) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) tableView.horizontalHeader().setStretchLastSection(True) tableView.verticalHeader().hide() tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) tableView.setSelectionMode(QAbstractItemView.SingleSelection) newStr = "^{0}.*".format(tabName) proxyModel.setFilterRegExp(QRegExp(newStr, Qt.CaseInsensitive)) proxyModel.setFilterKeyColumn(0) proxyModel.sort(0, Qt.AscendingOrder) tableView.selectionModel().selectionChanged.connect(self.selectionChanged) self.addTab(tableView, tabName) class MainWindow(QMainWindow): def __init__(self, parent=None): # initialise base class super(MainWindow, self).__init__(parent) self._addrWidget = AddressWidget() self.setCentralWidget(self._addrWidget) self._createFileMenu() # extracted from createMenus() self._createToolsMenu() # extracted from createMenus() self._addrWidget.selectionChanged.connect(self._updateActions) self.setWindowTitle(self.tr("Address Book - Part 2")) # private methods --------------------------------------------------------- def _createFileMenu(self): # create actions openAct = QAction(self.tr("&Open..."), self) saveAct = QAction(self.tr("&Save..."), self) exitAct = QAction(self.tr("E&xit"), self) # connect signals/slots for event handling openAct.triggered.connect(self._openFile) saveAct.triggered.connect(self._saveFile) exitAct.triggered.connect(QApplication.instance().exit) # create menu fileMenu = self.menuBar().addMenu(self.tr("&File")) fileMenu.addAction(openAct) fileMenu.addAction(saveAct) fileMenu.addSeparator() fileMenu.addAction(exitAct) def _createToolsMenu(self): # create actions addAct = QAction(self.tr("&Add Entry..."), self) self._editAct = QAction(self.tr("&EditEntry..."), self) self._removeAct = QAction(self.tr("&Remove Entry"), self) # connect signals/slots for event handling addAct.triggered.connect(self._addrWidget.addEntry) self._editAct.triggered.connect(self._addrWidget.editEntry) self._removeAct.triggered.connect(self._addrWidget.removeEntry) # create menu toolMenu = self.menuBar().addMenu(self.tr("&Tools")) toolMenu.addAction(addAct) toolMenu.addAction(self._editAct) toolMenu.addSeparator() toolMenu.addAction(self._removeAct) # set menu item visibility self._editAct.setEnabled(False) self._removeAct.setEnabled(False) # private slots --------------------------------------------------------- @pyqtSlot() def _openFile(self): fname = QFileDialog.getOpenFileName(self) if fname: self._addrWidget.readFromFile(fname) @pyqtSlot() def _saveFile(self): fname = QFileDialog.getSaveFileName(self) if fname: self._addrWidget.writeToFile(fname) @pyqtSlot(QItemSelection) def _updateActions(self, sel): indexes = sel.indexes() if indexes: self._removeAct.setEnabled(True) self._editAct.setEnabled(True) else: self._removeAct.setEnabled(False) self._editAct.setEnabled(False) # main ======================================================================== def main(): import sys app = QApplication(sys.argv) mw = MainWindow() mw.show() sys.exit(app.exec_()) if __name__ == '__main__': main()
Page List
▼
Monday, January 30, 2012
Qt 4.8 Address Book Example - Part 2
Part 2 implements the methods and classes needed to initialize the AddressWidget; this includes the full implementation of the TableModel and NewAddressTab classes.