source: fact/Evidence/GUI.cc@ 17038

Last change on this file since 17038 was 17037, checked in by ogrimm, 11 years ago
Evidence GUI ported to qwt 6.1.0. Added highlighting of curves in GUI.
File size: 38.8 KB
Line 
1/* ============================================================
2
3Edd - Evidence Data Display
4
5Qt-based graphical user interface for the Evidence contron system
6
7EddLineDisplay changes its background colour in case it display
8a DIM status service
9
10April 2010, February 2012, Oliver Grimm
11
12============================================================ */
13
14#include "GUI.h"
15
16class EddDim *Handler;
17
18//
19// History chooser function (opens plot for numeric data, TextHist for all other)
20//
21void OpenHistory(char *Service, int FromIndex, int ToIndex) {
22
23 QString Format;
24 DimBrowser Browser;
25 struct EddDim::HistItem Hist = Handler->GetHistory(Service);
26 char *Name, *Fmt;
27
28 // Check if history service available
29 if (strcmp(Service, "Edd/Rate_kBSec") == 0) Format = "F";
30 else Format = Hist.Format;
31
32 // If service currently available, take its format
33 Browser.getServices(Service);
34 if (Browser.getNextService(Name, Fmt) != 0) Format = QString(Fmt);
35
36 // Open new window
37 QMainWindow *M = new QMainWindow;
38 M->setCentralWidget(new QWidget(M));
39 M->setStatusBar(new QStatusBar(M));
40 M->setAttribute(Qt::WA_DeleteOnClose);
41 M->setWindowTitle("Edd History");
42
43 QGridLayout *Layout = new QGridLayout(M->centralWidget());
44
45 // If service ends with 'C' make text display, otherwise numeric plot
46 if (Format.endsWith('C', Qt::CaseInsensitive)) Layout->addWidget(new EddText(Service), 0, 0);
47 else {
48 EddPlot *W = new EddPlot(Service, FromIndex);
49
50 // If service is array and no index given, try to find out how many indices exist and add all
51 if (FromIndex == -1 && Format.size() == 1) {
52 DimCurrentInfo X(Service, (char *) NULL, 0);
53
54 FromIndex = 0;
55 ToIndex = EvidenceServer::Tokenize(EvidenceServer::ToString(Format.toAscii().data(), X.getData(), X.getSize()), " ").size() - 1;
56 }
57
58 // Add all requested indices
59 for (int i=FromIndex+1; i<=ToIndex; i++) W->AddService(Service,i);
60
61 Layout->addWidget(W, 0, 0, 1, 5);
62 }
63
64 M->resize(400,450);
65 M->show();
66}
67
68//
69// Set status tip (returns true if service was available)
70//
71bool SetStatus(QWidget *W, QString Name, int Time, QString Format, int Index) {
72
73 QString Status;
74
75 if (Index != -1) Name = Name + ":" + QString::number(Index);
76
77 if (Time == -1) Status = QString("%1: unavailable").arg(Name);
78 else Status = QString("%1: Last update %2 Format '%3'").arg(Name).arg(QDateTime::fromTime_t(Time).toString()).arg(Format);
79
80 W->setStatusTip(Status);
81
82 return(Time != -1);
83}
84
85
86//////////////////////////////////////////
87// Text display for arbitary DIM service//
88//////////////////////////////////////////
89
90EddLineDisplay::EddLineDisplay(QString Name, int Index, QWidget *P):
91 QLineEdit(P), ServiceName(Name), Index(Index) {
92
93 LastHist = NULL;
94
95 // Widget properties
96 setReadOnly(true);
97 setMaximumWidth(100);
98 ShowAsTime = false;
99 setFrame(false);
100 setAttribute(Qt::WA_DeleteOnClose);
101 QPalette Pal = palette();
102 Pal.setColor(QPalette::Base, Qt::lightGray);
103 setPalette(Pal);
104
105 // Context menu
106 Menu = new QMenu(this);
107 Menu->addAction("Open new history", this, SLOT(MenuOpenHistory()));
108 Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
109 Menu->addAction("Copy data", this, SLOT(MenuCopyData()));
110
111 // Subscribe to service
112 Handler->Subscribe(Name, this, Index);
113}
114
115// Destructor
116EddLineDisplay::~EddLineDisplay() {
117
118 Handler->Unsubscribe(ServiceName, this, Index);
119}
120
121// Update widget
122void EddLineDisplay::Update(const QString &, int Time, const QByteArray &, const QString &Format, const QString &Text, int) {
123
124 // Check if service available
125 QPalette Pal = palette();
126 if (!SetStatus(this, ServiceName, Time, Format, Index)) {
127 setText("n/a");
128 Pal.setColor(QPalette::Base, Qt::lightGray);
129 setPalette(Pal);
130 return;
131 }
132 else Pal.setColor(QPalette::Base, Qt::white);
133
134 // Message service backgound colour determined by severity
135 if (ServiceName.endsWith("/Message")) {
136 switch (Text.section(' ', 0, 0).toInt()) {
137 case 0: Pal.setColor(QPalette::Base, Qt::white); break;
138 case 1: Pal.setColor(QPalette::Base, Qt::yellow); break;
139 case 2: Pal.setColor(QPalette::Base, Qt::red); break;
140 case 3: Pal.setColor(QPalette::Base, Qt::red); break;
141 default: break;
142 }
143 setText(Text.section(' ', 1));
144 }
145 else if (!ShowAsTime) {
146 bool OK;
147 double Num = Text.toDouble(&OK);
148
149 if (OK) setText(QString::number(Num, 'g', 4));
150 else setText(Text);
151 }
152 else setText(QDateTime::fromTime_t(Text.toInt()).toString());
153
154 setCursorPosition(0);
155 setPalette(Pal);
156}
157
158// Open plot if mouse release within widget
159void EddLineDisplay::mouseReleaseEvent(QMouseEvent *Event) {
160
161 if (Event->button()!=Qt::LeftButton || !contentsRect().contains(Event->pos())) return;
162
163 // Check if last history plot still open, then raise
164 foreach (QWidget *Widget, QApplication::allWidgets()) {
165 if (Widget == LastHist) {
166 Widget->activateWindow();
167 Widget->raise();
168 return;
169 }
170 }
171
172 // If not, open new plot
173 MenuOpenHistory();
174}
175
176// Handling of mouse press event: Register start position for drag
177void EddLineDisplay::mousePressEvent(QMouseEvent *Event) {
178
179 if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
180}
181
182// Handling of dragging (Drag and MimeData will be deleted by Qt)
183void EddLineDisplay::mouseMoveEvent(QMouseEvent *Event) {
184
185 if ((Event->buttons() & Qt::LeftButton) == 0) return;
186 if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
187
188 QDrag *Drag = new QDrag(this);
189 QMimeData *MimeData = new QMimeData;
190 QByteArray Data;
191 MimeData->setData("Edd/Service", Data.append(ServiceName + " " + QString::number(Index)));
192 Drag->setMimeData(MimeData);
193 Drag->exec();
194}
195
196//
197// Opening context menu
198//
199void EddLineDisplay::contextMenuEvent(QContextMenuEvent *Event) {
200
201 Menu->exec(Event->globalPos());
202}
203
204// Menu: Open history plot
205void EddLineDisplay::MenuOpenHistory() {
206
207 OpenHistory(ServiceName.toAscii().data(), Index);
208}
209
210// Menu: Copy service name
211void EddLineDisplay::MenuCopyService() {
212
213 QMimeData *MimeData = new QMimeData;
214 QByteArray Data;
215 MimeData->setData("Edd/Service", Data.append(ServiceName + " " + QString::number(Index)));
216 QApplication::clipboard()->setMimeData(MimeData);
217}
218
219// Menu: Copy data
220void EddLineDisplay::MenuCopyData() {
221
222 QApplication::clipboard()->setText(text());
223}
224
225
226//////////////////////////////////////////
227// Sending string command to DIM server //
228//////////////////////////////////////////
229
230EddCommand::EddCommand(QString Name, QWidget *P): QLineEdit(P), Name(Name) {
231
232 setToolTip("Send command "+Name);
233 setStatusTip(QString("Command %1").arg(Name));
234
235 connect(this, SIGNAL(returnPressed()), SLOT(SendCommand()));
236}
237
238// Get command format and returns NULL (not empty) QString if not available
239QString EddCommand::GetFormat() {
240
241 char *ItsName, *Format;
242 DimBrowser Browser;
243
244 Browser.getServices(Name.toAscii().data());
245
246 if (Browser.getNextService(ItsName, Format) != DimCOMMAND) return QString();
247 else return Format;
248}
249
250// Send command
251void EddCommand::SendCommand() {
252
253 QByteArray Data;
254
255 // Ignore empty commands
256 if (text().isEmpty()) return;
257
258 // Check if command available and retrieve format
259 QString Format = GetFormat();
260
261 if (Format.isNull()) {
262 QMessageBox::warning(this, "Edd Message", "Command " + Name + " currently unavailable", QMessageBox::Ok);
263 return;
264 }
265
266 // Command has no argument
267 if (Format.isEmpty()) {
268 DimClient::sendCommand(Name.toAscii().data(), NULL, 0);
269 return;
270 }
271
272 // Command has string as argument
273 if (Format == "C") {
274 DimClient::sendCommand(Name.toAscii().data(), text().toAscii().data());
275 return;
276 }
277
278 // Command has structure format, interpret data as hex
279 if (Format.size() > 1) {
280 Data = QByteArray::fromHex(text().toAscii());
281 }
282 else {
283 QDataStream Stream(&Data, QIODevice::WriteOnly);
284 std::vector<std::string> Values = EvidenceServer::Tokenize(text().toStdString());
285
286 for (unsigned int i=0; i<Values.size(); i++) {
287 switch (Format[0].toAscii()) {
288 case 'I':
289 case 'L': Stream << (int) atoi(Values[i].c_str()); break;
290 case 'S': Stream << (short) atoi(Values[i].c_str()); break;
291 case 'F': Stream << (float) atof(Values[i].c_str()); break;
292 case 'D': Stream << (double) atof(Values[i].c_str()); break;
293 case 'X': Stream << (long long) (atof(Values[i].c_str())); break;
294 }
295 }
296 }
297
298 DimClient::sendCommand(Name.toAscii().data(), Data.data(), Data.size());
299 clear();
300}
301
302// Opening context menu
303void EddCommand::contextMenuEvent(QContextMenuEvent *Event) {
304
305 QMenu *Menu = createStandardContextMenu();
306 Menu->addSeparator();
307 Menu->addAction("Command help", this, SLOT(MenuCommandHelp()));
308 Menu->exec(Event->globalPos());
309 delete Menu;
310}
311
312// Help text
313void EddCommand::MenuCommandHelp() {
314
315 QString Format = GetFormat();
316 QString Text = Name;
317
318 if (Format.isNull()) {
319 Text += " is currently unavailable";
320 }
321 else Text += " has format '"+Format+"'";
322
323 Text += "\n\nCommand arguments will be transmitted as string for format 'C',\n"
324 "interpreted as an array of numbers for all other single character formats\n"
325 "and as bytes in hexadecimal encoding for all more complex formats";
326
327 QMessageBox::about(this, "Edd - Command help", Text);
328}
329
330//////////////////////////////////
331// History plot for DIM service //
332//////////////////////////////////
333
334EddPlot::EddPlot(QString Service, int Index, QWidget *P): EddBasePlot(P) {
335
336 // Widget properties
337 setAcceptDrops(true);
338
339 // Horizontal time scale
340 setAxisScaleDraw(QwtPlot::xBottom, new EddDateScale());
341 connect(axisWidget(QwtPlot::xBottom), SIGNAL(scaleDivChanged()), SLOT(ScaleUpdate()));
342
343 // Additonal context menu items
344 QAction* Action = Menu->addAction("Paste service", this, SLOT(MenuPasteService()));
345 Menu->removeAction(Action);
346 Menu->insertAction(Menu->actions().value(1), Action);
347
348 Action = Menu->addAction("Show last hour", this, SLOT(MenuShowLastHour()));
349 Menu->insertAction(Menu->actions().value(8), Action);
350 Action = Menu->addAction("Show last day", this, SLOT(MenuShowLastDay()));
351 Menu->insertAction(Menu->actions().value(8), Action);
352
353 Action = Menu->addAction("All as text", this, SLOT(MenuAllAsText()));
354 Menu->insertAction(Menu->actions().value(10), Action);
355
356 // Set timer update plot after new data added
357 SingleShot = new QTimer(this);
358 connect(SingleShot, SIGNAL(timeout()), this, SLOT(UpdatePlot()));
359 SingleShot->setSingleShot(true);
360
361 // DIM client
362 if (!Service.isEmpty()) AddService(Service, Index);
363}
364
365// Destructor (items with parent widget are automatically deleted)
366EddPlot::~EddPlot() {
367
368 while (!List.isEmpty()) RemoveService(List.last().Curve);
369}
370
371// Add service to plot
372void EddPlot::AddService(QString Name, int Index) {
373
374 if (Index < 0) Index = 0;
375
376 // Check if curve already present on plot
377 for (int i=0; i<List.size(); i++) {
378 if (Name == List[i].Name && Index == List[i].Index) {
379 QMessageBox::warning(this, "Edd Message",Name+" ("+QString::number(Index)+") already present",QMessageBox::Ok);
380 return;
381 }
382 }
383
384 // Generate new curve and subscribe to service
385 struct ItemDetails N;
386
387 N.Name = Name;
388 N.Index = Index;
389 N.Curve = NewCurve(Name+":"+QString::number(Index));
390 List.append(N);
391
392 // Get history
393 struct EddDim::HistItem Hist = Handler->GetHistory(Name);
394
395 for (int i=0; i<Hist.DataText.size(); i++) {
396 if (Hist.DataText[i].second.size() > N.Index) {
397 AddPoint(List.size()-1, double(Hist.DataText[i].first)*1000, Hist.DataText[i].second.at(N.Index).toFloat());
398 }
399 }
400
401 // Subscribe to service and start updating plot after 100 ms (to allow addition of other curves)
402 Handler->Subscribe(Name, this, Index);
403 SingleShot->start(100);
404}
405
406// Remove subscription
407void EddPlot::RemoveService(QwtPlotCurve *Curve) {
408
409 for (int i=0; i<List.size(); i++) if (List[i].Curve == Curve) {
410 Handler->Unsubscribe(List[i].Name, this, List[i].Index);
411 List.removeAt(i);
412 DeleteCurve(Curve);
413 break;
414 }
415}
416
417// Update widget (must happen in GUI thread)
418void EddPlot::Update(const QString &Name, int Time, const QByteArray &, const QString &Format, const QString &Text, int Index) {
419
420 for (int ItemNo=0; ItemNo<List.size(); ItemNo++) if (List[ItemNo].Name==Name && List[ItemNo].Index==Index) {
421
422 // Append data if service available
423 if (SetStatus(this, Name, Time, Format)) AddPoint(ItemNo, (double) Time*1000, atof(Text.toAscii().data()));
424 NewData = true;
425 }
426}
427
428// Add text indicating time range to plot
429void EddPlot::ScaleUpdate() {
430
431 QwtText Text;
432 QFont Font;
433
434 // Set footer
435 Text = QDateTime::fromMSecsSinceEpoch(axisScaleDiv(QwtPlot::xBottom).lowerBound()).toString("d-MMM-yyyy hh:mm:ss") + " to " + QDateTime::fromMSecsSinceEpoch(axisScaleDiv(QwtPlot::xBottom).upperBound()).toString("d-MMM-yyyy hh:mm:ss");
436
437 Font.setPointSize(6);
438 Text.setFont(Font);
439 setFooter(Text);
440}
441
442// Drag and drop methods
443void EddPlot::dragEnterEvent(QDragEnterEvent *Event) {
444
445 if (Event->mimeData()->hasFormat("Edd/Service")) Event->acceptProposedAction();
446}
447
448void EddPlot::dropEvent(QDropEvent *Event) {
449
450 QByteArray D(Event->mimeData()->data("Edd/Service"));
451 AddService(D.left(D.lastIndexOf(' ')), D.right(D.size()-D.lastIndexOf(' ')).toInt());
452}
453
454// Add new service by pasting name
455void EddPlot::MenuPasteService() {
456
457 const QMimeData *D = QApplication::clipboard()->mimeData();
458 if (!D->hasFormat("Edd/Service")) return;
459
460 QByteArray E(D->data("Edd/Service"));
461 AddService(E.left(E.lastIndexOf(' ')), E.right(E.size()-E.lastIndexOf(' ')).toInt());
462}
463
464// Show last hour/last day
465void EddPlot::MenuShowLastHour() {
466
467 setAxisScale(QwtPlot::xBottom, (time(NULL)-60*60)*1000, (time(NULL)+60)*1000);
468 replot();
469}
470
471void EddPlot::MenuShowLastDay() {
472
473 setAxisScale(QwtPlot::xBottom, (time(NULL)-24*3600)*1000, (time(NULL)+3600)*1000);
474 replot();
475}
476
477void EddPlot::MenuAllAsText() {
478
479 QMainWindow *M = new QMainWindow;
480 M->setCentralWidget(new QWidget(M));
481 M->setStatusBar(new QStatusBar(M));
482 M->setAttribute(Qt::WA_DeleteOnClose);
483 M->setWindowTitle("Edd Services");
484
485 QGridLayout *Layout = new QGridLayout(M->centralWidget());
486
487 for (int i=0; i<List.size(); i++) {
488 Layout->addWidget(new EddLineDisplay(List[i].Name, List[i].Index), i/10, i%10);
489 }
490
491 M->resize(400,450);
492 M->show();
493}
494
495//
496// Reimplementation of QwtDateScaleDraw::label()
497//
498QwtText EddDateScale::label(double Value) const {
499
500 QString Format;
501
502 if (scaleDiv().range() > 30*24*60*60*1000.0) Format = "d-MMM\n yyyy";
503 else if (scaleDiv().range() > 1*24*60*60*1000.0) Format = " h 'h'\nd-MMM";
504 else if (scaleDiv().range() > 60*60*1000.0) Format = "hh:mm";
505 else Format = "hh:mm:ss";
506
507 return QwtDate::toString(toDateTime(Value), Format, QwtDate::FirstThursday);
508}
509
510//////////////////
511// General plot //
512//////////////////
513
514// Constructor (all slots called in GUI thread, therefore no mutex necessary)
515EddBasePlot::EddBasePlot(QWidget *P): QwtPlot(P) {
516
517 // Widget properties
518 setAttribute(Qt::WA_DeleteOnClose);
519 setAutoReplot(false);
520 setCanvasBackground(EddPlotBackgroundColor);
521 NewData = false;
522
523 // Plot navigation
524 Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
525 Zoomer->setTrackerMode(QwtPicker::AlwaysOff);
526 connect(Zoomer, SIGNAL(zoomed(const QRectF &)), this, SLOT(HandleZoom(const QRectF &)));
527 connect(Zoomer, SIGNAL(zoomed(const QRectF &)), this, SLOT(ReDoStats()));
528
529 Magnifier = new QwtPlotMagnifier(canvas());
530 Magnifier->setMouseButton(Qt::NoButton, Qt::NoModifier);
531 Magnifier->setZoomInKey(Qt::Key_M, Qt::NoModifier);
532 Magnifier->setZoomOutKey(Qt::Key_M, Qt::ShiftModifier);
533
534 Panner = new QwtPlotPanner(canvas());
535 Panner->setMouseButton(Qt::LeftButton, Qt::ShiftModifier);
536 connect(Panner, SIGNAL(panned(int, int)), this, SLOT(ReDoStats()));
537
538 Picker = new QwtPicker(QwtPicker::RectRubberBand, QwtPicker::AlwaysOff, this);
539 Picker->setStateMachine(new QwtPickerDragRectMachine);
540 connect(Picker, SIGNAL(selected(const QPolygon &)), SLOT(MouseSelection(const QPolygon &)));
541
542 // Grid and legend
543 Grid = new QwtPlotGrid;
544 Grid->setMajorPen(QPen(Qt::gray, 0, Qt::DotLine));
545 Grid->attach(this);
546
547 Legend = new EddLegend();
548 insertLegend(Legend, QwtPlot::TopLegend, 0.3);
549
550 // Marker for statistics text
551 Stats = new QwtPlotMarker();
552 Stats->setLineStyle(QwtPlotMarker::NoLine);
553 Stats->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom);
554 Stats->hide();
555 Stats->attach(this);
556
557 // Context menu
558 Menu = new QMenu(this);
559 StripAction = Menu->addAction("Stripchart", this, SLOT(UpdatePlot()));
560 StripAction->setCheckable(true);
561 Menu->addSeparator();
562 YLogAction = Menu->addAction("y scale log", this, SLOT(UpdatePlot()));
563 YLogAction->setCheckable(true);
564 NormAction = Menu->addAction("Normalize", this, SLOT(UpdatePlot()));
565 NormAction->setCheckable(true);
566 StyleAction = Menu->addAction("Draw dots", this, SLOT(UpdatePlot()));
567 StyleAction->setCheckable(true);
568 StatisticsAction = Menu->addAction("Statistics", this, SLOT(ReDoStats()));
569 StatisticsAction->setCheckable(true);
570 Menu->addAction("Zoom out", this, SLOT(MenuZoomOut()));
571 Menu->addSeparator();
572 Menu->addAction("Set update rate", this, SLOT(MenuSetUpdateRate()));
573 Menu->addSeparator();
574 Menu->addAction("Save as ASCII", this, SLOT(MenuSaveASCII()));
575 Menu->addAction("Save plot", this, SLOT(MenuSave()));
576 Menu->addAction("Print plot", this, SLOT(MenuPrint()));
577 Menu->addAction("Plot help", this, SLOT(MenuPlotHelp()));
578
579 // Set timer to regularly update plot if new data available
580 Timer = new QTimer(this);
581 connect(Timer, SIGNAL(timeout()), this, SLOT(UpdatePlot()));
582 Timer->start(2000);
583}
584
585// Destructor (items with parent widget are automatically deleted)
586EddBasePlot::~EddBasePlot() {
587
588 delete Grid;
589}
590
591// Print statistics to plot if requested
592void EddBasePlot::ReDoStats() {
593
594 QString Text;
595 double Mean, Sigma;
596 unsigned long Count = 0;
597
598 // Set visibility of statistics box
599 Stats->setVisible(StatisticsAction->isChecked());
600
601 for (int i=0; i<Items.size(); i++) {
602 Mean = 0;
603 Sigma = 0;
604 Count = 0;
605
606 // Calculate mean and sigma for data points currently visible
607 for (int j=0; j<Items[i].y.size(); j++) {
608 if (!axisScaleDiv(QwtPlot::xBottom).contains(Items[i].x[j])) continue;
609 Mean += Items[i].y[j];
610 Sigma += Items[i].y[j]*Items[i].y[j];
611 Count++;
612 }
613
614 if (Count == 0) Mean = 0;
615 else Mean /= Count;
616 if (Count < 2) Sigma = 0;
617 else Sigma = sqrt(Count/(Count-1)*(Sigma/Count-Mean*Mean));
618
619 // Prepare string
620 Text += Items[i].Signal->title().text() + ": m " + QString::number(Mean, 'f', 2) + ", s " + QString::number(Sigma, 'f', 2) + "\n";
621 }
622
623 // Replot once to get axis correct
624 replot();
625
626 // Print string to plot
627 QwtText text(Text);
628 text.setFont(QFont("Helvetica", 8));
629 Stats->setValue(axisScaleDiv(QwtPlot::xBottom).upperBound(), axisScaleDiv(QwtPlot::yLeft).upperBound());
630 Stats->setLabel(text);
631
632 // Replot again to update text
633 replot();
634}
635
636// Update all curves in plot
637void EddBasePlot::UpdatePlot() {
638
639 double Lower = axisScaleDiv(QwtPlot::xBottom).lowerBound();
640 double Upper = axisScaleDiv(QwtPlot::xBottom).upperBound();
641 double MaxTime = DBL_MIN;
642
643 // Only update if called by timer if new data is available
644 if (sender() == Timer && !NewData) return;
645 NewData = false;
646
647 // Select engine for linear or logarithmic scale
648 if (!YLogAction->isChecked()) {
649 setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
650 }
651 else setAxisScaleEngine(QwtPlot::yLeft, new QwtLogScaleEngine);
652
653 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
654 // Determine current maximum value for strip chart plotting
655 if (Items[ItemNo].Signal->boundingRect().right() > MaxTime) {
656 MaxTime = Items[ItemNo].Signal->boundingRect().right();
657 }
658
659 // Set symbol if requested
660 if (StyleAction->isChecked()) {
661 QwtSymbol *Symbol = new QwtSymbol();
662 Symbol->setStyle(QwtSymbol::Ellipse);
663 Symbol->setSize(4);
664 Items[ItemNo].Signal->setSymbol(Symbol);
665 }
666 else Items[ItemNo].Signal->setSymbol(new QwtSymbol);
667
668 // Determine number of data points
669 int DataPoints = Items[ItemNo].x.size();
670 if (DataPoints == 0) continue;
671
672 // Normalize y scale if requested
673 double *y = new double [DataPoints];
674 for (int i=0; i<DataPoints; i++) {
675 y[i] = Items[ItemNo].y[i];
676
677 if (NormAction->isChecked()) {
678 if (Items[ItemNo].Smallest != Items[ItemNo].Largest) {
679 y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
680 }
681 else y[i] = 1;
682 }
683 }
684
685 // Plot data
686 Items[ItemNo].Signal->setSamples(Items[ItemNo].x.data(), y, DataPoints);
687 Items[ItemNo].Signal->show();
688 delete[] y;
689
690 // Generate bounding box of all curves for zoomer
691 BBox = BBox.unite(Items[ItemNo].Signal->boundingRect());
692 }
693
694 // Reset zoom base to include all data
695 Zoomer->setZoomBase(BBox);
696
697 // If plot is strip char, only move axis but keep range
698 if (StripAction->isChecked()) {
699 setCanvasBackground(EddPlotBackgroundColor.lighter(90));
700 setAxisScale(QwtPlot::xBottom, Lower+ BBox.right() - MaxTime, Upper + BBox.right() - MaxTime);
701 }
702 else setCanvasBackground(EddPlotBackgroundColor);
703
704 ReDoStats();
705}
706
707// Append curve to plot
708QwtPlotCurve *EddBasePlot::NewCurve(QwtText Title) {
709
710 static Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white,
711 Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan, Qt::darkCyan, Qt::magenta, Qt::darkMagenta,
712 Qt::gray, Qt::darkGray, Qt::lightGray};
713 struct PlotItem N;
714
715 N.Signal = new QwtPlotCurve(Title);
716 N.Signal->attach(this);
717 N.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
718 N.Largest = DBL_MIN;
719 N.Smallest = DBL_MAX;
720 Items.append(N);
721
722 QVariant ItemInfo = itemToInfo(N.Signal);
723 ((EddLegendLabel *) Legend->legendWidget(ItemInfo))->Curve = N.Signal;
724
725 // Context menu might delete curve and legend -> seg fault if using direct connection, as legend item deleted
726 connect(((EddLegendLabel *) Legend->legendWidget(ItemInfo)), SIGNAL(DeleteCurve(QwtPlotCurve *)), SLOT(RemoveService(QwtPlotCurve *)), Qt::QueuedConnection);
727
728 return N.Signal;
729}
730
731// Remove item
732void EddBasePlot::DeleteCurve(QwtPlotCurve *Curve) {
733
734 for (int i=0; i<Items.size(); i++) if (Items[i].Signal == Curve) {
735 delete Curve;
736 Items.takeAt(i);
737 break;
738 }
739 UpdatePlot();
740}
741
742// Clear curve data
743void EddBasePlot::ClearCurve(unsigned int Item) {
744
745 if (Item >= (unsigned int) Items.size()) return;
746
747 Items[Item].x.clear();
748 Items[Item].y.clear();
749}
750
751// Append data point
752void EddBasePlot::AddPoint(unsigned int Item, double x, double y) {
753
754 if (Item >= (unsigned int) Items.size()) return;
755
756 Items[Item].x.append(x);
757 Items[Item].y.append(y);
758
759 if (y > Items[Item].Largest) Items[Item].Largest = y;
760 if (y < Items[Item].Smallest) Items[Item].Smallest = y;
761}
762
763// Rescale plot in case selection has been made outside the canvas
764void EddBasePlot::MouseSelection(const QPolygon &P_integer) {
765
766 QwtInterval xPlot, xMouse, yPlot, yMouse;
767 QPolygonF P(P_integer);
768
769 // Shift selected rectangle so that upper left corner is 0/0 on canvas
770 QRectF R = P.boundingRect().translated(-plotLayout()->canvasRect().topLeft());
771
772 // Current axis intervals
773 xPlot = axisScaleDiv(QwtPlot::xBottom).interval();
774 yPlot = axisScaleDiv(QwtPlot::yLeft).interval();
775
776 // Selected axis intervals
777 xMouse = QwtInterval(invTransform(QwtPlot::xBottom, R.left()),
778 invTransform(QwtPlot::xBottom, R.right()));
779 yMouse = QwtInterval(invTransform(QwtPlot::yLeft, R.bottom()),
780 invTransform(QwtPlot::yLeft, R.top()));
781
782 // Selection region outside all axis?
783 if (R.right() < 0 && R.top() > plotLayout()->canvasRect().height()) return;
784
785 // If selected rectangle completely inside canvas taken care of by zoomer
786 if (plotLayout()->canvasRect().contains(P.boundingRect())) return;
787
788 // Rescale both axis if selected rectangle encompasses canvas completely
789 if (P.boundingRect().contains(plotLayout()->canvasRect())) {
790 yMouse.setMaxValue(yMouse.maxValue() + yPlot.width());
791 yMouse.setMinValue(yMouse.minValue() - yPlot.width());
792 xMouse.setMinValue(xMouse.minValue() - xPlot.width());
793 xMouse.setMaxValue(xMouse.maxValue() + xPlot.width());
794
795 setAxisScale(QwtPlot::xBottom, xMouse.minValue(), xMouse.maxValue());
796 setAxisScale(QwtPlot::yLeft, yMouse.minValue(), yMouse.maxValue());
797 }
798
799 // Rescale y axis (increase range if selected rectangle larger than axis area)
800 if (R.right() < 0) {
801 if (yMouse.maxValue() > axisScaleDiv(QwtPlot::yLeft).upperBound()) {
802 yMouse.setMaxValue(yMouse.maxValue() + yPlot.width());
803 }
804 if (yMouse.minValue() < axisScaleDiv(QwtPlot::yLeft).lowerBound()) {
805 yMouse.setMinValue(yMouse.minValue() - yPlot.width());
806 }
807
808 setAxisScale(QwtPlot::yLeft, yMouse.minValue(), yMouse.maxValue());
809 }
810
811 // Rescale x axis (increase range if selected rectangle larger than axis area)
812 if (R.top() > plotLayout()->canvasRect().height()) {
813 if (xMouse.maxValue() > axisScaleDiv(QwtPlot::xBottom).upperBound()) {
814 xMouse.setMaxValue(xMouse.maxValue() + xPlot.width());
815 }
816 if (xMouse.minValue() < axisScaleDiv(QwtPlot::xBottom).lowerBound()) {
817 xMouse.setMinValue(xMouse.minValue() - xPlot.width());
818 }
819
820 setAxisScale(QwtPlot::xBottom, xMouse.minValue(), xMouse.maxValue());
821 }
822
823 // Draw new scales
824 replot();
825}
826
827// Reset graph axes to autoscale when fully unzoomed
828void EddBasePlot::HandleZoom(const QRectF &) {
829
830 if(Zoomer->zoomRectIndex() == 0) {
831 setAxisAutoScale(QwtPlot::xBottom);
832 setAxisAutoScale(QwtPlot::yLeft);
833 }
834
835 UpdatePlot();
836}
837
838// Double clicking hightlights curve
839void EddBasePlot::mouseDoubleClickEvent (QMouseEvent *Event) {
840
841 double Dist, MinDistance = std::numeric_limits<double>::infinity();
842 int Index = -1;
843
844 // Check which curve is closest
845 for (int i=0; i<Items.size(); i++) {
846 if (Items[i].Signal->closestPoint(Event->pos(), &Dist) != -1 && Dist < MinDistance) {
847 MinDistance = Dist;
848 Index = i;
849 }
850 }
851
852 // Toggle 'thick line' action
853 if (Index != -1) {
854 ((EddLegendLabel *) Legend->legendWidget(itemToInfo(Items[Index].Signal)))->ThickLineAction->toggle();
855 }
856}
857
858// Opening context menu
859void EddBasePlot::contextMenuEvent(QContextMenuEvent *Event) {
860
861 Menu->exec(Event->globalPos());
862}
863
864// Zoom completely out
865void EddBasePlot::MenuZoomOut() {
866
867 Zoomer->zoom(0);
868 UpdatePlot();
869}
870
871// Set maximum update rate of plot
872void EddBasePlot::MenuSetUpdateRate() {
873
874 bool OK;
875 double Rate;
876
877 Rate = QInputDialog::getDouble(this, "Edd Request",
878 "Minimum period between plot updates (sec)", (double) Timer->interval()/1000, 0, std::numeric_limits<double>::max(), 2, &OK);
879 if (OK) Timer->setInterval(Rate*1000);
880}
881
882// Save data of plot as test
883void EddBasePlot::MenuSaveASCII() {
884 QString Filename = QFileDialog::getSaveFileName(this,
885 "Filename", ".", "Text files (*.txt *.ascii *.asc);;All files (*)");
886 if (Filename.length() <= 0) return;
887
888 QFile File(Filename);
889 if (!File.open(QFile::WriteOnly | QIODevice::Text | QFile::Truncate)) {
890 QMessageBox::warning(this, "Edd Message","Could not open file for writing.",QMessageBox::Ok);
891 return;
892 }
893
894 // Write x and y data for all signals to file
895 QTextStream Stream(&File);
896
897 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
898 Stream << QString("# ") + Items[ItemNo].Signal->title().text() + ".hist" << endl;
899 for (unsigned int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
900 //Stream << Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->y(i) << endl;
901 Stream << Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->sample(i).y() << endl;
902 }
903 }
904}
905
906// Print plot
907void EddBasePlot::MenuPrint() {
908
909 QPrinter *Printer = new QPrinter;
910 QPrintDialog *PrintDialog = new QPrintDialog(Printer, this);
911 if (PrintDialog->exec() == QDialog::Accepted) {
912 QPainter Painter(Printer);
913 QPixmap Pixmap = QPixmap::grabWidget(this);
914 Painter.drawPixmap(0, 0, Pixmap);
915 }
916 delete Printer; delete PrintDialog;
917}
918
919// Save plot as image
920void EddBasePlot::MenuSave() {
921
922 QString Filename = QFileDialog::getSaveFileName(this,
923 "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)");
924 if (Filename.length()>0) {
925 QPixmap Pixmap = QPixmap::grabWidget(this);
926 if(!Pixmap.save(Filename)) {
927 QMessageBox::warning(this, "Edd Message","Could not write image file.",QMessageBox::Ok);
928 remove(Filename.toAscii().data());
929 }
930 }
931}
932
933// Help text
934void EddBasePlot::MenuPlotHelp() {
935
936 QMessageBox::about(this, "Edd - Plot help",
937 "Zoom\tMouse wheel\n"
938 "\tKeys m and shift-m\n"
939 "\tSelecting region with left mouse button\n"
940 "\tMiddle button zooms out one level\n"
941 "\tSelecting a range on an axis\n"
942 "\tSelecting whole canvas\n\n"
943 "Pan\tShift and left mouse button\n\n"
944 "ESC cancels selection\n"
945 "Double-clicking highlights nearest curve\n"
946 "Cursor keys move mouse\n\n"
947 "Statistics are calculated over the current x axis extend\n\n"
948 "Items can be added to history plot by drag&drop from\n"
949 "DIM service displays or from other plots legends");
950}
951
952
953////////////////////////////////
954// Edd Legend and LegendLabel //
955////////////////////////////////
956
957// Reimplementation to return EddLegendLabel widget
958QWidget *EddLegend::createWidget(const QwtLegendData &) const {
959
960 return new EddLegendLabel();
961}
962
963// Constructor for EddLegendLabel class
964EddLegendLabel::EddLegendLabel() {
965
966 // Context menu
967 Menu = new QMenu(this);
968 Menu->addAction("Open in new history", this, SLOT(MenuOpenHistory()));
969 Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
970
971 ThickLineAction = new QAction("Thick line", this);
972 ThickLineAction->setCheckable(true);
973 connect(ThickLineAction, SIGNAL(toggled(bool)), SLOT(MenuThickLine(bool)));
974 Menu->addAction(ThickLineAction);
975
976 Menu->addAction("Remove curve", this, SLOT(MenuRemove()));
977
978 Curve = NULL;
979}
980
981// Opening context menu
982void EddLegendLabel::contextMenuEvent(QContextMenuEvent *Event) {
983
984 Menu->exec(Event->globalPos());
985}
986
987// Handling of mouse press event: Register start position for drag
988void EddLegendLabel::mousePressEvent(QMouseEvent *Event) {
989
990 if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
991}
992
993// Handling of dragging (Drag and MimeData will be deleted by Qt)
994void EddLegendLabel::mouseMoveEvent(QMouseEvent *Event) {
995
996 if ((Event->buttons() & Qt::LeftButton) == 0) return;
997 if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
998
999 QString D(text().text());
1000 D.replace(':',' ');
1001
1002 QDrag *Drag = new QDrag(this);
1003 QMimeData *MimeData = new QMimeData;
1004 QByteArray Data;
1005 MimeData->setData("Edd/Service", Data.append(D));
1006 Drag->setMimeData(MimeData);
1007 Drag->exec();
1008}
1009
1010// Handling of mouse release event: Open history
1011void EddLegendLabel::mouseReleaseEvent(QMouseEvent *Event) {
1012
1013 if (Event->button() != Qt::LeftButton) return;
1014
1015 QString D(text().text());
1016 D.replace(':',' ');
1017 QStringList A = D.split(" ");
1018
1019 OpenHistory(A[0].toAscii().data(), A[1].toInt());
1020}
1021
1022// Menu: Open history plot
1023void EddLegendLabel::MenuOpenHistory() {
1024
1025 QString D(text().text());
1026 D.replace(':',' ');
1027 QStringList A = D.split(" ");
1028
1029 OpenHistory(A[0].toAscii().data(), A[1].toInt());
1030}
1031
1032// Menu: Copy service name
1033void EddLegendLabel::MenuCopyService() {
1034
1035 QString D(text().text());
1036 D.replace(':',' ');
1037
1038 QMimeData *MimeData = new QMimeData;
1039 QByteArray Data;
1040 MimeData->setData("Edd/Service", Data.append(D));
1041 QApplication::clipboard()->setMimeData(MimeData);
1042}
1043
1044// Menu: Thick line width
1045void EddLegendLabel::MenuThickLine(bool State) {
1046
1047 if (Curve == NULL) {
1048 printf("Warning: No QwtPlotCurve set in EddLegendLabel::MenuThickLine(), programming error\n");
1049 return;
1050 }
1051
1052 // Set pxel width of curve
1053 QPen Pen = Curve->pen();
1054 Pen.setWidth(State ? 4:1);
1055 Curve->setPen(Pen);
1056 Curve->plot()->replot();
1057
1058 // Highlight legend entry
1059 QFont Font = font();
1060 Font.setWeight(State ? QFont::Bold:QFont::Normal);
1061 setFont(Font);
1062}
1063
1064// Menu: Normal line width
1065void EddLegendLabel::MenuRemove() {
1066
1067 emit(DeleteCurve(Curve));
1068}
1069
1070//////////////////////////////////////
1071// History text box for DIM service //
1072//////////////////////////////////////
1073
1074// Constructor
1075EddText::EddText(QString Name, bool Pure, QWidget *P):
1076 QTextEdit(P), Name(Name), Pure(Pure) {
1077
1078 // Widget properties
1079 setReadOnly(true);
1080 setAttribute(Qt::WA_DeleteOnClose);
1081 setAutoFillBackground(true);
1082 document()->setMaximumBlockCount(1000);
1083 Accumulate = true;
1084
1085 // Get history for this service
1086 if (!Pure) {
1087 struct EddDim::HistItem Hist = Handler->GetHistory(Name);
1088
1089 for (int i=0; i<Hist.DataText.size(); i++) {
1090 moveCursor (QTextCursor::Start);
1091 insertPlainText(QString("(")+QDateTime::fromTime_t(Hist.DataText[i].first).toString()+") " +
1092 QString(Hist.DataRaw[i].second) + "\n");
1093 }
1094 }
1095
1096 // DIM client
1097 Handler->Subscribe(Name, this);
1098}
1099
1100// Destructor
1101EddText::~EddText() {
1102
1103 Handler->Unsubscribe(Name, this);
1104}
1105
1106
1107// Update widget (must happen in GUI thread)
1108void EddText::Update(const QString &, int Time, const QByteArray &, const QString &Format, const QString &Text, int) {
1109
1110 QPalette Pal = palette();
1111
1112 // Check if service available
1113 if (!SetStatus(this, Name, Time, Format)) {
1114 Pal.setColor(QPalette::Base, Qt::lightGray);
1115 setPalette(Pal);
1116 setText("n/a");
1117 return;
1118 }
1119
1120 Pal.setColor(QPalette::Base, Qt::white);
1121 setPalette(Pal);
1122 QDateTime Timex = QDateTime::fromTime_t(Time);
1123
1124 // Clear display in case text should not accumulate
1125 if (Accumulate == false) clear();
1126
1127 if (!Pure) {
1128 moveCursor(QTextCursor::Start);
1129 insertPlainText(QString("(")+Timex.toString()+QString(") "));
1130 insertPlainText(Text + "\n");
1131 }
1132 else if (Format == "C") insertPlainText(Text);
1133}
1134
1135
1136/////////////////////////////
1137// Interface to Dim system //
1138/////////////////////////////
1139EddDim::EddDim() {
1140
1141 Volume = 0;
1142 Period = 10;
1143 Mutex = new QMutex(QMutex::Recursive);
1144
1145 // Timer to calculate data rates
1146 QTimer *Timer = new QTimer(this);
1147 Timer->connect(Timer, SIGNAL(timeout()), this, SLOT(UpdateStatistics()));
1148 Timer->start(Period*1000);
1149
1150 // Connect to DIM handler
1151 if (connect(this, SIGNAL(INT(QString, int, QByteArray, QString)), SLOT(Update(QString, int, QByteArray, QString))) == false) {
1152 printf("Failed connection in EddDim()\n");
1153 }
1154}
1155
1156// Destructor
1157EddDim::~EddDim() {
1158
1159 delete Mutex;
1160}
1161
1162// Subscribe to DIM service
1163void EddDim::Subscribe(QString Name, class EddWidget *Instance, int Index) {
1164
1165 // Lock before accessing list
1166 QMutexLocker Locker(Mutex);
1167
1168 // Check if already subscribed to service
1169 if (ServiceList.contains(Name)) {
1170 ServiceList[Name].Subscribers.append(QPair<class EddWidget *, int>(Instance, Index));
1171
1172 if (Index>=0 && Index<ServiceList[Name].Items.size()) {
1173 Instance->Update(Name, ServiceList[Name].TimeStamp, ServiceList[Name].ByteArray, ServiceList[Name].Format, ServiceList[Name].Items[Index]);
1174 }
1175 else Instance->Update(Name, ServiceList[Name].TimeStamp, ServiceList[Name].ByteArray, ServiceList[Name].Format, ServiceList[Name].Text);
1176
1177 return;
1178 }
1179
1180 // Create new entry in service list
1181 ServiceList[Name].ByteArray = QByteArray();
1182 ServiceList[Name].TimeStamp = -1;
1183 ServiceList[Name].Subscribers.append(QPair<class EddWidget *, int>(Instance, Index));
1184 ServiceList[Name].DIMService = new DimStampedInfo(Name.toAscii().data(), INT_MAX, NO_LINK, this);
1185}
1186
1187
1188// Unsubscribe from DIM service
1189void EddDim::Unsubscribe(QString Name, class EddWidget *Instance, int Index) {
1190
1191 // Lock before accessing list
1192 QMutexLocker Locker(Mutex);
1193
1194 if (!ServiceList.contains(Name)) return;
1195
1196 QPair<class EddWidget *, int> P(Instance, Index);
1197
1198 if (ServiceList[Name].Subscribers.contains(P)) {
1199 ServiceList[Name].Subscribers.removeAt(ServiceList[Name].Subscribers.indexOf(P));
1200 }
1201
1202 // If no more needed, drop DIM subsription
1203 if (ServiceList[Name].Subscribers.isEmpty()) {
1204 delete ServiceList[Name].DIMService;
1205 ServiceList.remove(Name);
1206 }
1207}
1208
1209// Ignore service in update
1210void EddDim::Ignore(QString Name, bool Ignore) {
1211
1212 QMutexLocker Locker(&IgnoreMutex);
1213 IgnoreMap[Name] = Ignore;
1214}
1215
1216// Get history buffer
1217struct EddDim::HistItem EddDim::GetHistory(QString Name) {
1218
1219 // History already available?
1220 if (HistoryList.contains(Name)) return HistoryList[Name];
1221
1222 // Create history class to retrieve history data
1223 const struct EvidenceHistory::Item *R;
1224 class EvidenceHistory *Hist = new EvidenceHistory(Name.toStdString());
1225 Hist->GetHistory();
1226 HistoryList[Name].Time = time(NULL);
1227 HistoryList[Name].Format = Hist->GetFormat();
1228
1229 while ((R = Hist->Next()) != NULL) {
1230 HistoryList[Name].DataRaw.push_back(QPair<int, QByteArray>(R->Time, QByteArray(R->Data, R->Size)));
1231 HistoryList[Name].DataText.push_back(QPair<int, QStringList>(R->Time, QString::fromStdString(EvidenceServer::ToString(Hist->GetFormat(), R->Data, R->Size)).split(' ')));
1232 }
1233
1234 delete Hist;
1235
1236 return HistoryList[Name];
1237}
1238
1239
1240// Update throughput statistics and clear up history memory
1241void EddDim::UpdateStatistics() {
1242
1243 // Lock before accessing internal variables
1244 QMutexLocker Locker(Mutex);
1245
1246 // Remove unused histories after not less than 5 seconds
1247 QList<QString> L = HistoryList.keys();
1248 for(int i=0; i<L.size(); i++) {
1249 if ((time(NULL)-HistoryList[L[i]].Time) > 60) HistoryList.remove(L[i]);
1250 }
1251
1252 float Rate = Volume/1024.0/Period;
1253 Volume = 0;
1254
1255 // No unlock because Mutex is recursive
1256 Update("Edd/Rate_kBSec", time(NULL), QByteArray((char *) &Rate, sizeof(Rate)), "F");
1257}
1258
1259
1260// Store service information for usage by Subscribe(), update statistics and emit signal to widgets
1261void EddDim::Update(QString Name, int Time, QByteArray Data, QString Format) {
1262
1263 // Lock before accessing list
1264 QMutexLocker Locker(Mutex);
1265
1266 Volume += Data.size();
1267
1268 // Store service data and update all subscribers
1269 if (ServiceList.contains(Name)) {
1270 ServiceList[Name].TimeStamp = Time;
1271 ServiceList[Name].ByteArray = Data;
1272 ServiceList[Name].Format = Format;
1273 ServiceList[Name].Text = QString::fromStdString(EvidenceServer::ToString(Format.toAscii().data(), Data.data(), Data.size()));
1274 ServiceList[Name].Items = ServiceList[Name].Text.split(" ");
1275
1276 for (int i=0; i<ServiceList[Name].Subscribers.size(); i++) {
1277 QPair<class EddWidget *, int> P = ServiceList[Name].Subscribers[i];
1278
1279 if (P.second >=0 && P.second < ServiceList[Name].Items.size()) {
1280 P.first->Update(Name, Time, Data, Format, ServiceList[Name].Items[P.second], P.second);
1281 }
1282 else P.first->Update(Name, Time, Data, Format, ServiceList[Name].Text, P.second);
1283 }
1284 }
1285}
1286
1287// Handling of DIM service update (Data asynchronouly send to EddDim::Update())
1288void EddDim::infoHandler() {
1289
1290 QMutexLocker Locker(&IgnoreMutex);
1291 bool Ignore = IgnoreMap[getInfo()->getName()];
1292 Locker.unlock();
1293
1294 if (!EvidenceServer::ServiceOK(getInfo())) INT(getInfo()->getName(), -1);
1295 else if (!Ignore) INT(getInfo()->getName(), getInfo()->getTimestamp(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), getInfo()->getFormat());
1296}
1297
1298
1299/////////////////////
1300// Open new window //
1301/////////////////////
1302
1303// Constructor
1304EddWindow::EddWindow(QString ButtonName, QString WindowName): QPushButton(ButtonName) {
1305
1306 M = new QMainWindow;
1307 M->setCentralWidget(new QWidget);
1308 M->setStatusBar(new QStatusBar(M));
1309 M->setWindowTitle(WindowName);
1310 L = new QGridLayout(M->centralWidget());
1311
1312 connect(this, SIGNAL(pressed()), M, SLOT(show()));
1313 connect(this, SIGNAL(pressed()), M, SLOT(raise()));
1314}
1315
1316// Return layout
1317QGridLayout *EddWindow::Layout() {
1318
1319 return L;
1320}
1321
1322// Return window
1323QMainWindow *EddWindow::Window() {
1324
1325 return M;
1326}
Note: See TracBrowser for help on using the repository browser.