Monday, January 30, 2012

Qt 4.8 Address Book Example - Part 1

The following code is based on the Qt Address Book Example, one of their ItemViews Examples.  The example uses  QTableView, QTabWidget and QSortFilterProxyModel to display entries grouped alphabetically on tabbed pages.



The conversion from C++ to PyQt4 is given here in 5 parts; the last includes two modifications to the original code.

The example consists of six C++ classes: Main, MainWindow, AddressWidget, TableModel, NewAddressTab, AddDialog and their corresponding header files.

Part 1 implements the Main and MainWindow classes with stubs for the AddressWidget class.  Note that Main is not implemented as a separate class in Python and that all classes are defined in one module file.




#!/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:
    =========
        Basic application shell and menus created. Menu items, other than
        Exit, are not fully functional.
    
    NOTES:
    =====
    The application is based on five classes:
        Mainwindow
        AddressWidget
        TabelModel
        NewAddressTab
        AddDialog
        
    The original C++ is an example rather than a tutorial; each class
    occupies its own file with associated include files.
    
    Here the application is built up, starting with MainWindow
    stubs for the other classes are defined as encountered, to be 
    implemented in later steps.
    
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, QTabWidget, QAction,
                         QFileDialog, QItemSelection)
from PyQt4.QtCore import (pyqtSlot, pyqtSignal)

class AddressWidget(QWidget):
    def __init__(self, parent=None):    # initialise base class
        super(AddressWidget, self).__init__(parent)

    # 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

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 1"))

    # 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()