Wednesday, January 25, 2012

Qt 4.8 Address Book Tutorial - Part 2

This code is based on the Qt 4.8 Address Book Tutorial  Part 2, Adding Addresses.


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'''
    PyQt4 conversion of Qt Tutorial 'Address Book' - Part 2
    
    The next step in creating the address book is to implement some user 
    interactions. We will provide a push button that the user can click to 
    add a new contact. Also, some form of data structure is needed to store 
    these contacts in an organised way.

    NOTE:
    ====
    The original C++ code uses a QMap to hold the contact names and addresses.
    PyQt4 does not include QMap, a Python 'dictionary' which holds 'keys'
    and 'values' was used instead.
    
    Extracted button panel creation to it's own 'init' method to make
    the code easier to read
        
last modified: 2012-01-23 jg
ref: 
    http://developer.qt.nokia.com/doc/qt-4.8/tutorials-addressbook.html
    http://developer.qt.nokia.com/doc/qt-4.8/tutorials-addressbook-part2.html
'''
import sys
from PyQt4.QtGui import (QApplication, QWidget, QLabel, QTextEdit, QLineEdit,
                         QGridLayout, QPushButton, QVBoxLayout, QMessageBox)
from PyQt4.QtCore import (Qt, pyqtSlot)

class AddressBook(QWidget):

    def __init__(self, parent=None):
        super(AddressBook, self).__init__(parent)

        # create a dictonary to hold contacts
        self._contacts = {}

        # create input labels and fields
        nameLabel = QLabel(self.tr("Name:"))
        self._nameLine = QLineEdit(self)
        self._nameLine.setReadOnly(True)

        addrLabel = QLabel(self.tr("Address:"))
        self._addrText = QTextEdit(self)
        self._addrText.setReadOnly(True)

        # create and populate layout
        mainLayout = QGridLayout()
        mainLayout.addWidget(nameLabel, 0, 0)
        mainLayout.addWidget(self._nameLine, 0, 1)
        mainLayout.addWidget(addrLabel, 1, 0, Qt.AlignTop)
        mainLayout.addWidget(self._addrText, 1, 1)
        mainLayout.addLayout(self._initButtonPanel(), 1, 2)

        # set this objects layout and window title
        self.setLayout(mainLayout)
        self.setWindowTitle(self.tr("Simple Address Book"))

    def _initButtonPanel(self):
        # create buttons
        self._addBtn = QPushButton(self.tr("&Add"))
        self._submitBtn = QPushButton(self.tr("&Submit"))
        self._cancelBtn = QPushButton(self.tr("&Cancel"))

        # set button visibility
        self._addBtn.show()
        self._submitBtn.hide()
        self._cancelBtn.hide()

        # define signal/slot connections for event handling
        self._addBtn.clicked.connect(self._addContact)
        self._submitBtn.clicked.connect(self._submitContact)
        self._cancelBtn.clicked.connect(self._cancel)

        # layout buttons
        layout = QVBoxLayout()
        layout.addWidget(self._addBtn, Qt.AlignTop)
        layout.addWidget(self._submitBtn)
        layout.addWidget(self._cancelBtn)
        layout.addStretch()             # force additional space to bottom

        return layout

    @pyqtSlot()
    def _addContact(self):
        self._oldName = self._nameLine.text()
        self._oldAddr = self._addrText.toPlainText()

        self._nameLine.clear()
        self._addrText.clear()

        self._nameLine.setReadOnly(False)
        self._nameLine.setFocus(Qt.OtherFocusReason)
        self._addrText.setReadOnly(False)

        self._addBtn.setEnabled(False)
        self._submitBtn.show()
        self._cancelBtn.show()

    @pyqtSlot()
    def _submitContact(self):
        name = self._nameLine.text()
        addr = self._addrText.toPlainText()

        if not (name or addr):
            QMessageBox.information(self,
                                    self.tr("Empty Field"),
                                    self.tr("Please enter a name and address."))
            return

        if name not in self._contacts:
            self._contacts[name] = addr
            QMessageBox.information(self,
                                    self.tr("Add Successful"),
             self.tr("{0} has been added to your address book.").format(name))
        else:
            QMessageBox.information(self,
                                     self.tr("Add Unsuccessful"),
             self.tr("Sorry, {0} is already in your address book.").format(name))

        # restore buttons
        if len(self._contacts) == 0:
            self._nameLine.clear()
            self._addrText.clear()

        self._nameLine.setReadOnly(True)
        self._addrText.setReadOnly(True)
        self._addBtn.setEnabled(True)
        self._submitBtn.hide()
        self._cancelBtn.hide()

    @pyqtSlot()
    def _cancel(self):
        self._nameLine.setText(self._oldName)
        self._nameLine.setReadOnly(True)

        self._addrText.setText(self._oldAddr)
        self._addrText.setReadOnly(True)
        self._addBtn.setEnabled(True)
        self._submitBtn.hide()
        self._cancelBtn.hide()


def main():
    app = QApplication(sys.argv)    # required for all GUI applications

    ab = AddressBook()
    ab.show()                       # make me visible

    sys.exit(app.exec_())           # start main event thread

if __name__ == '__main__':
    main()