PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
QPlusStatusIcon.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2  Program: Plus
3  Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4  See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 // Local includes
8 #include "QPlusStatusIcon.h"
9 
10 // Qt includes
11 #include <QCoreApplication>
12 #include <QGridLayout>
13 #include <QLabel>
14 #include <QMenu>
15 #include <QMouseEvent>
16 #include <QScrollBar>
17 #include <QSizePolicy>
18 #include <QVBoxLayout>
19 
20 //-----------------------------------------------------------------------------
21 
22 namespace
23 {
24  const QString ERROR_HTML = "<font color=\"#DF0000\">";
25  const QString WARNING_HTML = "<font color=\"#FF8000\">";
26  const QString INFO_HTML = "<font color=\"Black\">";
27  const QString END_HTML = "</font>";
28 }
29 
30 //-----------------------------------------------------------------------------
31 QPlusStatusIcon::QPlusStatusIcon(QWidget* aParent, Qt::WindowFlags aFlags)
32  : QWidget(aParent, aFlags)
33  , m_Level(vtkPlusLogger::LOG_LEVEL_INFO)
34  , m_DotLabel(NULL)
35  , m_MessageListFrame(NULL)
36  , m_MessageTextEdit(NULL)
37  , m_DisplayMessageCallbackTag(0)
38  , m_MaxMessageCount(vtkPlusLogger::UnlimitedLogMessages())
39 {
40  this->setMinimumSize(18, 18);
41  this->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
42 
43  // Set up layout and create dot label
44  QGridLayout* grid = new QGridLayout();
45  grid->setSpacing(0);
46  grid->setContentsMargins(0, 0, 0, 0);
47 
48  m_DotLabel = new QLabel(this);
49  grid->addWidget(m_DotLabel);
50  this->setLayout(grid);
51 
52  auto pix = QPixmap(":/icons/Resources/icon_DotGreen.png");
53  m_DotLabel->setPixmap(pix.scaled(m_DotLabel->width() - 1, m_DotLabel->height() - 1, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
54 
55  if (m_DotLabel == NULL)
56  {
57  LOG_ERROR("Status icon cannot be initialized!");
58  return;
59  }
60 
61  // Set callback for logger to display errors
62  vtkSmartPointer<vtkDisplayMessageCallback> cb = vtkSmartPointer<vtkDisplayMessageCallback>::New();
63  m_DisplayMessageCallbackTag = vtkPlusLogger::Instance()->AddObserver(vtkPlusLogger::MessageLogged, cb);
64  m_DisplayWideMessageCallbackTag = vtkPlusLogger::Instance()->AddObserver(vtkPlusLogger::WideMessageLogged, cb);
65 
66  connect(cb, SIGNAL(AddMessage(QString)), this, SLOT(AddMessage(QString)));
67 
68  // Install event filter that is called on any event
69  this->installEventFilter(this);
70 
72  {
73  LOG_ERROR("Message list widget cannot be initialized!");
74  return;
75  }
76 }
77 
78 //-----------------------------------------------------------------------------
80 {
83 }
84 
85 //-----------------------------------------------------------------------------
86 void QPlusStatusIcon::AddMessage(QString aInputString)
87 {
88  // No matter what store the log entry to be able to reconstruct it later
89  m_MessageLog.push_back(aInputString);
90 
91  if (m_MaxMessageCount != vtkPlusLogger::UnlimitedLogMessages() && m_MessageTextEdit->document()->lineCount() > m_MaxMessageCount)
92  {
93  // Clear earliest messages
94  int linesToDelete(m_MessageTextEdit->document()->lineCount() - m_MaxMessageCount * 0.80);
95  m_MessageLog.erase(m_MessageLog.begin(), m_MessageLog.begin() + linesToDelete);
96  }
97 
98  if (m_FilterLineEdit->text().isEmpty() || aInputString.contains(m_FilterLineEdit->text(), Qt::CaseInsensitive))
99  {
100  this->ParseMessage(aInputString);
101  }
102 }
103 
104 //-----------------------------------------------------------------------------
105 void QPlusStatusIcon::ParseMessage(QString& aInputString)
106 {
107  // Parse input string and extract log level and the message
108  bool ok;
109  unsigned int pos = aInputString.indexOf('|');
110 
111  int logLevel = aInputString.left(pos).toInt(&ok);
112  if (! ok)
113  {
114  logLevel = -1;
115  }
116 
117  QString message;
118 
119  // Re-color dot and message text if necessary
120  switch (logLevel)
121  {
122  case vtkPlusLogger::LOG_LEVEL_ERROR:
123  if (m_Level > vtkPlusLogger::LOG_LEVEL_ERROR)
124  {
125  m_Level = vtkPlusLogger::LOG_LEVEL_ERROR;
126  m_DotLabel->setPixmap(QPixmap(":/icons/Resources/icon_DotRed.png").scaled(m_DotLabel->width() - 1, m_DotLabel->height() - 1, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
127  }
128  message = ERROR_HTML;
129  break;
130  case vtkPlusLogger::LOG_LEVEL_WARNING:
131  if (m_Level > vtkPlusLogger::LOG_LEVEL_WARNING)
132  {
133  m_Level = vtkPlusLogger::LOG_LEVEL_WARNING;
134  m_DotLabel->setPixmap(QPixmap(":/icons/Resources/icon_DotOrange.png").scaled(m_DotLabel->width() - 1, m_DotLabel->height() - 1, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
135  }
136  message = WARNING_HTML;
137  break;
138  default:
139  message = INFO_HTML;
140  break;
141  }
142 
143  message = message.append(aInputString.right(aInputString.size() - pos - 1)).append(END_HTML);
144 
145  if (m_MaxMessageCount != vtkPlusLogger::UnlimitedLogMessages() && m_MessageTextEdit->document()->lineCount() > m_MaxMessageCount)
146  {
147  QTextCursor tc = m_MessageTextEdit->textCursor();
148 
149  // Store original selection
150  int originalSelectionStart = tc.selectionStart();
151  int originalSelectionLength = tc.selectionEnd() - originalSelectionStart;
152 
153  // Keep only the most recent 80% of the lines, delete the rest
154  int linesToDelete = m_MessageTextEdit->document()->lineCount() - m_MaxMessageCount * 0.80;
155  tc.movePosition(QTextCursor::Start);
156  for (int i = 0; i < linesToDelete; i++)
157  {
158  tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
159  tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
160  }
161  originalSelectionStart -= (tc.selectionEnd() - tc.selectionStart());
162  tc.removeSelectedText();
163 
164  // Restore original selection
165  if (originalSelectionStart > 0 && originalSelectionLength > 0)
166  {
167  // if there was a selection, then restore it
168  tc.movePosition(QTextCursor::Start);
169  tc.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, originalSelectionStart);
170  m_MessageTextEdit->setTextCursor(tc);
171  m_MessageTextEdit->ensureCursorVisible();
172  tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, originalSelectionLength);
173  m_MessageTextEdit->setTextCursor(tc);
174  m_MessageTextEdit->ensureCursorVisible();
175  }
176  else
177  {
178  // there was no selection, so just follow the tail
179  m_MessageTextEdit->moveCursor(QTextCursor::End);
180  m_MessageTextEdit->setTextCursor(tc);
181  QCoreApplication::processEvents(); // need to call this to update the max position of the slider
182  QScrollBar* vScrollBar = m_MessageTextEdit->verticalScrollBar();
183  if (vScrollBar)
184  {
185  vScrollBar->triggerAction(QScrollBar::SliderToMaximum);
186  }
187  }
188  }
189 
190  m_MessageTextEdit->append(message);
191 }
192 
193 //-----------------------------------------------------------------------------
195 {
196  LOG_TRACE("ToolStateDisplayWidget::ConstructMessageListWidget");
197 
198  // (Re-)Create message list widget
199  if (m_MessageListFrame != NULL)
200  {
201  delete m_MessageListFrame;
202  m_MessageListFrame = NULL;
203  m_MessageTextEdit = NULL;
204  m_FilterLineEdit = NULL;
205  m_ClearFilterButton = NULL;
206  }
207 
208  m_MessageListFrame = new QFrame(this, Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
209  m_MessageListFrame->setMinimumSize(480, 480);
210  m_MessageListFrame->resize(480, 480);
211 
212  if (m_MessageListFrame == NULL)
213  {
214  LOG_ERROR("Message list widget cannot be created!");
215  return PLUS_FAIL;
216  }
217 
218  // Install event filter to properly handle close
219  m_MessageListFrame->installEventFilter(this);
220 
221  // Set inner part of message list widget
222  QVBoxLayout* vbox = new QVBoxLayout(m_MessageListFrame);
223  vbox->setSpacing(0);
224  vbox->setContentsMargins(0, 0, 0, 0);
225  vbox->setAlignment(Qt::AlignTop);
226 
227  m_MessageTextEdit = new QTextEdit(m_MessageListFrame);
228  m_MessageTextEdit->setWordWrapMode(QTextOption::NoWrap);
229  m_MessageTextEdit->setReadOnly(true);
230  m_MessageTextEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
231  m_MessageTextEdit->setContextMenuPolicy(Qt::CustomContextMenu);
232  connect(m_MessageTextEdit, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(CreateCustomContextMenu(const QPoint&)));
233  vbox->addWidget(m_MessageTextEdit);
234 
235  // Add filter controls
236  QFrame* filterFrame = new QFrame();
237  filterFrame->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
238  QHBoxLayout* filterLayout = new QHBoxLayout(filterFrame);
239  filterLayout->setDirection(QBoxLayout::RightToLeft);
240  filterLayout->setContentsMargins(0, 0, 0, 0);
241  filterLayout->setSpacing(0);
242  m_ClearFilterButton = new QPushButton();
243  m_ClearFilterButton->setIcon(QIcon(":/icons/Resources/icon_ClearText.png"));
244  m_ClearFilterButton->setToolTip(tr("Clear filter"));
245  m_ClearFilterButton->setEnabled(false);
246  filterLayout->addWidget(m_ClearFilterButton);
247  connect(m_ClearFilterButton, SIGNAL(clicked()), this, SLOT(ClearFilterButtonClicked()));
248  m_FilterLineEdit = new QLineEdit();
249  m_FilterLineEdit->setText("");
250  m_FilterLineEdit->setMinimumWidth(150);
251  m_FilterLineEdit->setAlignment(Qt::AlignRight);
252  connect(m_FilterLineEdit, SIGNAL(textEdited(const QString&)), this, SLOT(FilterLineEditEdited(const QString&)));
253  filterLayout->addWidget(m_FilterLineEdit);
254  filterLayout->addWidget(new QLabel("Message log filter: "));
255  filterLayout->addStretch(1);
256  vbox->addWidget(filterFrame);
257 
258  QTextCursor tc = m_MessageTextEdit->textCursor();
259  tc.movePosition(QTextCursor::End);
260  m_MessageTextEdit->setTextCursor(tc);
261 
262  connect(&m_FilterInputTimer, SIGNAL(timeout()), this, SLOT(ApplyFilterTimerFired()));
263 
264  return PLUS_SUCCESS;
265 }
266 
267 //-----------------------------------------------------------------------------
268 bool QPlusStatusIcon::eventFilter(QObject* obj, QEvent* ev)
269 {
270  if (ev->type() == QEvent::MouseButtonPress)
271  {
272  QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(ev);
273  if (mouseEvent != NULL)
274  {
275  if (mouseEvent->buttons() == Qt::LeftButton)
276  {
277  if ((m_MessageListFrame == NULL) || (! m_MessageListFrame->isVisible()))
278  {
279  m_Level = vtkPlusLogger::LOG_LEVEL_INFO;
280  m_DotLabel->setPixmap(QPixmap(":/icons/Resources/icon_DotGreen.png").scaled(m_DotLabel->width() - 1, m_DotLabel->height() - 1, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
281 
282  QTextCursor cursor(m_MessageTextEdit->textCursor());
283  cursor.movePosition(QTextCursor::End);
284  cursor.movePosition(QTextCursor::StartOfLine);
285  m_MessageTextEdit->setTextCursor(cursor);
286 
287  m_MessageListFrame->move(mapToGlobal(QPoint(m_DotLabel->x() - m_MessageListFrame->width(), m_DotLabel->y() - m_MessageListFrame->height() - 40)));
288  m_MessageListFrame->show();
289 
290  }
291  else
292  {
293  ResetIconState();
294  m_MessageListFrame->hide();
295  }
296 
297  return true;
298  }
299  }
300  }
301  else if ((obj == m_MessageListFrame) && (ev->type() == QEvent::Close))
302  {
303  m_Level = vtkPlusLogger::LOG_LEVEL_INFO;
304  m_DotLabel->setPixmap(QPixmap(":/icons/Resources/icon_DotGreen.png").scaled(m_DotLabel->width() - 1, m_DotLabel->height() - 1, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
305  }
306 
307  // Pass the event on to the parent class
308  return QWidget::eventFilter(obj, ev);
309 }
310 
311 //----------------------------------------------------------------------------
312 void QPlusStatusIcon::resizeEvent(QResizeEvent* event)
313 {
314  const QPixmap* pix = m_DotLabel->pixmap();
315  m_DotLabel->setPixmap(pix->scaled(m_DotLabel->width() - 1, m_DotLabel->height() - 1, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
316 
317  QWidget::resizeEvent(event);
318 }
319 
320 //-----------------------------------------------------------------------------
322 {
323  QMenu* menu = m_MessageTextEdit->createStandardContextMenu();
324  QAction* clear = new QAction("Clear", this);
325  menu->addAction(clear);
326  connect(clear, SIGNAL(triggered()), this, SLOT(ClearMessageList()));
327  menu->exec(m_MessageTextEdit->mapToGlobal(aPoint));
328  delete menu;
329 }
330 
331 //-----------------------------------------------------------------------------
333 {
334  m_MessageTextEdit->clear();
335  ResetIconState();
336  m_MessageListFrame->hide();
337 }
338 
339 //-----------------------------------------------------------------------------
341 {
342  this->ApplyFilter();
343 }
344 
345 //-----------------------------------------------------------------------------
347 {
348  // clear log
349  m_FilterLineEdit->clear();
350  m_ClearFilterButton->setEnabled(false);
351  this->ApplyFilter();
352 }
353 
354 //-----------------------------------------------------------------------------
356 {
357  m_ClearFilterButton->setEnabled(!m_FilterLineEdit->text().isEmpty());
358 
359  if (m_FilterInputTimer.isActive())
360  {
361  m_FilterInputTimer.stop();
362  }
363 
364  m_FilterInputTimer.setSingleShot(true);
365  m_FilterInputTimer.start(500);
366 }
367 
368 //-----------------------------------------------------------------------------
370 {
371  // Clear the log
372  m_MessageTextEdit->clear();
373 
374  // iterate over entire log, check for filter, if so, add message
375  for (std::vector<QString>::iterator it = m_MessageLog.begin(); it != m_MessageLog.end(); ++it)
376  {
377  QString& entry(*it);
378  if (m_FilterLineEdit->text().isEmpty() || entry.contains(m_FilterLineEdit->text(), Qt::CaseInsensitive))
379  {
380  this->ParseMessage(entry);
381  }
382  }
383 }
384 
385 //-----------------------------------------------------------------------------
387 {
388  m_Level = vtkPlusLogger::LOG_LEVEL_INFO;
389  m_DotLabel->setPixmap(QPixmap(":/icons/Resources/icon_DotGreen.png").scaled(m_DotLabel->width() - 1, m_DotLabel->height() - 1, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
390 }
391 
392 //-----------------------------------------------------------------------------
394 {
395  if (count < 0)
396  {
397  count = vtkPlusLogger::UnlimitedLogMessages();
398  }
399  this->m_MaxMessageCount = count;
400 }
401 
402 //-----------------------------------------------------------------------------
403 void vtkDisplayMessageCallback::Execute(vtkObject* caller, unsigned long eventId, void* callData)
404 {
405  if (vtkPlusLogger::MessageLogged == eventId)
406  {
407  char* callDataChars = reinterpret_cast<char*>(callData);
408 
409  emit AddMessage(QString::fromLatin1(callDataChars));
410  }
411  else if (vtkPlusLogger::WideMessageLogged == eventId)
412  {
413  wchar_t* callDataChars = reinterpret_cast<wchar_t*>(callData);
414 
415  emit AddMessage(QString::fromWCharArray(callDataChars));
416  }
417 }
void ClearFilterButtonClicked()
void FilterLineEditEdited(const QString &)
void resizeEvent(QResizeEvent *event)
Class to abstract away specific sequence file read/write details.
Definition: vtkPlusLogger.h:21
void SetMaxMessageCount(int count)
QTextEdit * m_MessageTextEdit
igsioStatus PlusStatus
Definition: PlusCommon.h:40
QLineEdit * m_FilterLineEdit
QPushButton * m_ClearFilterButton
QPlusStatusIcon(QWidget *aParent=0, Qt::WindowFlags aFlags=0)
for i
#define PLUS_FAIL
Definition: PlusCommon.h:43
void ParseMessage(QString &aInputString)
unsigned long m_DisplayMessageCallbackTag
unsigned long m_DisplayWideMessageCallbackTag
bool eventFilter(QObject *obj, QEvent *ev)
void AddMessage(QString aInputString)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
void CreateCustomContextMenu(const QPoint &aPoint)
PlusStatus ConstructMessageListWidget()
Phidget_ChannelClass uint32_t * count
Definition: phidget22.h:1321
static vtkIGSIOLogger * Instance()
virtual void Execute(vtkObject *caller, unsigned long eventId, void *callData)
QFrame * m_MessageListFrame
const char * message
Definition: phidget22.h:2457
std::vector< QString > m_MessageLog