Qutlook Example (ActiveQt)▲
The project file for the example looks like this:
TEMPLATE =
app
TARGET =
qutlook
QT +=
widgets axcontainer
TYPELIBS =
$$system(dumpcpp -
getfile {
00062
FFF-
0000
-
0000
-
C000-
000000000046
}
)
isEmpty(TYPELIBS) {
message("Microsoft Outlook type library not found!"
)
REQUIRES +=
Outlook
}
else
{
HEADERS =
addressview.h
SOURCES =
addressview.cpp main.cpp
}
The project file uses the dumpcpp tool to add an MS Outlook type library to the project. If this fails, then the generated makefile will just print an error message, otherwise the build step will now run the dumpcpp tool on the type library, and generate a header and a cpp file (in this case, msoutl.h and msoutl.cpp) that declares and implement an easy to use API to the Outlook objects.
class
AddressView : public
QWidget
{
Q_OBJECT
public
:
explicit
AddressView(QWidget *
parent =
nullptr
);
protected
slots:
void
addEntry();
void
changeEntry();
void
itemSelected(const
QModelIndex &
amp;index);
void
updateOutlook();
protected
:
AddressBookModel *
model;
QTreeView *
m_treeView;
QPushButton *
m_addButton;
QPushButton *
m_changeButton;
QLineEdit *
m_firstName;
QLineEdit *
m_lastName;
QLineEdit *
m_address;
QLineEdit *
m_email;
}
;
The AddressView class is a QWidget subclass for the user interface. The QTreeView widget will display the contents of Outlook's Contact folder as provided by the model.
#include
"addressview.h"
#include
"msoutl.h"
#include <QtWidgets>
class
AddressBookModel : public
QAbstractListModel
{
public
:
explicit
AddressBookModel(AddressView *
parent);
virtual
~
AddressBookModel();
int
rowCount(const
QModelIndex &
amp;parent =
QModelIndex()) const
;
int
columnCount(const
QModelIndex &
amp;parent) const
;
QVariant headerData(int
section, Qt::
Orientation orientation, int
role) const
;
QVariant data(const
QModelIndex &
amp;index, int
role) const
;
void
changeItem(const
QModelIndex &
amp;index, const
QString &
amp;firstName, const
QString &
amp;lastName, const
QString &
amp;address, const
QString &
amp;email);
void
addItem(const
QString &
amp;firstName, const
QString &
amp;lastName, const
QString &
amp;address, const
QString &
amp;email);
void
update();
private
:
Outlook::
Application outlook;
Outlook::
Items *
contactItems;
mutable
QHash&
lt;QModelIndex, QStringList&
gt; cache;
}
;
The AddressBookModel class is a QAbstractListModel subclass that communicates directly with Outlook, using a QHash for caching.
AddressBookModel::
AddressBookModel(AddressView *
parent)
:
QAbstractListModel(parent)
{
if
(!
outlook.isNull()) {
Outlook::
NameSpace session(outlook.Session());
session.Logon();
Outlook::
MAPIFolder *
folder =
session.GetDefaultFolder(Outlook::
olFolderContacts);
contactItems =
new
Outlook::
Items(folder-&
gt;Items());
connect(contactItems, SIGNAL(ItemAdd(IDispatch*
)), parent, SLOT(updateOutlook()));
connect(contactItems, SIGNAL(ItemChange(IDispatch*
)), parent, SLOT(updateOutlook()));
connect(contactItems, SIGNAL(ItemRemove()), parent, SLOT(updateOutlook()));
delete
folder;
}
}
The constructor initializes Outlook. The various signals Outlook provides to notify about contents changes are connected to the updateOutlook() slot.
AddressBookModel::
~
AddressBookModel()
{
delete
contactItems;
if
(!
outlook.isNull())
Outlook::
NameSpace(outlook.Session()).Logoff();
}
The destructor logs off from the session.
int
AddressBookModel::
rowCount(const
QModelIndex &
amp;) const
{
return
contactItems ? contactItems-&
gt;Count() : 0
;
}
int
AddressBookModel::
columnCount(const
QModelIndex &
amp; /*parent*/
) const
{
return
4
;
}
The rowCount() implementation returns the number of entries as reported by Outlook. columnCount and headerData are implemented to show four columns in the tree view.
QVariant AddressBookModel::
headerData(int
section, Qt::
Orientation /*orientation*/
, int
role) const
{
if
(role !=
Qt::
DisplayRole)
return
QVariant();
switch
(section) {
case
0
:
return
tr("First Name"
);
case
1
:
return
tr("Last Name"
);
case
2
:
return
tr("Address"
);
case
3
:
return
tr("Email"
);
default
:
break
;
}
return
QVariant();
}
The headerData() implementation returns hardcoded strings.
QVariant AddressBookModel::
data(const
QModelIndex &
amp;index, int
role) const
{
if
(!
index.isValid() ||
role !=
Qt::
DisplayRole)
return
QVariant();
QStringList data;
if
(cache.contains(index)) {
data =
cache.value(index);
}
else
{
Outlook::
ContactItem contact(contactItems-&
gt;Item(index.row() +
1
));
data &
lt;&
lt; contact.FirstName() &
lt;&
lt; contact.LastName() &
lt;&
lt; contact.HomeAddress() &
lt;&
lt; contact.Email1Address();
cache.insert(index, data);
}
if
(index.column() &
lt; data.size())
return
data.at(index.column());
return
QVariant();
}
The data() implementation is the core of the model. If the requested data is in the cache the cached value is used, otherwise the data is acquired from Outlook.
void
AddressBookModel::
changeItem(const
QModelIndex &
amp;index, const
QString &
amp;firstName, const
QString &
amp;lastName, const
QString &
amp;address, const
QString &
amp;email)
{
Outlook::
ContactItem item(contactItems-&
gt;Item(index.row() +
1
));
item.SetFirstName(firstName);
item.SetLastName(lastName);
item.SetHomeAddress(address);
item.SetEmail1Address(email);
item.Save();
cache.take(index);
}
The changeItem() slot is called when the user changes the current entry using the user interface. The Outlook item is accessed using the Outlook API, and is modified using the property setters. Finally, the item is saved to Outlook, and removed from the cache. Note that the model does not signal the view of the data change, as Outlook will emit a signal on its own.
void
AddressBookModel::
addItem(const
QString &
amp;firstName, const
QString &
amp;lastName, const
QString &
amp;address, const
QString &
amp;email)
{
Outlook::
ContactItem item(outlook.CreateItem(Outlook::
olContactItem));
if
(!
item.isNull()) {
item.SetFirstName(firstName);
item.SetLastName(lastName);
item.SetHomeAddress(address);
item.SetEmail1Address(email);
item.Save();
}
}
The addItem() slot calls the CreateItem method of Outlook to create a new contact item, sets the properties of the new item to the values entered by the user and saves the item.
void
AddressBookModel::
update()
{
beginResetModel();
cache.clear();
endResetModel();
}
The update() slot clears the cache, and emits the reset() signal to notify the view about the data change requiring a redraw of the contents.
AddressView::
AddressView(QWidget *
parent)
:
QWidget(parent)
{
QGridLayout *
mainGrid =
new
QGridLayout(this
);
QLabel *
firstNameLabel =
new
QLabel(tr("First &Name"
), this
);
firstNameLabel-&
gt;resize(firstNameLabel-&
gt;sizeHint());
mainGrid-&
gt;addWidget(firstNameLabel, 0
, 0
);
QLabel *
lastNameLabel =
new
QLabel(tr("&Last Name"
), this
);
lastNameLabel-&
gt;resize(lastNameLabel-&
gt;sizeHint());
mainGrid-&
gt;addWidget(lastNameLabel, 0
, 1
);
QLabel *
addressLabel =
new
QLabel(tr("Add&ress"
), this
);
addressLabel-&
gt;resize(addressLabel-&
gt;sizeHint());
mainGrid-&
gt;addWidget(addressLabel, 0
, 2
);
QLabel *
emailLabel =
new
QLabel(tr("&E-Mail"
), this
);
emailLabel-&
gt;resize(emailLabel-&
gt;sizeHint());
mainGrid-&
gt;addWidget(emailLabel, 0
, 3
);
m_addButton =
new
QPushButton(tr("A&dd"
), this
);
m_addButton-&
gt;resize(m_addButton-&
gt;sizeHint());
mainGrid-&
gt;addWidget(m_addButton, 0
, 4
);
connect(m_addButton, &
amp;QPushButton::
clicked, this
, &
amp;AddressView::
addEntry);
m_firstName =
new
QLineEdit(this
);
m_firstName-&
gt;resize(m_firstName-&
gt;sizeHint());
mainGrid-&
gt;addWidget(m_firstName, 1
, 0
);
firstNameLabel-&
gt;setBuddy(m_firstName);
m_lastName =
new
QLineEdit(this
);
m_lastName-&
gt;resize(m_lastName-&
gt;sizeHint());
mainGrid-&
gt;addWidget(m_lastName, 1
, 1
);
lastNameLabel-&
gt;setBuddy(m_lastName);
m_address =
new
QLineEdit(this
);
m_address-&
gt;resize(m_address-&
gt;sizeHint());
mainGrid-&
gt;addWidget(m_address, 1
, 2
);
addressLabel-&
gt;setBuddy(m_address);
m_email =
new
QLineEdit(this
);
m_email-&
gt;resize(m_email-&
gt;sizeHint());
mainGrid-&
gt;addWidget(m_email, 1
, 3
);
emailLabel-&
gt;setBuddy(m_email);
m_changeButton =
new
QPushButton(tr("&Change"
), this
);
m_changeButton-&
gt;resize(m_changeButton-&
gt;sizeHint());
mainGrid-&
gt;addWidget(m_changeButton, 1
, 4
);
connect(m_changeButton, &
amp;QPushButton::
clicked, this
, &
amp;AddressView::
changeEntry);
m_treeView =
new
QTreeView(this
);
m_treeView-&
gt;setSelectionMode(QTreeView::
SingleSelection);
m_treeView-&
gt;setRootIsDecorated(false
);
model =
new
AddressBookModel(this
);
m_treeView-&
gt;setModel(model);
connect(m_treeView-&
gt;selectionModel(), &
amp;QItemSelectionModel::
currentChanged, this
, &
amp;AddressView::
itemSelected);
mainGrid-&
gt;addWidget(m_treeView, 2
, 0
, 1
, 5
);
}
void
AddressView::
updateOutlook()
{
model-&
gt;update();
}
void
AddressView::
addEntry()
{
if
(!
m_firstName-&
gt;text().isEmpty() ||
!
m_lastName-&
gt;text().isEmpty() ||
!
m_address-&
gt;text().isEmpty() ||
!
m_email-&
gt;text().isEmpty()) {
model-&
gt;addItem(m_firstName-&
gt;text(), m_lastName-&
gt;text(), m_address-&
gt;text(), m_email-&
gt;text());
}
m_firstName-&
gt;clear();
m_lastName-&
gt;clear();
m_address-&
gt;clear();
m_email-&
gt;clear();
}
void
AddressView::
changeEntry()
{
QModelIndex current =
m_treeView-&
gt;currentIndex();
if
(current.isValid())
model-&
gt;changeItem(current, m_firstName-&
gt;text(), m_lastName-&
gt;text(), m_address-&
gt;text(), m_email-&
gt;text());
}
void
AddressView::
itemSelected(const
QModelIndex &
amp;index)
{
if
(!
index.isValid())
return
;
QAbstractItemModel *
model =
m_treeView-&
gt;model();
m_firstName-&
gt;setText(model-&
gt;data(model-&
gt;index(index.row(), 0
)).toString());
m_lastName-&
gt;setText(model-&
gt;data(model-&
gt;index(index.row(), 1
)).toString());
m_address-&
gt;setText(model-&
gt;data(model-&
gt;index(index.row(), 2
)).toString());
m_email-&
gt;setText(model-&
gt;data(model-&
gt;index(index.row(), 3
)).toString());
}
The rest of the file implements the user interface using only Qt APIs, i.e. without communicating with Outlook directly.
#include
"addressview.h"
#include <QApplication>
int
main(int
argc, char
*
argv[])
{
QApplication a(argc, argv);
AddressView view;
view.setWindowTitle(QObject::
tr("Qt Example - Looking at Outlook"
));
view.show();
return
a.exec();
}
The main() entry point function finally instantiates the user interface and enters the event loop.
To build the example you must first build the QAxContainer library. Then run your make tool in examples/activeqt/qutlook and run the resulting qutlook.exe.