demos/dbus-viewer/qdbusviewer.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 2004-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the demonstration applications of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qdbusviewer.h"
00025 #include "qdbusmodel.h"
00026 
00027 #include <QtXml/QtXml>
00028 
00029 class QDBusViewModel: public QDBusModel
00030 {
00031 public:
00032     inline QDBusViewModel(const QString &service, const QDBusConnection &connection)
00033         : QDBusModel(service, connection)
00034     {}
00035 
00036     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
00037     {
00038         if (role == Qt::FontRole && itemType(index) == InterfaceItem) {
00039             QFont f;
00040             f.setItalic(true);
00041             return f;
00042         }
00043         return QDBusModel::data(index, role);
00044     }
00045 };
00046 
00047 QDBusViewer::QDBusViewer(const QDBusConnection &connection, QWidget *parent)
00048     : QWidget(parent), c(connection)
00049 {
00050     services = new QTreeWidget;
00051     services->setRootIsDecorated(false);
00052     services->setHeaderLabels(QStringList("Services"));
00053 
00054     tree = new QTreeView;
00055     tree->setContextMenuPolicy(Qt::CustomContextMenu);
00056 
00057     connect(tree, SIGNAL(activated(const QModelIndex&)), this, SLOT(activate(const QModelIndex&)));
00058 
00059     refreshAction = new QAction(tr("&Refresh"), tree);
00060     refreshAction->setData(42); // increase the amount of 42 used as magic number by one
00061     refreshAction->setShortcut(QKeySequence::Refresh);
00062     connect(refreshAction, SIGNAL(triggered()), this, SLOT(refreshChildren()));
00063 
00064     QShortcut *refreshShortcut = new QShortcut(QKeySequence::Refresh, tree);
00065     connect(refreshShortcut, SIGNAL(activated()), this, SLOT(refreshChildren()));
00066 
00067     QVBoxLayout *topLayout = new QVBoxLayout(this);
00068     log = new QTextEdit;
00069     log->setReadOnly(true);
00070 
00071     QHBoxLayout *layout = new QHBoxLayout;
00072     layout->addWidget(services, 1);
00073     layout->addWidget(tree, 2);
00074 
00075     topLayout->addLayout(layout);
00076     topLayout->addWidget(log);
00077 
00078     connect(services, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
00079             this, SLOT(serviceChanged(QTreeWidgetItem*)));
00080     connect(tree, SIGNAL(customContextMenuRequested(QPoint)),
00081             this, SLOT(showContextMenu(QPoint)));
00082 
00083     QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection);
00084 
00085     if (c.isConnected()) {
00086         logMessage("Connected to D-Bus.");
00087         QDBusConnectionInterface *iface = c.interface();
00088         connect(iface, SIGNAL(serviceRegistered(QString)),
00089                 this, SLOT(serviceRegistered(QString)));
00090         connect(iface, SIGNAL(serviceUnregistered(QString)),
00091                 this, SLOT(serviceUnregistered(QString)));
00092         connect(iface, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
00093                 this, SLOT(serviceOwnerChanged(QString,QString,QString)));
00094     } else {
00095         logError("Cannot connect to D-Bus: " + c.lastError().message());
00096     }
00097 }
00098 
00099 void QDBusViewer::logMessage(const QString &msg)
00100 {
00101     log->append(msg + "\n");
00102 }
00103 
00104 void QDBusViewer::logError(const QString &msg)
00105 {
00106     log->append("<font color=\"red\">Error: </font>" + Qt::escape(msg) + "<br>");
00107 }
00108 
00109 void QDBusViewer::refresh()
00110 {
00111     services->clear();
00112 
00113     const QStringList serviceNames = c.interface()->registeredServiceNames();
00114     foreach (QString service, serviceNames)
00115         new QTreeWidgetItem(services, QStringList(service));
00116 }
00117 
00118 void QDBusViewer::activate(const QModelIndex &item)
00119 {
00120     if (!item.isValid())
00121         return;
00122 
00123     const QDBusModel *model = static_cast<const QDBusModel *>(item.model());
00124 
00125     BusSignature sig;
00126     sig.mService = currentService;
00127     sig.mPath = model->dBusPath(item);
00128     sig.mInterface = model->dBusInterface(item);
00129     sig.mName = model->dBusMethodName(item);
00130 
00131     switch (model->itemType(item)) {
00132     case QDBusModel::SignalItem:
00133         connectionRequested(sig);
00134         break;
00135     case QDBusModel::MethodItem:
00136         callMethod(sig);
00137         break;
00138 #if 0
00139     case QDBusModel::PropertyItem:
00140         break;
00141 #endif
00142     default:
00143         break;
00144     }
00145 }
00146 
00147 void QDBusViewer::callMethod(const BusSignature &sig)
00148 {
00149     QDBusInterface iface(sig.mService, sig.mPath, sig.mInterface, c);
00150     const QMetaObject *mo = iface.metaObject();
00151 
00152     // find the method
00153     QMetaMethod method;
00154     for (int i = 0; i < mo->methodCount(); ++i) {
00155         const QString signature = QString::fromLatin1(mo->method(i).signature());
00156         if (signature.startsWith(sig.mName) && signature.at(sig.mName.length()) == '(')
00157             method = mo->method(i);
00158     }
00159     if (!method.signature()) {
00160         QMessageBox::warning(this, "Unable to find method",
00161                 QString("Unable to find method %1 on path %2 in interface %3").arg(
00162                     sig.mName).arg(sig.mPath).arg(sig.mInterface));
00163         return;
00164     }
00165 
00166     QList<QByteArray> paramTypes = method.parameterTypes();
00167     int paramTypeCount = 0;
00168     foreach (QByteArray paramType, paramTypes) {
00169         if (paramType.endsWith('&'))
00170             continue; // ignore OUT parameters
00171 
00172         if (!QVariant(QVariant::nameToType(paramType)).canConvert(QVariant::String)) {
00173             QMessageBox::warning(this, "Unable to call method",
00174                     QString("Cannot marshall parameter of type %1").arg(
00175                         QVariant::nameToType(paramType)));
00176             return;
00177         }
00178         ++paramTypeCount;
00179     }
00180 
00181     QList<QVariant> arguments;
00182     if (!paramTypes.isEmpty()) {
00183         QString input;
00184         bool ok = true;
00185         while (arguments.isEmpty()) {
00186             input = QInputDialog::getText(this, "Arguments",
00187                     "Please enter the arguments for the call, separated by comma."
00188                     "<br>Example: <i>hello,world,2.3,-34</i><br><br><b>Signature:</b> "
00189                     + Qt::escape(QString::fromUtf8(method.signature())),
00190                     QLineEdit::Normal, input, &ok);
00191             if (!ok)
00192                 return;
00193 
00194             QStringList argStrings = input.split(',');
00195             if (argStrings.count() != paramTypeCount)
00196                 continue;
00197 
00198             for (int i = 0; i < argStrings.count(); ++i) {
00199                 if (paramTypes.at(i).endsWith('&'))
00200                     continue; // ignore OUT paramters
00201 
00202                 QVariant v = argStrings.at(i);
00203                 if (!v.convert(QVariant::nameToType(paramTypes.at(i)))) {
00204                     arguments.clear();
00205                     break;
00206                 }
00207                 arguments += v;
00208             }
00209         }
00210     }
00211 
00212     QDBusMessage message = QDBusMessage::createMethodCall(sig.mService, sig.mPath, sig.mInterface,
00213             sig.mName);
00214     message.setArguments(arguments);
00215     c.callWithCallback(message, this, SLOT(dumpMessage(QDBusMessage)));
00216 }
00217 
00218 void QDBusViewer::showContextMenu(const QPoint &point)
00219 {
00220     QModelIndex item = tree->indexAt(point);
00221     if (!item.isValid())
00222         return;
00223 
00224     const QDBusModel *model = static_cast<const QDBusModel *>(item.model());
00225 
00226     BusSignature sig;
00227     sig.mService = currentService;
00228     sig.mPath = model->dBusPath(item);
00229     sig.mInterface = model->dBusInterface(item);
00230     sig.mName = model->dBusMethodName(item);
00231 
00232     QMenu menu;
00233     menu.addAction(refreshAction);
00234 
00235     switch (model->itemType(item)) {
00236     case QDBusModel::SignalItem: {
00237         QAction *action = new QAction("&Connect", &menu);
00238         action->setData(1);
00239         menu.addAction(action);
00240         break; }
00241     case QDBusModel::MethodItem: {
00242         QAction *action = new QAction("&Call", &menu);
00243         action->setData(2);
00244         menu.addAction(action);
00245         break; }
00246 #if 0
00247     case PropertyItem: {
00248         QAction *actionSet = new QAction("&Set value", &menu);
00249         actionSet->setData(3);
00250         QAction *actionGet = new QAction("&Get value", &menu);
00251         actionGet->setData(4);
00252         menu.addAction(actionSet);
00253         menu.addAction(actionGet);
00254         break; }
00255 #endif
00256     default:
00257         break;
00258     }
00259 
00260     QAction *selectedAction = menu.exec(tree->viewport()->mapToGlobal(point));
00261     if (!selectedAction)
00262         return;
00263 
00264     switch (selectedAction->data().toInt()) {
00265     case 1:
00266         connectionRequested(sig);
00267         break;
00268     case 2:
00269         callMethod(sig);
00270         break;
00271     }
00272 }
00273 
00274 void QDBusViewer::connectionRequested(const BusSignature &sig)
00275 {
00276     if (!c.connect(sig.mService, QString(), sig.mInterface, sig.mName, this,
00277               SLOT(dumpMessage(QDBusMessage)))) {
00278         logError(QString("Unable to connect to service %1, path %2, interface %3, signal %4").arg(
00279                     sig.mService).arg(sig.mPath).arg(sig.mInterface).arg(sig.mName));
00280     }
00281 }
00282 
00283 void QDBusViewer::dumpMessage(const QDBusMessage &message)
00284 {
00285     QList<QVariant> args = message.arguments();
00286     QString out = "Received ";
00287 
00288     switch (message.type()) {
00289     case QDBusMessage::SignalMessage:
00290         out += "signal ";
00291         break;
00292     case QDBusMessage::ErrorMessage:
00293         out += "error message ";
00294         break;
00295     case QDBusMessage::ReplyMessage:
00296         out += "reply ";
00297         break;
00298     default:
00299         out += "message ";
00300         break;
00301     }
00302 
00303     out += "from ";
00304     out += message.service();
00305     if (!message.path().isEmpty())
00306         out += ", path " + message.path();
00307     if (!message.interface().isEmpty())
00308         out += ", interface <i>" + message.interface() + "</i>";
00309     if (!message.member().isEmpty())
00310         out += ", member " + message.member();
00311     out += "<br>";
00312     if (!args.isEmpty()) {
00313         out += "&nbsp;&nbsp;Parameters: ";
00314         foreach (QVariant arg, args) {
00315             if (arg.canConvert(QVariant::StringList)) {
00316                 out += "<b>{</b>";
00317                 QStringList list = arg.toStringList();
00318                 foreach (QString item, list)
00319                     out += "<b>\"</b>" + Qt::escape(item) + "<b>\"</b>, ";
00320                 if (!list.isEmpty())
00321                     out.chop(2);
00322                 out += "<b>}</b>";
00323             } else if (arg.canConvert(QVariant::Rect)) {
00324                 QRect r = arg.toRect();
00325                 out += QString::fromLatin1("QRect(%1, %2, %3, %4)").arg(r.left()).arg(r.top()).arg(
00326                         r.right()).arg(r.bottom());
00327             } else if (qVariantCanConvert<QDBusArgument>(arg)) {
00328                 out += "[QDBusArgument: " + qvariant_cast<QDBusArgument>(arg).currentSignature();
00329                 out += "]";
00330             } else if (arg.canConvert(QVariant::String)) {
00331                 out += "<b>\"</b>" + Qt::escape(arg.toString()) + "<b>\"</b>";
00332             } else {
00333                 out += "[";
00334                 out += arg.typeName();
00335                 out += "]";
00336             }
00337             out += ", ";
00338         }
00339         out.chop(2);
00340     }
00341 
00342     log->append(out);
00343 }
00344 
00345 void QDBusViewer::serviceChanged(QTreeWidgetItem *item)
00346 {
00347     delete tree->model();
00348 
00349     currentService.clear();
00350     if (!item)
00351         return;
00352     currentService = item->text(0);
00353 
00354     tree->setModel(new QDBusViewModel(currentService, c));
00355     connect(tree->model(), SIGNAL(busError(QString)), this, SLOT(logError(QString)));
00356 }
00357 
00358 void QDBusViewer::serviceRegistered(const QString &service)
00359 {
00360     if (service == c.baseService())
00361         return;
00362 
00363     new QTreeWidgetItem(services, QStringList(service));
00364 }
00365 
00366 static QTreeWidgetItem *findItem(const QTreeWidget *services, const QString &name)
00367 {
00368     for (int i = 0; i < services->topLevelItemCount(); ++i) {
00369         if (services->topLevelItem(i)->text(0) == name)
00370             return services->topLevelItem(i);
00371     }
00372     return 0;
00373 }
00374 
00375 void QDBusViewer::serviceUnregistered(const QString &name)
00376 {
00377     delete findItem(services, name);
00378 }
00379 
00380 void QDBusViewer::serviceOwnerChanged(const QString &name, const QString &oldOwner,
00381                                       const QString &newOwner)
00382 {
00383     QTreeWidgetItem *item = findItem(services, name);
00384 
00385     if (!item && oldOwner.isEmpty() && !newOwner.isEmpty())
00386         serviceRegistered(name);
00387     else if (item && !oldOwner.isEmpty() && newOwner.isEmpty())
00388         delete item;
00389     else if (item && !oldOwner.isEmpty() && !newOwner.isEmpty()) {
00390         delete item;
00391         serviceRegistered(name);
00392     }
00393 }
00394 
00395 void QDBusViewer::refreshChildren()
00396 {
00397     QDBusModel *model = qobject_cast<QDBusModel *>(tree->model());
00398     if (!model)
00399         return;
00400     model->refresh(tree->currentIndex());
00401 }
00402 

Generated on Thu Mar 15 11:52:32 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1