CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxPluginBrowser.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 // Based on a class from CTK:
13 
14 /*=============================================================================
15 
16  Library: CTK
17 
18  Copyright (c) German Cancer Research Center,
19  Division of Medical and Biological Informatics
20 
21  Licensed under the Apache License, Version 2.0 (the "License");
22  you may not use this file except in compliance with the License.
23  You may obtain a copy of the License at
24 
25  http://www.apache.org/licenses/LICENSE-2.0
26 
27  Unless required by applicable law or agreed to in writing, software
28  distributed under the License is distributed on an "AS IS" BASIS,
29  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30  See the License for the specific language governing permissions and
31  limitations under the License.
32 
33 =============================================================================*/
34 
35 #include "cxPluginBrowser.h"
36 #include <QtWidgets>
37 
38 #include "cxPluginTableModel.h"
39 #include "ctkServiceReference.h"
40 #include "ctkServiceException.h"
41 #include "ctkPluginConstants.h"
42 
43 #include <ctkConfig.h>
44 #include <ctkPluginException.h>
45 #include <ctkPluginFramework.h>
46 #include <ctkPluginContext.h>
47 #include <iostream>
48 
49 #include <QApplication>
50 #include <QMainWindow>
51 #include <QStringList>
52 #include <QDirIterator>
53 #include <QUrl>
54 #include <QSettings>
55 #include <QCloseEvent>
56 #include "cxSettings.h"
57 
58 //#define SETTINGS_WND_GEOM "mainwindow.geom"
59 //#define SETTINGS_WND_STATE "mainwindow.state"
60 
61 namespace cx
62 {
63 
65 : framework(framework)
66 {
67  mSettingsMainWindowStateTag = "pluginbrowser/state";
68  mSettingsMainWindowGeometryTag = "pluginbrowser/geometry";
69 
70  pluginEventTypeToString[ctkPluginEvent::INSTALLED] = "Installed";
71  pluginEventTypeToString[ctkPluginEvent::LAZY_ACTIVATION] = "Lazy Activation";
72  pluginEventTypeToString[ctkPluginEvent::RESOLVED] = "Resolved";
73  pluginEventTypeToString[ctkPluginEvent::STARTED] = "Started";
74  pluginEventTypeToString[ctkPluginEvent::STARTING] = "Starting";
75  pluginEventTypeToString[ctkPluginEvent::STOPPED] = "Stopped";
76  pluginEventTypeToString[ctkPluginEvent::STOPPING] = "Stopping";
77  pluginEventTypeToString[ctkPluginEvent::UNINSTALLED] = "Uninstalled";
78  pluginEventTypeToString[ctkPluginEvent::UNRESOLVED] = "Unresolved";
79  pluginEventTypeToString[ctkPluginEvent::UPDATED] = "Updated";
80 
81 // qDebug() << "ctkPluginBrowser::ctkPluginBrowser 1";
82 // qDebug() << "ctkPluginBrowser::ctkPluginBrowser fw is null: " << (framework.get() == NULL);
83 // qDebug() << "ctkPluginBrowser::ctkPluginBrowser" << framework->getPluginContext();
84  framework->getPluginContext()->connectFrameworkListener(this, SLOT(frameworkEvent(ctkPluginFrameworkEvent)));
85  framework->getPluginContext()->connectPluginListener(this, SLOT(pluginEvent(ctkPluginEvent)));
86  framework->getPluginContext()->connectServiceListener(this, "serviceEvent");
87 // qDebug() << "ctkPluginBrowser::ctkPluginBrowser 2";
88 
89  this->setupUi();
90 
91  mPluginTableModel = new ctkPluginTableModel(framework, this);
92  pluginsTableView->setModel(mPluginTableModel);
93 
94  connect(pluginsTableView->selectionModel(),
95  &QItemSelectionModel::currentChanged,
96  this,
97  &ctkPluginBrowser::pluginSelected);
98 
99  startPluginNowAction = new QAction(QIcon(":/icons/open_icon_library/arrow-right-double-3.png"), "Start Plugin (ignore activation policy)", this);
100  startPluginAction = new QAction(QIcon(":/icons/open_icon_library/arrow-right-3.png"), "Start Plugin", this);
101  stopPluginAction = new QAction(QIcon(":/icons/open_icon_library/media-record-3.png"), "Stop Plugin", this);
102  toggleInstallPluginAction = new QAction(QIcon(":/icons/preset_reset.png"), "Toggle Install", this);
103 
104  connect(startPluginNowAction, SIGNAL(triggered()), this, SLOT(startPluginNow()));
105  connect(startPluginAction, SIGNAL(triggered()), this, SLOT(startPlugin()));
106  connect(stopPluginAction, SIGNAL(triggered()), this, SLOT(stopPlugin()));
107  connect(toggleInstallPluginAction, SIGNAL(triggered()), this, SLOT(toggleInstallPlugin()));
108 
109  startPluginNowAction->setEnabled(false);
110  startPluginAction->setEnabled(false);
111  stopPluginAction->setEnabled(false);
112 
113  pluginToolBar->addAction(startPluginNowAction);
114  pluginToolBar->addAction(startPluginAction);
115  pluginToolBar->addAction(stopPluginAction);
116  pluginToolBar->addAction(toggleInstallPluginAction);
117 
118  if(settings()->contains(mSettingsMainWindowGeometryTag))
119  {
120  this->restoreGeometry(settings()->value(mSettingsMainWindowGeometryTag).toByteArray());
121  }
122  if (settings()->contains(mSettingsMainWindowStateTag))
123  {
124  this->restoreState(settings()->value(mSettingsMainWindowStateTag).toByteArray());
125  }
126 
127  QTimer::singleShot(0, this, SLOT(resizePluginsTableView()));
128 }
129 
131 {
132  settings()->setValue(mSettingsMainWindowGeometryTag, this->saveGeometry());
133  settings()->setValue(mSettingsMainWindowStateTag, this->saveState());
134 }
135 
136 void ctkPluginBrowser::resizePluginsTableView()
137 {
138  pluginsTableView->resizeColumnToContents(0);
139 }
140 
141 void ctkPluginBrowser::setupUi()
142 {
143  pluginsTableView = new QTableView(this);
144  pluginsTableView->setObjectName(QString::fromUtf8("pluginsTableView"));
145  pluginsTableView->setWindowTitle("Plugins");
146  pluginsTableView->setAutoScroll(false);
147  pluginsTableView->setSelectionMode(QAbstractItemView::SingleSelection);
148  pluginsTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
149  pluginsTableView->setShowGrid(false);
150  pluginsTableView->setSortingEnabled(true);
151  pluginsTableView->horizontalHeader()->setCascadingSectionResizes(true);
152  pluginsTableView->horizontalHeader()->setHighlightSections(false);
153  pluginsTableView->horizontalHeader()->setProperty("showSortIndicator", QVariant(true));
154  pluginsTableView->horizontalHeader()->setStretchLastSection(true);
155  pluginsTableView->verticalHeader()->setVisible(false);
156 
157  this->wrapInDockWidget(pluginsTableView);
158 
159  pluginToolBar = new QToolBar(this);
160  pluginToolBar->setObjectName(QString::fromUtf8("pluginToolBar"));
161  this->addToolBar(Qt::TopToolBarArea, pluginToolBar);
162 
163  searchPathSelectWidget = new cx::MultiFileInputWidget(this);
164  searchPathSelectWidget->setDescription("Search Paths");
165  searchPathSelectWidget->setWindowTitle("Search Paths");
166 
167  searchPathSelectWidget->setHelp("Select where to look for plugins");
168  searchPathSelectWidget->setBrowseHelp("Browse for a search path");
169  searchPathSelectWidget->setUseRelativePath(true);
170  searchPathSelectWidget->setBasePath(qApp->applicationDirPath());
171 
172  searchPathSelectWidget->setFilenames(framework->getSearchPaths());
173  connect(searchPathSelectWidget, SIGNAL(fileChanged()), this, SLOT(searchPathChanged()));
174 
175  this->wrapInDockWidget(searchPathSelectWidget);
176 
177  mPluginInfoWidget = new QTextEdit(this);
178  mPluginInfoWidget->setObjectName("PluginInfoWidget");
179  mPluginInfoWidget->setWindowTitle("Plugin Info");
180  this->wrapInDockWidget(mPluginInfoWidget);
181 
182 }
183 
184 void ctkPluginBrowser::wrapInDockWidget(QWidget* widget)
185 {
186  QDockWidget* dockWidget = new QDockWidget(widget->windowTitle(), this);
187  dockWidget->setObjectName(widget->objectName()+"_dockwidget");
188  dockWidget->setFeatures(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable);
189  dockWidget->setWidget(widget);
190  this->addDockWidget(static_cast<Qt::DockWidgetArea>(1), dockWidget);
191 }
192 
193 void ctkPluginBrowser::searchPathChanged()
194 {
195  QStringList files = searchPathSelectWidget->getAbsoluteFilenames();
196  QStringList validFiles;
197  for (int i=0; i<files.size(); ++i)
198  if (QFileInfo(files[i]).exists())
199  validFiles << files[i];
200 
201  framework->setSearchPaths(validFiles);
202 }
203 
204 void ctkPluginBrowser::pluginSelected(const QModelIndex &index)
205 {
206  this->updatePluginToolbar();
207  this->updatePluginInfo();
208 }
209 
210 void ctkPluginBrowser::updatePluginToolbar()
211 {
212  startPluginNowAction->setEnabled(false);
213  startPluginAction->setEnabled(false);
214  stopPluginAction->setEnabled(false);
215 
216  PluginData plugin = this->getSelectedPlugin();
217 
218  if (!plugin.second)
219  {
220  toggleInstallPluginAction->setText("Install");
221  return;
222  }
223  toggleInstallPluginAction->setText("Uninstall");
224 
225  const ctkPlugin::States startStates = ctkPlugin::INSTALLED | ctkPlugin::RESOLVED | ctkPlugin::STOPPING;
226  const ctkPlugin::States stopStates = ctkPlugin::STARTING | ctkPlugin::ACTIVE;
227  if (startStates.testFlag(plugin.second->getState()))
228  {
229  startPluginNowAction->setEnabled(true);
230  startPluginAction->setEnabled(true);
231  }
232 
233  if (stopStates.testFlag(plugin.second->getState()))
234  {
235  stopPluginAction->setEnabled(true);
236  }
237 }
238 
239 void ctkPluginBrowser::updatePluginInfo()
240 {
241  PluginData plugin = this->getSelectedPlugin();
242 
243  QString text;
244 
245  if (plugin.second)
246  {
247  QString manifest("/META-INF/MANIFEST.MF");
248  text += QString("%1").arg(manifest);
249  text += QString("<hr>");
250  QByteArray mfContent = plugin.second->getResource(manifest);
251  QString rawText("<div style=\"white-space: pre-wrap;\">%1</div>");
252  text += rawText.arg(QString(mfContent));
253  text += QString("<hr>");
254  text += QString("<p><b>Location:</b> %1</p>").arg(plugin.second->getLocation());
255  }
256 
257  if (!plugin.second.isNull() && plugin.second->getPluginContext())
258  {
259  QStringList serviceTexts;
260  QList<ctkServiceReference> serviceRefs = plugin.second->getPluginContext()->getServiceReferences("");
261  QListIterator<ctkServiceReference> it(serviceRefs);
262  while (it.hasNext())
263  {
264  ctkServiceReference ref(it.next());
265 
266  if (ref.getPlugin()->getSymbolicName() != plugin.first)
267  continue;
268 
269  serviceTexts += QString("<tr><td><b>Service</td></tr>");
270  QStringList propKeys = ref.getPropertyKeys();
271  for (int i=0; i<propKeys.size(); ++i)
272  {
273  QString val = ref.getProperty(propKeys[i]).toString();
274  QString bblue("<b><font color=\"blue\">%1</font></b>");
275  QString green("<b><font color=\"green\">%1</font></b>");
276  QString formattedKey = green.arg(propKeys[i]);
277  QString formattedVal = bblue.arg(val);
278  serviceTexts += QString("<tr> <td></td> <td>%1</td> <td>%2</td> </tr>").arg(formattedKey).arg(formattedVal);
279  }
280  }
281 
282  text += QString("<table>%1</table>").arg(serviceTexts.join("\n"));
283  }
284  else
285  {
286  text += QString("<p>Service info not available</p>");
287  }
288 
289  mPluginInfoWidget->setHtml(text);
290 }
291 
292 void ctkPluginBrowser::frameworkEvent(const ctkPluginFrameworkEvent& event)
293 {
294  qDebug() << "FrameworkEvent: [" << event.getPlugin()->getSymbolicName() << "]" << event.getErrorString();
295 }
296 
297 void ctkPluginBrowser::pluginEvent(const ctkPluginEvent& event)
298 {
299  qDebug() << "PluginEvent: [" << event.getPlugin()->getSymbolicName() << "]" << pluginEventTypeToString[event.getType()];
300 
301  updatePluginToolbar();
302 }
303 
304 void ctkPluginBrowser::serviceEvent(const ctkServiceEvent &event)
305 {
306  ctkServiceReference ref = event.getServiceReference();
307  qDebug() << "ServiceEvent: [" << event.getType() << "]" << event.getServiceReference().getUsingPlugins();
308  qDebug() << " keys: " << ref.getPropertyKeys();
309  qDebug() << " objectclass: " << ref.getProperty("objectclass");
310 }
311 
312 PluginData ctkPluginBrowser::getSelectedPlugin()
313 {
314  QModelIndex selection = pluginsTableView->selectionModel()->currentIndex();
315  QVariant v = selection.data(Qt::UserRole);
316  QString name = v.toString();
317  ctkPluginPtr plugin = framework->getInstalledPluginFromSymbolicName(name);
318  return std::make_pair(name, plugin);
319 }
320 
321 void ctkPluginBrowser::toggleInstallPlugin()
322 {
323  PluginData plugin = this->getSelectedPlugin();
324  if (plugin.second)
325  {
326  framework->uninstall(plugin.first);
327  }
328  else
329  {
330  framework->install(plugin.first);
331  }
332 }
333 
334 void ctkPluginBrowser::startPlugin()
335 {
336  startPlugin(ctkPlugin::START_TRANSIENT | ctkPlugin::START_ACTIVATION_POLICY);
337 }
338 
339 void ctkPluginBrowser::startPluginNow()
340 {
341  startPlugin(ctkPlugin::START_TRANSIENT);
342 }
343 
344 void ctkPluginBrowser::startPlugin(ctkPlugin::StartOptions options)
345 {
346  PluginData plugin = this->getSelectedPlugin();
347  framework->start(plugin.first, options);
348 }
349 
350 void ctkPluginBrowser::stopPlugin()
351 {
352  PluginData plugin = this->getSelectedPlugin();
353  framework->stop(plugin.first);
354 }
355 
356 }
QSharedPointer< ctkPlugin > ctkPluginPtr
QStringList getAbsoluteFilenames() const
void setValue(const QString &key, const QVariant &value)
Definition: cxSettings.cpp:58
std::pair< QString, ctkPluginPtr > PluginData
ctkPluginBrowser(PluginFrameworkManagerPtr framework)
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:21
boost::shared_ptr< class PluginFrameworkManager > PluginFrameworkManagerPtr
void setFilenames(QStringList text)
Namespace for all CustusX production code.