#!/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()
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.
Labels:
Qt Address Book Example