#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
PyQt4 conversion of Qt Tutorial 'Address Book' - Part 4
We now have an address book that not only holds contacts in an organized manner,
but also allows navigation. It would be convenient to include edit and remove
functions so that a contact's details can be changed when needed.
However, this requires a little improvement, added through implementing:
enum: AddMode, EditMode, NavMode
updateInteface(mode)
editBtn, removeBtn
editContact()
removeContact()
NOTE:
====
The code to handle the enabling/disabling, hiding/showing of the
various buttons has been extracted to the single method, 'updateInterface()'
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-part4.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):
# application operation 'modes'
# assigns values such that NavMode = 0, AddMode = 1 and EditMode = 2
NavMode, AddMode, EditMode = range(3)
def __init__(self, parent=None):
super(AddressBook, self).__init__(parent)
# create a dictionary to hold contacts
self._contacts = {}
# create attributes to represent object state
self._currentMode = '' # user actions
self._oldName = '' # entry value at mode change
self._oldAddress = '' # entry value at mode change
# 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"))
self._editBtn = QPushButton(self.tr("&Edit"))
self._removeBtn = QPushButton(self.tr("&Remove"))
# set button visibility
self._addBtn.show()
self._submitBtn.hide()
self._cancelBtn.hide()
self._editBtn.setEnabled(False)
self._removeBtn.setEnabled(False)
# 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)
self._editBtn.clicked.connect(self._editContact)
self._removeBtn.clicked.connect(self._removeContact)
# layout buttons
layout = QVBoxLayout()
layout.addWidget(self._addBtn, Qt.AlignTop)
layout.addWidget(self._submitBtn)
layout.addWidget(self._cancelBtn)
layout.addWidget(self._editBtn)
layout.addWidget(self._removeBtn)
layout.addStretch() # force additional space to bottom
return layout
# private slots -----------------------------------------------------------
@pyqtSlot()
def _addContact(self):
self._oldName = self._nameLine.text()
self._oldAddress = self._addrText.toPlainText()
self._nameLine.clear()
self._addrText.clear()
self.updateInterface(self.AddMode)
@pyqtSlot()
def _submitContact(self):
name = self._nameLine.text()
address = self._addrText.toPlainText()
if not (name or address):
QMessageBox.information(self,
self.tr("Empty Field"),
self.tr("Please enter a name and address."))
return
if self._currentMode == self.AddMode:
if name not in self._contacts:
self._contacts[name] = address
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))
elif self._currentMode == self.EditMode:
if self._oldName != name:
if name not in self._contacts:
QMessageBox.information(self,
self.tr("Edit Successful"),
self.tr("{0} has been edited in your address book.").format(name))
del self._contacts[self._oldName]
self._contacts[name] = address
else:
QMessageBox.information(self,
self.tr("Edit Unsuccessful"),
self.tr("Sorry, {0} is already in your address book.").format(name))
elif self._oldAddress != address:
QMessageBox.information(self,
self.tr("Edit Successful"),
self.tr("{0} has been edited in your address book.").format(name))
self._contacts[name] = address
self.updateInterface(self.NavMode)
@pyqtSlot()
def _cancel(self):
self._nameLine.setText(self._oldName)
self._nameLine.setReadOnly(True)
self._addrText.setText(self._oldAddress)
self.updateInterface(self.NavMode)
@pyqtSlot()
def _editContact(self):
self._oldName = self._nameLine.text()
self._oldAddress = self._addrText.toPlainText()
self.updateInterface(self.EditMode)
@pyqtSlot()
def _removeContact(self):
name = self._nameLine.text()
if name in self._contacts:
ans = QMessageBox.question(
self,
self.tr("Confirm Remove"),
self.tr("Are you sure you want to remove {0}?").format(name),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
if ans == QMessageBox.Yes:
self.previous()
del self._contacts[name]
QMessageBox.information(
self,
self.tr("Remove successful"),
self.tr("{0} has been removed from your address book.").format(name),
QMessageBox.Ok)
self.updateInterface(self.NavMode)
# public slots ------------------------------------------------------------
@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])
# public methods ---------------------------------------------------------
def updateInterface(self, mode):
'''
Enable/disable buttons based on whether the user is adding,
editing or navigating Address Book entries.
'''
self._currentMode = mode
if mode != self.NavMode:
self._nameLine.setReadOnly(False)
self._nameLine.setFocus(Qt.OtherFocusReason)
self._addrText.setReadOnly(False)
self._addBtn.setEnabled(False)
self._editBtn.setEnabled(False)
self._removeBtn.setEnabled(False)
self._nextBtn.setEnabled(False)
self._prevBtn.setEnabled(False)
self._submitBtn.show()
self._cancelBtn.show()
else:
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._editBtn.setEnabled(num >= 1)
self._removeBtn.setEnabled(num >= 1)
self._nextBtn.setEnabled(num > 1)
self._prevBtn.setEnabled(num > 1)
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()
Page List
▼
Wednesday, January 25, 2012
Qt 4.8 Address Book Tutorial - Part 4
This code is based on the Qt 4.8 Address Book Tutorial Part 4, Editing and Removing Addresses.