#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
    PyQt4 conversion of Qt Tutorial 'Address Book' - Part 3
    
    Add the ability to navigate backwards and forwards through
    the  contacts.
    NOTES:
    =====
    The method name for 'next()' has been changed to 'next_()' as
    'next' is a reserved word in Python.
    
    The 'next_()' and 'prev()' methods are implemented using sorted
    lists of the contacts names (keys). As lists in Python
    use 'pointers' (memory addresses) to objects and not the actual objects
    themselves the methods should scale reasonably well.
        
last modified: 2012-01-24 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-part3.html
'''
import sys
from PyQt4.QtGui import (QApplication, QWidget, QLabel, QTextEdit, QLineEdit,
                         QGridLayout, QPushButton, QVBoxLayout, QMessageBox,
                         QHBoxLayout)
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)
        mainLayout.addLayout(self._initNavPanel(), 2, 1)
        # set this objects layout and window title
        self.setLayout(mainLayout)
        self.setWindowTitle(self.tr("Simple Address Book"))
    def _initNavPanel(self):
        self._nextBtn = QPushButton(self.tr("&Next"))
        self._prevBtn = QPushButton(self.tr("&Previous"))
        self._nextBtn.setEnabled(False)
        self._prevBtn.setEnabled(False)
        self._nextBtn.clicked.connect(self.next_)
        self._prevBtn.clicked.connect(self.previous)
        layout = QHBoxLayout()
        layout.addWidget(self._prevBtn)
        layout.addWidget(self._nextBtn)
        return layout
    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._oldAddress = 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()
        self._nextBtn.setEnabled(False)   # disable navigationd during 'Add'
        self._prevBtn.setEnabled(False)
    @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
        num = len(self._contacts)
        if num == 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()
        # enable navigation if more than one contact
        self._nextBtn.setEnabled(num > 1)
        self._prevBtn.setEnabled(num > 1)
    @pyqtSlot()
    def _cancel(self):
        self._nameLine.setText(self._oldName)
        self._nameLine.setReadOnly(True)
        self._addrText.setText(self._oldAddress)
        self._addrText.setReadOnly(True)
        self._addBtn.setEnabled(True)
        self._submitBtn.hide()
        self._cancelBtn.hide()
        # enable navigation if more than one contact
        num = len(self._contacts)
        self._nextBtn.setEnabled(num > 1)
        self._prevBtn.setEnabled(num > 1)
    @pyqtSlot()
    def next_(self):
        name = self._nameLine.text()            # current contact's name
        sKeys = sorted(self._contacts.keys())   # sort names
        i = sKeys.index(name) + 1               # get index for next name in sorted list
        if i >= len(sKeys): i = 0               # if we're past the end, go to the beginning
        nextKey = sKeys[i]                      # name at new index is key we want
        self._nameLine.setText(nextKey)
        self._addrText.setText(self._contacts[nextKey])
    @pyqtSlot()
    def previous(self):
        name = self._nameLine.text()            # current contact's name
        sKeys = sorted(self._contacts.keys())   # sort names
        i = sKeys.index(name) - 1               # index for previous name in sorted list
        if i < 0: i = len(sKeys) - 1            # if below the beginning, go to the end
        prevKey = sKeys[i]                      # name at the new index is the key we want
        self._nameLine.setText(prevKey)
        self._addrText.setText(self._contacts[prevKey])
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()
Wednesday, January 25, 2012
Qt 4.8 Address Book Tutorial - Part 3
This code is based on the Qt 4.8 Address Book Tutorial  Part 3, Navigating Between Entries.
Labels:
Qt Address Book Tutorial
