artdaq_mfextensions  v1_03_02
mvdlg.cc
1 #include <QtGui>
2 #include <QMenu>
3 #include <QMessageBox>
4 #include <QProgressDialog>
5 #include <QScrollBar>
6 
7 #include "cetlib/filepath_maker.h"
8 #include "fhiclcpp/make_ParameterSet.h"
9 #include "fhiclcpp/ParameterSet.h"
10 
11 #include "mfextensions/Binaries/mvdlg.hh"
12 
13 
14 #if GCC_VERSION >= 701000 || defined(__clang__)
15 #pragma GCC diagnostic push
16 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
17 #endif
18 
19 #include "trace.h"
20 
21 #if GCC_VERSION >= 701000 || defined(__clang__)
22 #pragma GCC diagnostic pop
23 #endif
24 
25 
26 #include "mvdlg.hh"
27 
28 // replace the ${..} part in the filename with env variable
29 // throw if the env does not exist
30 static void
31 process_fname(std::string& fname)
32 {
33  size_t sub_start = fname.find("${");
34  size_t sub_end = fname.find("}");
35 
36  const size_t npos = std::string::npos;
37 
38  if ((sub_start == npos && sub_end != npos)
39  || (sub_start != npos && sub_end == npos)
40  || (sub_start > sub_end))
41  {
42  throw std::runtime_error("Unrecognized configuration file. Use default configuration instead.");
43  }
44 
45  if (sub_start == npos) return;
46 
47  std::string env = std::string(getenv(fname.substr(sub_start + 2, sub_end - sub_start - 2).c_str()));
48  fname.replace(sub_start, sub_end - sub_start + 1, env);
49 
50  //printf("%s\n", fname.c_str());
51 }
52 
53 static fhicl::ParameterSet
54 readConf(std::string const& fname)
55 {
56  if (fname.empty()) return fhicl::ParameterSet();
57 
58  std::string filename = fname;
59  process_fname(filename);
60 
61  std::string env("FHICL_FILE_PATH=");
62 
63  if (filename[0] == '/')
64  {
65  env.append("/");
66  }
67  else
68  {
69  env.append(".");
70  }
71 
72  char* mfe_path = getenv("MFEXTENSIONS_DIR");
73  if (mfe_path) env.append(":").append(mfe_path).append("/config");
74 
75  putenv((char *)env.c_str());
76 
77  //printf("%s\n", env.c_str());
78 
79  cet::filepath_lookup policy("FHICL_FILE_PATH");
80 
81  // it throws when the file is not parsable
82  fhicl::ParameterSet pset;
83  fhicl::make_ParameterSet(filename, policy, pset);
84 
85  return pset;
86 }
87 
88 msgViewerDlg::msgViewerDlg(std::string const& conf, QDialog* parent)
89  : QDialog(parent)
90  , updating(false)
91  , paused(false)
92  , shortMode_(false)
93  , nMsgs(0)
94  , nSupMsgs(0)
95  , nThrMsgs(0)
96  , nFilters(0)
97  , simpleRender(true)
98  , sevThresh(SINFO)
99  , searchStr("")
100  , msg_pool_()
101  , host_msgs_()
102  , cat_msgs_()
103  , app_msgs_()
104  , sup_menu(new QMenu(this))
105  , thr_menu(new QMenu(this))
106  , receivers_(readConf(conf).get<fhicl::ParameterSet>("receivers", fhicl::ParameterSet()))
107 {
108  setupUi(this);
109 
110  // window geo settings
111  readSettings();
112 
113  // read configuration file
114  fhicl::ParameterSet pset = readConf(conf);
115 
116  // parse configuration file
117  parseConf(pset);
118 
119  // associate menu with push buttons
120  btnSuppression->setMenu(sup_menu);
121  btnThrottling->setMenu(thr_menu);
122 
123  // slots
124  connect(btnPause, SIGNAL(clicked()), this, SLOT(pause()));
125  connect(btnExit, SIGNAL(clicked()), this, SLOT(exit()));
126  connect(btnClear, SIGNAL(clicked()), this, SLOT(clear()));
127 
128  connect(btnRMode, SIGNAL(clicked()), this, SLOT(renderMode()));
129  connect(btnDisplayMode, SIGNAL(clicked()), this, SLOT(shortMode()));
130 
131  connect(btnSearch, SIGNAL(clicked()), this, SLOT(searchMsg()));
132  connect(btnSearchClear,
133  SIGNAL(clicked()), this, SLOT(searchClear()));
134 
135  connect(btnFilter, SIGNAL(clicked()), this, SLOT(setFilter()));
136 
137  connect(btnError, SIGNAL(clicked()), this, SLOT(setSevError()));
138  connect(btnWarning, SIGNAL(clicked()), this, SLOT(setSevWarning()));
139  connect(btnInfo, SIGNAL(clicked()), this, SLOT(setSevInfo()));
140  connect(btnDebug, SIGNAL(clicked()), this, SLOT(setSevDebug()));
141 
142  connect(sup_menu
143  , SIGNAL(triggered(QAction*))
144  , this
145  , SLOT(setSuppression(QAction*)));
146 
147  connect(thr_menu
148  , SIGNAL(triggered(QAction*))
149  , this
150  , SLOT(setThrottling(QAction*)));
151 
152  connect(vsSeverity
153  , SIGNAL(valueChanged(int))
154  , this
155  , SLOT(changeSeverity(int)));
156 
157  connect(&receivers_
158  , SIGNAL(newMessage(qt_mf_msg const &))
159  , this
160  , SLOT(onNewMsg(qt_mf_msg const &)));
161 
162  connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabWidgetCurrentChanged(int)));
163  connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int)));
164  MsgFilterDisplay allMessages;
165  allMessages.txtDisplay = txtMessages;
166  msgFilters_.push_back(allMessages);
167 
168  //https://stackoverflow.com/questions/2616483/close-button-only-for-some-tabs-in-qt
169  QTabBar *tabBar = tabWidget->findChild<QTabBar *>();
170  tabBar->setTabButton(0, QTabBar::RightSide, 0);
171  tabBar->setTabButton(0, QTabBar::LeftSide, 0);
172 
173  if (simpleRender) btnRMode->setChecked(true);
174  else btnRMode->setChecked(false);
175 
176  btnRMode->setEnabled(false);
177 
178  changeSeverity(sevThresh);
179 
180  QTextDocument* doc = new QTextDocument(txtMessages);
181  txtMessages->setDocument(doc);
182 
183  receivers_.start();
184 }
185 
186 msgViewerDlg::~msgViewerDlg()
187 {
188  receivers_.stop();
189  writeSettings();
190 }
191 
192 static void str_to_suppress(std::vector<std::string> const& vs, std::vector<suppress>& s, QMenu* menu)
193 {
194  QAction* act;
195 
196  if (vs.empty())
197  {
198  act = menu->addAction("None");
199  act->setEnabled(false);
200  return;
201  }
202 
203  s.reserve(vs.size());
204 
205  for (size_t i = 0; i < vs.size(); ++i)
206  {
207  s.push_back(suppress(vs[i]));
208  act = menu->addAction(QString(vs[i].c_str()));
209  act->setCheckable(true);
210  act->setChecked(true);
211  QVariant v = qVariantFromValue((void*)&s[i]);
212  act->setData(v);
213  }
214 }
215 
216 static void pset_to_throttle(std::vector<fhicl::ParameterSet> const& ps, std::vector<throttle>& t, QMenu* menu)
217 {
218  QAction* act;
219 
220  if (ps.empty())
221  {
222  act = menu->addAction("None");
223  act->setEnabled(false);
224  return;
225  }
226 
227  t.reserve(ps.size());
228 
229  for (size_t i = 0; i < ps.size(); ++i)
230  {
231  std::string name = ps[i].get<std::string>("name");
232  t.push_back(throttle(name
233  , ps[i].get<int>("limit", -1)
234  , ps[i].get<long>("timespan", -1)));
235  act = menu->addAction(QString(name.c_str()));
236  act->setCheckable(true);
237  act->setChecked(true);
238  QVariant v = qVariantFromValue((void*)&t[i]);
239  act->setData(v);
240  }
241 }
242 
243 void msgViewerDlg::parseConf(fhicl::ParameterSet const& conf)
244 {
245  fhicl::ParameterSet nulp;
246  //QAction * act;
247 
248  // suppression list
249  fhicl::ParameterSet sup = conf.get<fhicl::ParameterSet>("suppress", nulp);
250 
251  auto sup_host = sup.get<std::vector<std::string>>("hosts", std::vector<std::string>());
252  auto sup_app = sup.get<std::vector<std::string>>("applications", std::vector<std::string>());
253  auto sup_cat = sup.get<std::vector<std::string>>("categories", std::vector<std::string>());
254 
255  str_to_suppress(sup_host, e_sup_host, sup_menu);
256  sup_menu->addSeparator();
257 
258  str_to_suppress(sup_app, e_sup_app, sup_menu);
259  sup_menu->addSeparator();
260 
261  str_to_suppress(sup_cat, e_sup_cat, sup_menu);
262 
263  // throttling list
264  auto thr = conf.get<fhicl::ParameterSet>("throttle", nulp);
265 
266  auto thr_host = thr.get<std::vector<fhicl::ParameterSet>>("hosts", std::vector<fhicl::ParameterSet>());
267  auto thr_app = thr.get<std::vector<fhicl::ParameterSet>>("applications", std::vector<fhicl::ParameterSet>());
268  auto thr_cat = thr.get<std::vector<fhicl::ParameterSet>>("categories", std::vector<fhicl::ParameterSet>());
269 
270  pset_to_throttle(thr_host, e_thr_host, thr_menu);
271  thr_menu->addSeparator();
272 
273  pset_to_throttle(thr_app, e_thr_app, thr_menu);
274  thr_menu->addSeparator();
275 
276  pset_to_throttle(thr_cat, e_thr_cat, thr_menu);
277 
278  auto lvl = conf.get<std::string>("threshold", "INFO");
279  if (lvl == "DEBUG" || lvl == "debug" || lvl == "0") { sevThresh = SDEBUG; }
280  if (lvl == "INFO" || lvl == "info" || lvl == "1") { sevThresh = SINFO; }
281  if (lvl == "WARN" || lvl == "warn" || lvl == "2") { sevThresh = SWARNING; }
282  if (lvl == "ERROR" || lvl == "error" || lvl == "3") { sevThresh = SERROR; }
283 }
284 
285 bool msgViewerDlg::msg_throttled(qt_mf_msg const& mfmsg)
286 {
287  // suppression list
288 
289  ++nSupMsgs;
290 
291  for (size_t i = 0; i < e_sup_host.size(); ++i)
292  if (e_sup_host[i].match(mfmsg.host().toStdString()))
293  return true;
294 
295  for (size_t i = 0; i < e_sup_app.size(); ++i)
296  if (e_sup_app[i].match(mfmsg.app().toStdString()))
297  return true;
298 
299  for (size_t i = 0; i < e_sup_cat.size(); ++i)
300  if (e_sup_cat[i].match(mfmsg.cat().toStdString()))
301  return true;
302 
303  --nSupMsgs;
304 
305  // throttling
306 
307  ++nThrMsgs;
308 
309  for (size_t i = 0; i < e_thr_host.size(); ++i)
310  if (e_thr_host[i].reach_limit(mfmsg.host().toStdString(), mfmsg.time()))
311  return true;
312 
313  for (size_t i = 0; i < e_thr_app.size(); ++i)
314  if (e_thr_app[i].reach_limit(mfmsg.app().toStdString(), mfmsg.time()))
315  return true;
316 
317  for (size_t i = 0; i < e_thr_cat.size(); ++i)
318  if (e_thr_cat[i].reach_limit(mfmsg.cat().toStdString(), mfmsg.time()))
319  return true;
320 
321  --nThrMsgs;
322 
323  return false;
324 }
325 
326 void msgViewerDlg::writeSettings()
327 {
328  QSettings settings("ARTDAQ", "MsgViewer");
329 
330  settings.beginGroup("MainWindow");
331  settings.setValue("size", size());
332  settings.setValue("pos", pos());
333  settings.endGroup();
334 }
335 
336 void msgViewerDlg::readSettings()
337 {
338  QSettings settings("ARTDAQ", "MsgViewer");
339 
340  settings.beginGroup("MainWindow");
341  QPoint pos = settings.value("pos", QPoint(100, 100)).toPoint();
342  QSize size = settings.value("size", QSize(660, 760)).toSize();
343  resize(size);
344  move(pos);
345  settings.endGroup();
346 }
347 
348 void msgViewerDlg::onNewMsg(qt_mf_msg const& mfmsg)
349 {
350  // 21-Aug-2015, KAB: copying the incrementing (and displaying) of the number
351  // of messages to here. I'm also not sure if we want to
352  // count all messages or just non-suppressed ones or what. But, at least this
353  // change gets the counter incrementing on the display.
354  ++nMsgs;
355  lcdMsgs->display(nMsgs);
356 
357  // test if the message is suppressed or throttled
358  if (msg_throttled(mfmsg))
359  {
360  lcdSuppressionCount->display(nSupMsgs);
361  lcdThrottlingCount->display(nThrMsgs);
362  return;
363  }
364 
365  // push the message to the message pool
366  msg_pool_.emplace_back(mfmsg);
367  msgs_t::iterator it = --msg_pool_.end();
368 
369  // update corresponding lists of index
370  unsigned int flag = update_index(it);
371 
372  // update gui list
373  if (flag & LIST_APP) updateList<msg_iters_map_t>(lwApplication, app_msgs_);
374  if (flag & LIST_CAT) updateList<msg_iters_map_t>(lwCategory, cat_msgs_);
375  if (flag & LIST_HOST) updateList<msg_iters_map_t>(lwHost, host_msgs_);
376 
377  for (size_t d = 0; d < msgFilters_.size(); ++d)
378  {
379  bool hostMatch = msgFilters_[d].hostFilter.contains(it->host(), Qt::CaseInsensitive) || msgFilters_[d].hostFilter.size() == 0;
380  bool appMatch = msgFilters_[d].appFilter.contains(it->app(), Qt::CaseInsensitive) || msgFilters_[d].appFilter.size() == 0;
381  bool catMatch = msgFilters_[d].catFilter.contains(it->cat(), Qt::CaseInsensitive) || msgFilters_[d].catFilter.size() == 0;
382 
383  // Check to display the message
384  if (hostMatch && appMatch && catMatch)
385  {
386  msgFilters_[d].msgs.push_back(it);
387  displayMsg(it, d);
388  }
389  }
390 }
391 
392 unsigned int msgViewerDlg::update_index(msgs_t::iterator it)
393 {
394  QString const& app = it->app();
395  QString const& cat = it->cat();
396  QString const& host = it->host();
397 
398  unsigned int update = 0x0;
399 
400  if (cat_msgs_.find(cat) == cat_msgs_.end()) update |= LIST_CAT;
401  if (host_msgs_.find(host) == host_msgs_.end()) update |= LIST_HOST;
402  if (app_msgs_.find(app) == app_msgs_.end()) update |= LIST_APP;
403 
404  cat_msgs_[cat].push_back(it);
405  host_msgs_[host].push_back(it);
406  app_msgs_[app].push_back(it);
407 
408  return update;
409 }
410 
411 
412 void msgViewerDlg::displayMsg(msgs_t::const_iterator it, int display)
413 {
414  if (it->sev() < sevThresh) return;
415 
416  msgFilters_[display].nDisplayMsgs++;
417  if (display == tabWidget->currentIndex())
418  {
419  lcdDisplayedMsgs->display(msgFilters_[display].nDisplayMsgs);
420  }
421 
422  auto txt = it->text(shortMode_);
423  UpdateTextAreaDisplay(txt, msgFilters_[display].txtDisplay);
424 }
425 
426 void msgViewerDlg::displayMsg(int display)
427 {
428  int n = 0;
429  msgFilters_[display].txtDisplay->clear();
430  msgFilters_[display].nDisplayMsgs = 0;
431 
432  msg_iters_t::const_iterator it;
433 
434  n = msgFilters_[display].msgs.size();
435  it = msgFilters_[display].msgs.begin();
436  QProgressDialog progress("Fetching data...", "Cancel"
437  , 0, n / 1000, this);
438 
439  progress.setWindowModality(Qt::WindowModal);
440  progress.setMinimumDuration(2000); // 2 seconds
441 
442  QString txt;
443  int i = 0, prog = 0;
444 
445  updating = true;
446 
447  for (; it != msgFilters_[display].msgs.end(); ++it, ++i)
448  {
449  if (it->get()->sev() >= sevThresh)
450  {
451  txt += it->get()->text(shortMode_);
452  ++msgFilters_[display].nDisplayMsgs;
453  }
454 
455  if (i == 1000)
456  {
457  i = 0;
458  ++prog;
459  progress.setValue(prog);
460 
461  UpdateTextAreaDisplay(txt, msgFilters_[display].txtDisplay);
462  txt.clear();
463  }
464 
465  if (progress.wasCanceled())
466  break;
467  }
468 
469  if (display == tabWidget->currentIndex())
470  {
471  lcdDisplayedMsgs->display(msgFilters_[display].nDisplayMsgs);
472  }
473 
474  UpdateTextAreaDisplay(txt, msgFilters_[display].txtDisplay);
475 
476  updating = false;
477 }
478 
479 //https://stackoverflow.com/questions/21955923/prevent-a-qtextedit-widget-from-scrolling-when-there-is-a-selection
480 void msgViewerDlg::UpdateTextAreaDisplay(QString text, QTextEdit* widget)
481 {
482  const QTextCursor old_cursor = widget->textCursor();
483  const int old_scrollbar_value = widget->verticalScrollBar()->value();
484  const bool is_scrolled_down = old_scrollbar_value >= widget->verticalScrollBar()->maximum() * 0.95; // At least 95% scrolled down
485 
486  // Insert the text at the position of the cursor (which is the end of the document).
487  widget->append(text);
488 
489  if (old_cursor.hasSelection() || !is_scrolled_down)
490  {
491  // The user has selected text or scrolled away from the bottom: maintain position.
492  widget->setTextCursor(old_cursor);
493  widget->verticalScrollBar()->setValue(old_scrollbar_value);
494  }
495  else
496  {
497  // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom.
498  widget->moveCursor(QTextCursor::End);
499  widget->verticalScrollBar()->setValue(widget->verticalScrollBar()->maximum());
500  widget->horizontalScrollBar()->setValue(0);
501  }
502 }
503 
504 void msgViewerDlg::updateDisplays()
505 {
506  for (size_t ii = 0; ii < msgFilters_.size(); ++ii)
507  {
508  displayMsg(ii);
509  }
510 }
511 
512 template <typename M>
513 bool msgViewerDlg::updateList(QListWidget* lw, M const& map)
514 {
515  bool nonSelectedBefore = (lw->currentRow() == -1);
516  bool nonSelectedAfter = true;
517 
518  QString item = nonSelectedBefore ? "" : lw->currentItem()->text();
519 
520  lw->clear();
521  int row = 0;
522  typename M::const_iterator it = map.begin();
523 
524  while (it != map.end())
525  {
526  lw->addItem(it->first);
527  if (!nonSelectedBefore && nonSelectedAfter)
528  {
529  if (item == it->first)
530  {
531  lw->setCurrentRow(row);
532  nonSelectedAfter = false;
533  }
534  }
535  ++row;
536  ++it;
537  }
538 
539  if (!nonSelectedBefore && nonSelectedAfter) return true;
540 
541  return false;
542 }
543 
544 msg_iters_t msgViewerDlg::list_intersect(msg_iters_t const& l1, msg_iters_t const& l2)
545 {
546  msg_iters_t output;
547  msg_iters_t::const_iterator it1 = l1.begin();
548  msg_iters_t::const_iterator it2 = l2.begin();
549 
550  while (it1 != l1.end() && it2 != l2.end())
551  {
552  if (*it1 < *it2) { ++it1; }
553  else if (*it2 < *it1) { ++it2; }
554  else
555  {
556  output.push_back(*it1);
557  ++it1;
558  ++it2;
559  }
560  }
561 
562  TLOG(10) << "list_intersect: output list has " << output.size() << " entries";
563  return output;
564 }
565 
566 std::string sev_to_string(sev_code_t s)
567 {
568  switch (s)
569  {
570  case SDEBUG:
571  return "DEBUG";
572  case SINFO:
573  return "INFO";
574  case SWARNING:
575  return "WARNING";
576  case SERROR:
577  return "ERROR";
578  }
579  return "UNKNOWN";
580 }
581 
582 void msgViewerDlg::setFilter()
583 {
584  auto hostFilter = toQStringList(lwHost->selectedItems());
585  auto appFilter = toQStringList(lwApplication->selectedItems());
586  auto catFilter = toQStringList(lwCategory->selectedItems());
587 
588  lwHost->setCurrentRow(-1, QItemSelectionModel::Clear);
589  lwApplication->setCurrentRow(-1, QItemSelectionModel::Clear);
590  lwCategory->setCurrentRow(-1, QItemSelectionModel::Clear);
591 
592  if (hostFilter.isEmpty()
593  && appFilter.isEmpty()
594  && catFilter.isEmpty())
595  {
596  return;
597  }
598 
599  msg_iters_t result;
600  QString catFilterExpression = "";
601  QString hostFilterExpression = "";
602  QString appFilterExpression = "";
603  bool first = true;
604 
605  for (auto app = 0; app < appFilter.size(); ++app)
606  { // app-sev index
607  msg_iters_map_t::const_iterator it = app_msgs_.find(appFilter[app]);
608  appFilterExpression += QString(first ? "" : " || ") + appFilter[app];
609  first = false;
610  if (it != app_msgs_.end())
611  {
612  msg_iters_t temp(it->second);
613  TLOG(10) << "setFilter: app " << appFilter[app].toStdString() << " has " << temp.size() << " messages";
614  result.merge(temp);
615  }
616  }
617  TLOG(10) << "setFilter: result contains %zu messages", result.size();
618 
619  first = true;
620  if (!hostFilter.isEmpty())
621  {
622  msg_iters_t hostResult;
623  for (auto host = 0; host < hostFilter.size(); ++host)
624  { // host index
625  hostFilterExpression += QString(first ? "" : " || ") + hostFilter[host];
626  first = false;
627  msg_iters_map_t::const_iterator it = host_msgs_.find(hostFilter[host]);
628  if (it != host_msgs_.end())
629  {
630  msg_iters_t temp(it->second);
631  TLOG(10) << "setFilter: host " << hostFilter[host].toStdString() << " has " << temp.size() << " messages";
632  hostResult.merge(temp);
633  }
634  }
635  if (result.empty()) { result = hostResult; }
636  else { result = list_intersect(result, hostResult); }
637  TLOG(10) << "setFilter: result contains " << result.size() << " messages";
638  }
639 
640  first = true;
641  if (!catFilter.isEmpty())
642  {
643  msg_iters_t catResult;
644  for (auto cat = 0; cat < catFilter.size(); ++cat)
645  { // cat index
646  catFilterExpression += QString(first ? "" : " || ") + catFilter[cat];
647  first = false;
648  msg_iters_map_t::const_iterator it = cat_msgs_.find(catFilter[cat]);
649  if (it != cat_msgs_.end())
650  {
651  msg_iters_t temp(it->second);
652  TLOG(10) << "setFilter: cat " << catFilter[cat].toStdString() << " has " << temp.size() << " messages";
653  catResult.merge(temp);
654  }
655  }
656  if (result.empty()) { result = catResult; }
657  else { result = list_intersect(result, catResult); }
658  TLOG(10) << "setFilter: result contains " << result.size() << " messages";
659  }
660 
661  // Create the filter expression
662  auto nFilterExpressions = (appFilterExpression != "" ? 1 : 0) + (hostFilterExpression != "" ? 1 : 0) + (catFilterExpression != "" ? 1 : 0);
663  QString filterExpression = "";
664  if (nFilterExpressions == 1)
665  {
666  filterExpression = catFilterExpression + hostFilterExpression + appFilterExpression;
667  }
668  else
669  {
670  filterExpression = "(" + (catFilterExpression != "" ? catFilterExpression + ") && (" : "")
671  + hostFilterExpression
672  + (hostFilterExpression != "" && appFilterExpression != "" ? ") && (" : "")
673  + appFilterExpression + ")";
674  }
675 
676  // Add the tab and populate it
677 
678  auto newTabTitle = QString("Filter ") + QString::number(++nFilters);
679  QWidget* newTab = new QWidget();
680 
681  QTextEdit* txtDisplay = new QTextEdit(newTab);
682  QTextDocument* doc = new QTextDocument(txtDisplay);
683  txtDisplay->setDocument(doc);
684 
685  QVBoxLayout* layout = new QVBoxLayout();
686  layout->addWidget(txtDisplay);
687  layout->setContentsMargins(0, 0, 0, 0);
688  newTab->setLayout(layout);
689 
690  MsgFilterDisplay filteredMessages;
691  filteredMessages.msgs = result;
692  filteredMessages.hostFilter = hostFilter;
693  filteredMessages.appFilter = appFilter;
694  filteredMessages.catFilter = catFilter;
695  filteredMessages.txtDisplay = txtDisplay;
696  filteredMessages.nDisplayMsgs = result.size();
697  msgFilters_.push_back(filteredMessages);
698 
699  tabWidget->addTab(newTab, newTabTitle);
700  tabWidget->setTabToolTip(tabWidget->count() - 1, filterExpression);
701  tabWidget->setCurrentIndex(tabWidget->count() - 1);
702 
703  displayMsg(msgFilters_.size() - 1);
704 }
705 
707 {
708  if (!paused)
709  {
710  paused = true;
711  btnPause->setText("Resume");
712  //QMessageBox::about(this, "About MsgViewer", "Message receiving paused ...");
713  }
714  else
715  {
716  paused = false;
717  btnPause->setText("Pause");
718  }
719 }
720 
722 {
723  close();
724 }
725 
727 {
728  int ret = QMessageBox::question(this, tr("Message Viewer"), tr("Are you sure you want to clear all received messages?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
729  switch (ret)
730  {
731  case QMessageBox::Yes:
732  nMsgs = 0;
733  nSupMsgs = 0;
734  nThrMsgs = 0;
735  msg_pool_.clear();
736  host_msgs_.clear();
737  cat_msgs_.clear();
738  app_msgs_.clear();
739  updateList<msg_iters_map_t>(lwApplication, app_msgs_);
740  updateList<msg_iters_map_t>(lwCategory, cat_msgs_);
741  updateList<msg_iters_map_t>(lwHost, host_msgs_);
742  for (auto& display : msgFilters_)
743  {
744  display.txtDisplay->clear();
745  display.msgs.clear();
746  display.nDisplayMsgs = 0;
747  }
748 
749  lcdMsgs->display(nMsgs);
750  lcdDisplayedMsgs->display(0);
751  break;
752  case QMessageBox::No:
753  default:
754  break;
755  }
756 }
757 
759 {
760  if (!shortMode_)
761  {
762  shortMode_ = true;
763  btnDisplayMode->setText("Long View");
764  }
765  else
766  {
767  shortMode_ = false;
768  btnDisplayMode->setText("Compact View");
769  }
770  updateDisplays();
771 }
772 
774 {
775  switch (sev)
776  {
777  case SERROR:
778  setSevError();
779  break;
780 
781  case SWARNING:
782  setSevWarning();
783  break;
784 
785  case SINFO:
786  setSevInfo();
787  break;
788 
789  default: setSevDebug();
790  }
791 
792  updateDisplays();
793 }
794 
795 void msgViewerDlg::setSevError()
796 {
797  sevThresh = SERROR;
798  btnError->setChecked(true);
799  btnWarning->setChecked(false);
800  btnInfo->setChecked(false);
801  btnDebug->setChecked(false);
802  vsSeverity->setValue(sevThresh);
803 }
804 
805 void msgViewerDlg::setSevWarning()
806 {
807  sevThresh = SWARNING;
808  btnError->setChecked(false);
809  btnWarning->setChecked(true);
810  btnInfo->setChecked(false);
811  btnDebug->setChecked(false);
812  vsSeverity->setValue(sevThresh);
813 }
814 
815 void msgViewerDlg::setSevInfo()
816 {
817  sevThresh = SINFO;
818  btnError->setChecked(false);
819  btnWarning->setChecked(false);
820  btnInfo->setChecked(true);
821  btnDebug->setChecked(false);
822  vsSeverity->setValue(sevThresh);
823 }
824 
825 void msgViewerDlg::setSevDebug()
826 {
827  sevThresh = SDEBUG;
828  btnError->setChecked(false);
829  btnWarning->setChecked(false);
830  btnInfo->setChecked(false);
831  btnDebug->setChecked(true);
832  vsSeverity->setValue(sevThresh);
833 }
834 
835 void msgViewerDlg::renderMode()
836 {
837  simpleRender = !simpleRender;
838 
839  if (simpleRender)
840  {
841  btnRMode->setChecked(true);
842  for (auto display : msgFilters_)
843  {
844  display.txtDisplay->setPlainText(display.txtDisplay->toPlainText());
845  }
846  }
847  else
848  {
849  btnRMode->setChecked(false);
850  updateDisplays();
851  }
852 }
853 
854 void msgViewerDlg::searchMsg()
855 {
856  QString search = editSearch->text();
857 
858  if (search.isEmpty())
859  return;
860 
861  auto display = tabWidget->currentIndex();
862  if (search != searchStr)
863  {
864  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::Start);
865  if (!msgFilters_[display].txtDisplay->find(search))
866  {
867  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::End);
868  searchStr = "";
869  }
870  else
871  searchStr = search;
872  }
873  else
874  {
875  if (!msgFilters_[display].txtDisplay->find(search))
876  {
877  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::Start);
878  if (!msgFilters_[display].txtDisplay->find(search))
879  {
880  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::End);
881  searchStr = "";
882  }
883  }
884  }
885 }
886 
887 void msgViewerDlg::searchClear()
888 {
889  auto display = tabWidget->currentIndex();
890  editSearch->setText("");
891  searchStr = "";
892  msgFilters_[display].txtDisplay->find("");
893  msgFilters_[display].txtDisplay->moveCursor(QTextCursor::End);
894 }
895 
896 void msgViewerDlg::setSuppression(QAction* act)
897 {
898  bool status = act->isChecked();
899  suppress* sup = (suppress *)act->data().value<void*>();
900  sup->use(status);
901 }
902 
903 void msgViewerDlg::setThrottling(QAction* act)
904 {
905  bool status = act->isChecked();
906  throttle* thr = (throttle *)act->data().value<void*>();
907  thr->use(status);
908 }
909 
910 void msgViewerDlg::tabWidgetCurrentChanged(int newTab)
911 {
912  lcdDisplayedMsgs->display(msgFilters_[newTab].nDisplayMsgs);
913 
914  lwHost->setCurrentRow(-1, QItemSelectionModel::Clear);
915  lwApplication->setCurrentRow(-1, QItemSelectionModel::Clear);
916  lwCategory->setCurrentRow(-1, QItemSelectionModel::Clear);
917 
918  for (auto host : msgFilters_[newTab].hostFilter)
919  {
920  auto items = lwHost->findItems(host, Qt::MatchExactly);
921  if (items.size() > 0)
922  {
923  items[0]->setSelected(true);
924  }
925  }
926  for (auto app : msgFilters_[newTab].appFilter)
927  {
928  auto items = lwApplication->findItems(app, Qt::MatchExactly);
929  if (items.size() > 0)
930  {
931  items[0]->setSelected(true);
932  }
933  }
934  for (auto cat : msgFilters_[newTab].catFilter)
935  {
936  auto items = lwCategory->findItems(cat, Qt::MatchExactly);
937  if (items.size() > 0)
938  {
939  items[0]->setSelected(true);
940  }
941  }
942 }
943 
944 void msgViewerDlg::tabCloseRequested(int tabIndex)
945 {
946  if (tabIndex == 0 || static_cast<size_t>(tabIndex) >= msgFilters_.size()) return;
947 
948  auto widget = tabWidget->widget(tabIndex);
949  tabWidget->removeTab(tabIndex);
950  delete widget;
951 
952  msgFilters_.erase(msgFilters_.begin() + tabIndex);
953 }
954 
955 void msgViewerDlg::closeEvent(QCloseEvent* event)
956 {
957  event->accept();
958 }
959 
960 QStringList msgViewerDlg::toQStringList(QList<QListWidgetItem *> in)
961 {
962  QStringList out;
963 
964  for (auto i = 0; i < in.size(); ++i)
965  {
966  out << in[i]->text();
967  }
968 
969  return out;
970 }
QString const & app() const
Get the application of the message
Definition: qt_mf_msg.hh:86
void use(bool flag)
Enable or disable this throttle
Definition: throttle.hh:40
timeval time() const
Get the message timestamp
Definition: qt_mf_msg.hh:91
void exit()
Exit the program.
Definition: mvdlg.cc:721
void start()
Start all receivers
Qt wrapper around MessageFacility message
Definition: qt_mf_msg.hh:37
Suppress messages based on a regular expression
Definition: suppress.hh:13
void clear()
Clear the message buffer.
Definition: mvdlg.cc:726
QString const & host() const
Get the host from which the message came
Definition: qt_mf_msg.hh:76
void use(bool flag)
Set whether the suppression is active
Definition: suppress.hh:33
Throttle messages based on name and time limits. Separate from MessageFacility limiting.
Definition: throttle.hh:17
void stop()
Stop all receivers
void pause()
Pause message receiving.
Definition: mvdlg.cc:706
void changeSeverity(int sev)
Change the severity threshold.
Definition: mvdlg.cc:773
QString const & cat() const
Get the category of the message
Definition: qt_mf_msg.hh:81
void shortMode()
Switch to/from Short message mode.
Definition: mvdlg.cc:758
void closeEvent(QCloseEvent *event)
Perform actions on window close.
Definition: mvdlg.cc:955
msgViewerDlg(std::string const &conf, QDialog *parent=0)
Message Viewer Dialog Constructor.
Definition: mvdlg.cc:88