Medical Imaging Interaction Toolkit  2018.4.99-c0f884b2
Medical Imaging Interaction Toolkit
QmitkNodeSelectionButton.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 
15 
16 // berry includes
17 #include <berryWorkbenchPlugin.h>
18 #include <berryQtStyleManager.h>
19 
20 #include "QPainter"
21 #include "QTextDocument"
22 #include "QEvent"
23 
24 #include <mitkDataNode.h>
26 
27 // mitk core
28 #include <mitkBaseRenderer.h>
29 #include <mitkDataNode.h>
30 #include <mitkExtractSliceFilter.h>
32 #include <mitkPlanarFigure.h>
33 #include <mitkPropertyNameHelper.h>
34 
35 // vtk
36 #include <vtkLookupTable.h>
37 
38 QPixmap GetPixmapFromImageNode(const mitk::DataNode* dataNode, int height)
39 {
40  if (nullptr == dataNode)
41  {
42  return QPixmap();
43  }
44 
45  const mitk::Image* image = dynamic_cast<const mitk::Image*>(dataNode->GetData());
46  if ((nullptr == image || !image->IsInitialized()) || // -> must be an image
47  (image->GetPixelType().GetNumberOfComponents() != 1)) // -> for now only single component are allowed
48  {
49  auto descManager = QmitkNodeDescriptorManager::GetInstance();
50  auto desc = descManager->GetDescriptor(dataNode);
51  auto icon = desc->GetIcon(dataNode);
52  auto fallBackMap = icon.pixmap(height, height);
53  return fallBackMap;
54  }
55 
57  int sliceNumber = image->GetDimension(2) / 2;
58  planeGeometry->InitializeStandardPlane(image->GetGeometry(), mitk::PlaneGeometry::Axial, sliceNumber);
59 
61  extractSliceFilter->SetInput(image);
62  extractSliceFilter->SetInterpolationMode(mitk::ExtractSliceFilter::RESLICE_CUBIC);
63  extractSliceFilter->SetResliceTransformByGeometry(image->GetGeometry());
64  extractSliceFilter->SetWorldGeometry(planeGeometry);
65  extractSliceFilter->SetOutputDimensionality(2);
66  extractSliceFilter->SetVtkOutputRequest(true);
67  extractSliceFilter->Update();
68 
69  vtkImageData* imageData = extractSliceFilter->GetVtkOutput();
70 
71  mitk::LevelWindow levelWindow;
72  dataNode->GetLevelWindow(levelWindow);
73  vtkSmartPointer<vtkLookupTable> lookupTable = vtkSmartPointer<vtkLookupTable>::New();
74  lookupTable->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound());
75  lookupTable->SetSaturationRange(0.0, 0.0);
76  lookupTable->SetValueRange(0.0, 1.0);
77  lookupTable->SetHueRange(0.0, 0.0);
78  lookupTable->SetRampToLinear();
79 
80  vtkSmartPointer<vtkMitkLevelWindowFilter> levelWindowFilter = vtkSmartPointer<vtkMitkLevelWindowFilter>::New();
81  levelWindowFilter->SetLookupTable(lookupTable);
82  levelWindowFilter->SetInputData(imageData);
83  levelWindowFilter->SetMinOpacity(0.0);
84  levelWindowFilter->SetMaxOpacity(1.0);
85  int dims[3];
86  imageData->GetDimensions(dims);
87  double clippingBounds[] = { 0.0, static_cast<double>(dims[0]), 0.0, static_cast<double>(dims[1]) };
88  levelWindowFilter->SetClippingBounds(clippingBounds);
89  levelWindowFilter->Update();
90  imageData = levelWindowFilter->GetOutput();
91 
92  QImage thumbnailImage(reinterpret_cast<const unsigned char*>(imageData->GetScalarPointer()), dims[0], dims[1], QImage::Format_ARGB32);
93 
94  if (dims[0] > dims[1])
95  {
96  thumbnailImage = thumbnailImage.scaledToWidth(height, Qt::SmoothTransformation).rgbSwapped();
97  }
98  else
99  {
100  thumbnailImage = thumbnailImage.scaledToHeight(height, Qt::SmoothTransformation).rgbSwapped();
101  }
102 
103  return QPixmap::fromImage(thumbnailImage);
104 }
105 
107  : QPushButton(parent), m_OutDatedThumbNail(true), m_DataMTime(0), m_IsOptional(true), m_NodeModifiedObserverTag(0), m_NodeObserved(false)
108 { }
109 
111 {
112  this->RemoveNodeObserver();
113  this->m_SelectedNode = nullptr;
114 }
115 
117 {
118  if (this->m_SelectedNode.IsNotNull())
119  {
120  if (m_NodeObserved)
121  {
122  MITK_DEBUG << "Invalid observer state in QmitkNodeSelectionButton. There is already a registered observer. Internal logic is not correct. May be an old observer was not removed.";
123  }
124 
125  auto modifiedCommand = itk::MemberCommand<QmitkNodeSelectionButton>::New();
126  modifiedCommand->SetCallbackFunction(this, &QmitkNodeSelectionButton::OnNodeModified);
127 
128  // const cast because we need non const nodes and it seems to be the lesser of two evil.
129  // the changes to the node are only on the observer level. The other option would be to
130  // make the public interface require non const nodes, this we don't want to introduce.
131  auto nonconst_node = const_cast<mitk::DataNode*>(this->m_SelectedNode.GetPointer());
132  m_NodeModifiedObserverTag = nonconst_node->AddObserver(itk::ModifiedEvent(), modifiedCommand);
133  m_NodeObserved = true;
134  }
135 }
136 
138 {
139  if (this->m_SelectedNode.IsNotNull())
140  {
141  // const cast because we need non const nodes and it seems to be the lesser of two evil.
142  // the changes to the node are only on the observer level. The other option would be to
143  // make the public interface require non const nodes, this we don't want to introduce.
144  auto nonconst_node = const_cast<mitk::DataNode*>(this->m_SelectedNode.GetPointer());
145  nonconst_node->RemoveObserver(m_NodeModifiedObserverTag);
146  }
147  m_NodeObserved = false;
148 }
149 
150 void QmitkNodeSelectionButton::OnNodeModified(const itk::Object * /*caller*/, const itk::EventObject & event)
151 {
152  if (itk::ModifiedEvent().CheckEvent(&event))
153  {
154  this->update();
155  }
156 }
157 
159 {
160  return m_SelectedNode;
161 }
162 
164 {
165  if (m_SelectedNode != node)
166  {
167  this->RemoveNodeObserver();
168  this->m_SelectedNode = node;
169  this->m_OutDatedThumbNail = true;
170  this->AddNodeObserver();
171  }
172 
173  this->update();
174 }
175 
177 {
178  this->m_Info = info;
179  this->update();
180 }
181 
183 {
184  QString stylesheet;
185 
186  ctkPluginContext* context = berry::WorkbenchPlugin::GetDefault()->GetPluginContext();
187  ctkServiceReference styleManagerRef = context->getServiceReference<berry::IQtStyleManager>();
188  if (styleManagerRef)
189  {
190  auto styleManager = context->getService<berry::IQtStyleManager>(styleManagerRef);
191  stylesheet = styleManager->GetStylesheet();
192  }
193 
194  QPushButton::paintEvent(p);
195 
196  QPainter painter(this);
197  QTextDocument td(this);
198  td.setDefaultStyleSheet(stylesheet);
199 
200  auto widgetSize = this->size();
201  QPoint origin = QPoint(5, 5);
202 
203  if (this->m_SelectedNode)
204  {
205  auto iconLength = widgetSize.height() - 10;
206  auto node = this->m_SelectedNode;
207 
208  itk::ModifiedTimeType dataMTime = 0;
209  if (m_SelectedNode->GetData())
210  {
211  dataMTime = m_SelectedNode->GetData()->GetMTime();
212  }
213  if (dataMTime>m_DataMTime || this->m_OutDatedThumbNail)
214  {
215  this->m_ThumbNail = GetPixmapFromImageNode(node, iconLength);
216  this->m_OutDatedThumbNail = false;
217  m_DataMTime = dataMTime;
218  }
219 
220  auto thumbNailOrigin = origin;
221  thumbNailOrigin.setY(thumbNailOrigin.y() + ((iconLength - m_ThumbNail.height()) / 2));
222  painter.drawPixmap(thumbNailOrigin, m_ThumbNail);
223  origin.setX(origin.x() + iconLength + 5);
224 
225  if (this->isEnabled())
226  {
227  td.setHtml(QString::fromStdString("<font class=\"normal\">" + node->GetName() + "</font>"));
228  }
229  else
230  {
231  td.setHtml(QString::fromStdString("<font class=\"disabled\">" + node->GetName() + "</font>"));
232  }
233  }
234  else
235  {
236  if (this->isEnabled())
237  {
238  if (this->m_IsOptional)
239  {
240  td.setHtml(QString("<font class=\"normal\">") + m_Info + QString("</font>"));
241  }
242  else
243  {
244  td.setHtml(QString("<font class=\"warning\">") + m_Info + QString("</font>"));
245  }
246  }
247  else
248  {
249  td.setHtml(QString("<font class=\"disabled\">") + m_Info + QString("</font>"));
250  }
251  }
252 
253  auto textSize = td.size();
254 
255  origin.setY( (widgetSize.height() - textSize.height()) / 2.);
256 
257  painter.translate(origin);
258  td.drawContents(&painter);
259 }
260 
262 {
263  if (event->type() == QEvent::EnabledChange)
264  {
265  this->update();
266  }
267 }
268 
270 {
271  return m_IsOptional;
272 }
273 
275 {
276  m_IsOptional = isOptional;
277  this->update();
278 }
void OnNodeModified(const itk::Object *, const itk::EventObject &)
ctkPluginContext * GetPluginContext()
QmitkNodeSelectionButton(QWidget *parent=nullptr)
const mitk::PixelType GetPixelType(int n=0) const
Returns the PixelType of channel n.
Definition: mitkImage.cpp:101
vcl_size_t GetNumberOfComponents() const
Get the number of components of which each element consists.
const mitk::DataNode * GetSelectedNode() const
#define MITK_DEBUG
Definition: mitkLogMacros.h:22
static Pointer New()
static WorkbenchPlugin * GetDefault()
itk::ModifiedTimeType m_DataMTime
static void info(const char *fmt,...)
Definition: svm.cpp:86
void paintEvent(QPaintEvent *p) override
virtual void SetSelectedNode(const mitk::DataNode *node)
The LevelWindow class Class to store level/window values.
BaseData * GetData() const
Get the data object (instance of BaseData, e.g., an Image) managed by this DataNode.
unsigned int GetDimension() const
Get dimension of the image.
Definition: mitkImage.cpp:106
ScalarType GetUpperWindowBound() const
Image class for storing images.
Definition: mitkImage.h:72
void SetSelectionIsOptional(bool isOptional)
virtual void SetNodeInfo(QString info)
mitk::DataNode::ConstPointer m_SelectedNode
mitk::Image::Pointer image
virtual QString GetStylesheet() const =0
void changeEvent(QEvent *event) override
virtual bool IsInitialized() const
Check whether the data has been initialized, i.e., at least the Geometry and other header data has be...
static Pointer New()
mitk::BaseGeometry * GetGeometry(int t=0) const
Return the geometry, which is a TimeGeometry, of the data as non-const pointer.
Definition: mitkBaseData.h:138
bool GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer=nullptr, const char *propertyKey="levelwindow") const
Convenience access method for level-window properties (instances of LevelWindowProperty) ...
QPixmap GetPixmapFromImageNode(const mitk::DataNode *dataNode, int height)
ScalarType GetLowerWindowBound() const
static QmitkNodeDescriptorManager * GetInstance()
Class for nodes of the DataTree.
Definition: mitkDataNode.h:57