source: fact/Evidence/GUI.cc@ 17977

Last change on this file since 17977 was 17039, checked in by ogrimm, 11 years ago
Changed curve highlighting to single clicks
File size: 38.9 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
342 // Additonal context menu items
343 QAction* Action = Menu->addAction("Paste service", this, SLOT(MenuPasteService()));
344 Menu->removeAction(Action);
345 Menu->insertAction(Menu->actions().value(1), Action);
346
347 Action = Menu->addAction("Show last hour", this, SLOT(MenuShowLastHour()));
348 Menu->insertAction(Menu->actions().value(8), Action);
349 Action = Menu->addAction("Show last day", this, SLOT(MenuShowLastDay()));
350 Menu->insertAction(Menu->actions().value(8), Action);
351
352 Action = Menu->addAction("All as text", this, SLOT(MenuAllAsText()));
353 Menu->insertAction(Menu->actions().value(10), Action);
354
355 // Set timer update plot after new data added
356 SingleShot = new QTimer(this);
357 connect(SingleShot, SIGNAL(timeout()), this, SLOT(UpdatePlot()));
358 SingleShot->setSingleShot(true);
359
360 // DIM client
361 if (!Service.isEmpty()) AddService(Service, Index);
362}
363
364// Destructor (items with parent widget are automatically deleted)
365EddPlot::~EddPlot() {
366
367 while (!List.isEmpty()) RemoveService(List.last().Curve);
368}
369
370// Add service to plot
371void EddPlot::AddService(QString Name, int Index) {
372
373 if (Index < 0) Index = 0;
374
375 // Check if curve already present on plot
376 for (int i=0; i<List.size(); i++) {
377 if (Name == List[i].Name && Index == List[i].Index) {
378 QMessageBox::warning(this, "Edd Message",Name+" ("+QString::number(Index)+") already present",QMessageBox::Ok);
379 return;
380 }
381 }
382
383 // Generate new curve and subscribe to service
384 struct ItemDetails N;
385
386 N.Name = Name;
387 N.Index = Index;
388 N.Curve = NewCurve(Name+":"+QString::number(Index));
389 List.append(N);
390
391 // Get history
392 struct EddDim::HistItem Hist = Handler->GetHistory(Name);
393
394 for (int i=0; i<Hist.DataText.size(); i++) {
395 if (Hist.DataText[i].second.size() > N.Index) {
396 AddPoint(List.size()-1, double(Hist.DataText[i].first)*1000, Hist.DataText[i].second.at(N.Index).toFloat());
397 }
398 }
399
400 // Subscribe to service and start updating plot after 100 ms (to allow addition of other curves)
401 Handler->Subscribe(Name, this, Index);
402 SingleShot->start(100);
403}
404
405// Remove subscription
406void EddPlot::RemoveService(QwtPlotCurve *Curve) {
407
408 for (int i=0; i<List.size(); i++) if (List[i].Curve == Curve) {
409 Handler->Unsubscribe(List[i].Name, this, List[i].Index);
410 List.removeAt(i);
411 DeleteCurve(Curve);
412 break;
413 }
414}
415
416// Update widget (must happen in GUI thread)
417void EddPlot::Update(const QString &Name, int Time, const QByteArray &, const QString &Format, const QString &Text, int Index) {
418
419 for (int ItemNo=0; ItemNo<List.size(); ItemNo++) if (List[ItemNo].Name==Name && List[ItemNo].Index==Index) {
420
421 // Append data if service available
422 if (SetStatus(this, Name, Time, Format)) AddPoint(ItemNo, (double) Time*1000, atof(Text.toAscii().data()));
423 NewData = true;
424 }
425}
426
427// Drag and drop methods
428void EddPlot::dragEnterEvent(QDragEnterEvent *Event) {
429
430 if (Event->mimeData()->hasFormat("Edd/Service")) Event->acceptProposedAction();
431}
432
433void EddPlot::dropEvent(QDropEvent *Event) {
434
435 QByteArray D(Event->mimeData()->data("Edd/Service"));
436 AddService(D.left(D.lastIndexOf(' ')), D.right(D.size()-D.lastIndexOf(' ')).toInt());
437}
438
439// Add new service by pasting name
440void EddPlot::MenuPasteService() {
441
442 const QMimeData *D = QApplication::clipboard()->mimeData();
443 if (!D->hasFormat("Edd/Service")) return;
444
445 QByteArray E(D->data("Edd/Service"));
446 AddService(E.left(E.lastIndexOf(' ')), E.right(E.size()-E.lastIndexOf(' ')).toInt());
447}
448
449// Show last hour/last day
450void EddPlot::MenuShowLastHour() {
451
452 setAxisScale(QwtPlot::xBottom, (time(NULL)-60*60)*1000, (time(NULL)+60)*1000);
453 replot();
454}
455
456void EddPlot::MenuShowLastDay() {
457
458 setAxisScale(QwtPlot::xBottom, (time(NULL)-24*3600)*1000, (time(NULL)+3600)*1000);
459 replot();
460}
461
462void EddPlot::MenuAllAsText() {
463
464 QMainWindow *M = new QMainWindow;
465 M->setCentralWidget(new QWidget(M));
466 M->setStatusBar(new QStatusBar(M));
467 M->setAttribute(Qt::WA_DeleteOnClose);
468 M->setWindowTitle("Edd Services");
469
470 QGridLayout *Layout = new QGridLayout(M->centralWidget());
471
472 for (int i=0; i<List.size(); i++) {
473 Layout->addWidget(new EddLineDisplay(List[i].Name, List[i].Index), i/10, i%10);
474 }
475
476 M->resize(400,450);
477 M->show();
478}
479
480//
481// Reimplementation of QwtDateScaleDraw::label()
482//
483QwtText EddDateScale::label(double Value) const {
484
485 QString Format;
486
487 if (scaleDiv().range() > 30*24*60*60*1000.0) Format = "d-MMM\n yyyy";
488 else if (scaleDiv().range() > 1*24*60*60*1000.0) Format = " h 'h'\nd-MMM";
489 else if (scaleDiv().range() > 60*60*1000.0) Format = "hh:mm";
490 else Format = "hh:mm:ss";
491
492 return QwtDate::toString(toDateTime(Value), Format, QwtDate::FirstThursday);
493}
494
495//////////////////
496// General plot //
497//////////////////
498
499// Constructor (all slots called in GUI thread, therefore no mutex necessary)
500EddBasePlot::EddBasePlot(QWidget *P): QwtPlot(P) {
501
502 // Widget properties
503 setAttribute(Qt::WA_DeleteOnClose);
504 setAutoReplot(false);
505 setCanvasBackground(EddPlotBackgroundColor);
506 NewData = false;
507
508 // Plot navigation
509 Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
510 Zoomer->setTrackerMode(QwtPicker::AlwaysOff);
511 connect(Zoomer, SIGNAL(zoomed(const QRectF &)), this, SLOT(HandleZoom(const QRectF &)));
512 connect(Zoomer, SIGNAL(zoomed(const QRectF &)), this, SLOT(ReDoStats()));
513
514 Magnifier = new QwtPlotMagnifier(canvas());
515 Magnifier->setMouseButton(Qt::NoButton, Qt::NoModifier);
516 Magnifier->setZoomInKey(Qt::Key_M, Qt::NoModifier);
517 Magnifier->setZoomOutKey(Qt::Key_M, Qt::ShiftModifier);
518
519 Panner = new QwtPlotPanner(canvas());
520 Panner->setMouseButton(Qt::LeftButton, Qt::ShiftModifier);
521 connect(Panner, SIGNAL(panned(int, int)), this, SLOT(ReDoStats()));
522
523 Picker = new QwtPicker(QwtPicker::RectRubberBand, QwtPicker::AlwaysOff, this);
524 Picker->setStateMachine(new QwtPickerDragRectMachine);
525 connect(Picker, SIGNAL(selected(const QPolygon &)), SLOT(MouseSelection(const QPolygon &)));
526
527 // Grid and legend
528 Grid = new QwtPlotGrid;
529 Grid->setMajorPen(QPen(Qt::gray, 0, Qt::DotLine));
530 Grid->attach(this);
531
532 Legend = new EddLegend();
533 insertLegend(Legend, QwtPlot::TopLegend, 0.3);
534
535 // Marker for statistics text
536 Stats = new QwtPlotMarker();
537 Stats->setLineStyle(QwtPlotMarker::NoLine);
538 Stats->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom);
539 Stats->hide();
540 Stats->attach(this);
541
542 // Context menu
543 Menu = new QMenu(this);
544 StripAction = Menu->addAction("Stripchart", this, SLOT(UpdatePlot()));
545 StripAction->setCheckable(true);
546 Menu->addSeparator();
547 YLogAction = Menu->addAction("y scale log", this, SLOT(UpdatePlot()));
548 YLogAction->setCheckable(true);
549 NormAction = Menu->addAction("Normalize", this, SLOT(UpdatePlot()));
550 NormAction->setCheckable(true);
551 StyleAction = Menu->addAction("Draw dots", this, SLOT(UpdatePlot()));
552 StyleAction->setCheckable(true);
553 StatisticsAction = Menu->addAction("Statistics", this, SLOT(ReDoStats()));
554 StatisticsAction->setCheckable(true);
555 Menu->addAction("Zoom out", this, SLOT(MenuZoomOut()));
556 Menu->addSeparator();
557 Menu->addAction("Set update rate", this, SLOT(MenuSetUpdateRate()));
558 Menu->addSeparator();
559 Menu->addAction("Save as ASCII", this, SLOT(MenuSaveASCII()));
560 Menu->addAction("Save plot", this, SLOT(MenuSave()));
561 Menu->addAction("Print plot", this, SLOT(MenuPrint()));
562 Menu->addAction("Plot help", this, SLOT(MenuPlotHelp()));
563
564 // Set timer to regularly update plot if new data available
565 Timer = new QTimer(this);
566 connect(Timer, SIGNAL(timeout()), this, SLOT(UpdatePlot()));
567 Timer->start(2000);
568}
569
570// Destructor (items with parent widget are automatically deleted)
571EddBasePlot::~EddBasePlot() {
572
573 delete Grid;
574}
575
576// Print statistics to plot if requested
577void EddBasePlot::ReDoStats() {
578
579 QString Text;
580 double Mean, Sigma;
581 unsigned long Count = 0;
582
583 // Set visibility of statistics box
584 Stats->setVisible(StatisticsAction->isChecked());
585
586 for (int i=0; i<Items.size(); i++) {
587 Mean = 0;
588 Sigma = 0;
589 Count = 0;
590
591 // Calculate mean and sigma for data points currently visible
592 for (int j=0; j<Items[i].y.size(); j++) {
593 if (!axisScaleDiv(QwtPlot::xBottom).contains(Items[i].x[j])) continue;
594 Mean += Items[i].y[j];
595 Sigma += Items[i].y[j]*Items[i].y[j];
596 Count++;
597 }
598
599 if (Count == 0) Mean = 0;
600 else Mean /= Count;
601 if (Count < 2) Sigma = 0;
602 else Sigma = sqrt(Count/(Count-1)*(Sigma/Count-Mean*Mean));
603
604 // Prepare string
605 Text += Items[i].Signal->title().text() + ": m " + QString::number(Mean, 'f', 2) + ", s " + QString::number(Sigma, 'f', 2) + "\n";
606 }
607
608 // Replot once to get axis correct
609 replot();
610
611 // Print string to plot
612 QwtText text(Text);
613 text.setFont(QFont("Helvetica", 8));
614 Stats->setValue(axisScaleDiv(QwtPlot::xBottom).upperBound(), axisScaleDiv(QwtPlot::yLeft).upperBound());
615 Stats->setLabel(text);
616
617 // Replot again to update text
618 replot();
619}
620
621// Update all curves in plot
622void EddBasePlot::UpdatePlot() {
623
624 double Lower = axisScaleDiv(QwtPlot::xBottom).lowerBound();
625 double Upper = axisScaleDiv(QwtPlot::xBottom).upperBound();
626 double MaxTime = DBL_MIN;
627
628 // Only update if called by timer if new data is available
629 if (sender() == Timer && !NewData) return;
630 NewData = false;
631
632 // Select engine for linear or logarithmic scale
633 if (!YLogAction->isChecked()) {
634 setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
635 }
636 else setAxisScaleEngine(QwtPlot::yLeft, new QwtLogScaleEngine);
637
638 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
639 // Determine current maximum value for strip chart plotting
640 if (Items[ItemNo].Signal->boundingRect().right() > MaxTime) {
641 MaxTime = Items[ItemNo].Signal->boundingRect().right();
642 }
643
644 // Set symbol if requested
645 if (StyleAction->isChecked()) {
646 QwtSymbol *Symbol = new QwtSymbol();
647 Symbol->setStyle(QwtSymbol::Ellipse);
648 Symbol->setSize(4);
649 Items[ItemNo].Signal->setSymbol(Symbol);
650 }
651 else Items[ItemNo].Signal->setSymbol(new QwtSymbol);
652
653 // Determine number of data points
654 int DataPoints = Items[ItemNo].x.size();
655 if (DataPoints == 0) continue;
656
657 // Normalize y scale if requested
658 double *y = new double [DataPoints];
659 for (int i=0; i<DataPoints; i++) {
660 y[i] = Items[ItemNo].y[i];
661
662 if (NormAction->isChecked()) {
663 if (Items[ItemNo].Smallest != Items[ItemNo].Largest) {
664 y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
665 }
666 else y[i] = 1;
667 }
668 }
669
670 // Plot data
671 Items[ItemNo].Signal->setSamples(Items[ItemNo].x.data(), y, DataPoints);
672 Items[ItemNo].Signal->show();
673 delete[] y;
674
675 // Generate bounding box of all curves for zoomer
676 BBox = BBox.unite(Items[ItemNo].Signal->boundingRect());
677 }
678
679 // Reset zoom base to include all data
680 Zoomer->setZoomBase(BBox);
681
682 // If plot is strip char, only move axis but keep range
683 if (StripAction->isChecked()) {
684 setCanvasBackground(EddPlotBackgroundColor.lighter(90));
685 setAxisScale(QwtPlot::xBottom, Lower+ BBox.right() - MaxTime, Upper + BBox.right() - MaxTime);
686 }
687 else setCanvasBackground(EddPlotBackgroundColor);
688
689 // Set footer
690 QwtText Text;
691 QFont Font;
692
693 Text = QDateTime::fromMSecsSinceEpoch(axisScaleDiv(QwtPlot::xBottom).lowerBound()).toString("d-MMM-yyyy hh:mm:ss") + " to " +
694 QDateTime::fromMSecsSinceEpoch(axisScaleDiv(QwtPlot::xBottom).upperBound()).toString("d-MMM-yyyy hh:mm:ss") +
695 (StripAction->isChecked() ? " (Stripchart)" : "");
696
697 Font.setPointSize(6);
698 Text.setFont(Font);
699 setFooter(Text); // For EddScope (not a history plot, no time scale) this action is removed
700
701 // Recalculate statistics
702 ReDoStats();
703}
704
705// Append curve to plot
706QwtPlotCurve *EddBasePlot::NewCurve(QwtText Title) {
707
708 static Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white,
709 Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan, Qt::darkCyan, Qt::magenta, Qt::darkMagenta,
710 Qt::gray, Qt::darkGray, Qt::lightGray};
711 struct PlotItem N;
712
713 N.Signal = new QwtPlotCurve(Title);
714 N.Signal->attach(this);
715 N.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
716 N.Largest = DBL_MIN;
717 N.Smallest = DBL_MAX;
718 Items.append(N);
719
720 QVariant ItemInfo = itemToInfo(N.Signal);
721 ((EddLegendLabel *) Legend->legendWidget(ItemInfo))->Curve = N.Signal;
722
723 // Context menu might delete curve and legend -> seg fault if using direct connection, as legend item deleted
724 connect(((EddLegendLabel *) Legend->legendWidget(ItemInfo)), SIGNAL(DeleteCurve(QwtPlotCurve *)), SLOT(RemoveService(QwtPlotCurve *)), Qt::QueuedConnection);
725
726 return N.Signal;
727}
728
729// Remove item
730void EddBasePlot::DeleteCurve(QwtPlotCurve *Curve) {
731
732 for (int i=0; i<Items.size(); i++) if (Items[i].Signal == Curve) {
733 delete Curve;
734 Items.takeAt(i);
735 break;
736 }
737 UpdatePlot();
738}
739
740// Clear curve data
741void EddBasePlot::ClearCurve(unsigned int Item) {
742
743 if (Item >= (unsigned int) Items.size()) return;
744
745 Items[Item].x.clear();
746 Items[Item].y.clear();
747}
748
749// Append data point
750void EddBasePlot::AddPoint(unsigned int Item, double x, double y) {
751
752 if (Item >= (unsigned int) Items.size()) return;
753
754 Items[Item].x.append(x);
755 Items[Item].y.append(y);
756
757 if (y > Items[Item].Largest) Items[Item].Largest = y;
758 if (y < Items[Item].Smallest) Items[Item].Smallest = y;
759}
760
761// Rescale plot in case selection has been made outside the canvas
762void EddBasePlot::MouseSelection(const QPolygon &P_integer) {
763
764 QwtInterval xPlot, xMouse, yPlot, yMouse;
765 QPolygonF P(P_integer);
766
767 // Shift selected rectangle so that upper left corner is 0/0 on canvas
768 QRectF R = P.boundingRect().translated(-plotLayout()->canvasRect().topLeft());
769
770 // Current axis intervals
771 xPlot = axisScaleDiv(QwtPlot::xBottom).interval();
772 yPlot = axisScaleDiv(QwtPlot::yLeft).interval();
773
774 // Selected axis intervals
775 xMouse = QwtInterval(invTransform(QwtPlot::xBottom, R.left()),
776 invTransform(QwtPlot::xBottom, R.right()));
777 yMouse = QwtInterval(invTransform(QwtPlot::yLeft, R.bottom()),
778 invTransform(QwtPlot::yLeft, R.top()));
779
780 // Selection region outside all axis?
781 if (R.right() < 0 && R.top() > plotLayout()->canvasRect().height()) return;
782
783 // If selected rectangle completely inside canvas taken care of by zoomer
784 if (plotLayout()->canvasRect().contains(P.boundingRect())) return;
785
786 // Rescale both axis if selected rectangle encompasses canvas completely
787 if (P.boundingRect().contains(plotLayout()->canvasRect())) {
788 yMouse.setMaxValue(yMouse.maxValue() + yPlot.width());
789 yMouse.setMinValue(yMouse.minValue() - yPlot.width());
790 xMouse.setMinValue(xMouse.minValue() - xPlot.width());
791 xMouse.setMaxValue(xMouse.maxValue() + xPlot.width());
792
793 setAxisScale(QwtPlot::xBottom, xMouse.minValue(), xMouse.maxValue());
794 setAxisScale(QwtPlot::yLeft, yMouse.minValue(), yMouse.maxValue());
795 }
796
797 // Rescale y axis (increase range if selected rectangle larger than axis area)
798 if (R.right() < 0) {
799 if (yMouse.maxValue() > axisScaleDiv(QwtPlot::yLeft).upperBound()) {
800 yMouse.setMaxValue(yMouse.maxValue() + yPlot.width());
801 }
802 if (yMouse.minValue() < axisScaleDiv(QwtPlot::yLeft).lowerBound()) {
803 yMouse.setMinValue(yMouse.minValue() - yPlot.width());
804 }
805
806 setAxisScale(QwtPlot::yLeft, yMouse.minValue(), yMouse.maxValue());
807 }
808
809 // Rescale x axis (increase range if selected rectangle larger than axis area)
810 if (R.top() > plotLayout()->canvasRect().height()) {
811 if (xMouse.maxValue() > axisScaleDiv(QwtPlot::xBottom).upperBound()) {
812 xMouse.setMaxValue(xMouse.maxValue() + xPlot.width());
813 }
814 if (xMouse.minValue() < axisScaleDiv(QwtPlot::xBottom).lowerBound()) {
815 xMouse.setMinValue(xMouse.minValue() - xPlot.width());
816 }
817
818 setAxisScale(QwtPlot::xBottom, xMouse.minValue(), xMouse.maxValue());
819 }
820
821 // Draw new scales
822 replot();
823}
824
825// Reset graph axes to autoscale when fully unzoomed
826void EddBasePlot::HandleZoom(const QRectF &) {
827
828 if(Zoomer->zoomRectIndex() == 0) {
829 setAxisAutoScale(QwtPlot::xBottom);
830 setAxisAutoScale(QwtPlot::yLeft);
831 }
832
833 UpdatePlot();
834}
835
836// Mouse events: Release click highlights curve if mouse did not move (which is zoom)
837void EddBasePlot::mousePressEvent(QMouseEvent *) {
838
839 MouseMoved = false;
840}
841
842void EddBasePlot::mouseMoveEvent(QMouseEvent *) {
843
844 MouseMoved = true;
845}
846
847void EddBasePlot::mouseReleaseEvent(QMouseEvent *Event) {
848
849 double Dist, MinDistance = std::numeric_limits<double>::infinity();
850 int Index = -1;
851
852 if (Event->button() != Qt::LeftButton || MouseMoved) return;
853
854 // Check which curve is closest
855 for (int i=0; i<Items.size(); i++) {
856 if (Items[i].Signal->closestPoint(canvas()->mapFromGlobal(QCursor::pos()), &Dist) != -1 && Dist < MinDistance) {
857 MinDistance = Dist;
858 Index = i;
859 }
860 }
861
862 // Toggle 'thick line' action
863 if (Index != -1) {
864 ((EddLegendLabel *) Legend->legendWidget(itemToInfo(Items[Index].Signal)))->ThickLineAction->toggle();
865 }
866}
867
868// Opening context menu
869void EddBasePlot::contextMenuEvent(QContextMenuEvent *Event) {
870
871 Menu->exec(Event->globalPos());
872}
873
874// Zoom completely out
875void EddBasePlot::MenuZoomOut() {
876
877 Zoomer->zoom(0);
878 UpdatePlot();
879}
880
881// Set maximum update rate of plot
882void EddBasePlot::MenuSetUpdateRate() {
883
884 bool OK;
885 double Rate;
886
887 Rate = QInputDialog::getDouble(this, "Edd Request",
888 "Minimum period between plot updates (sec)", (double) Timer->interval()/1000, 0, std::numeric_limits<double>::max(), 2, &OK);
889 if (OK) Timer->setInterval(Rate*1000);
890}
891
892// Save data of plot as test
893void EddBasePlot::MenuSaveASCII() {
894 QString Filename = QFileDialog::getSaveFileName(this,
895 "Filename", ".", "Text files (*.txt *.ascii *.asc);;All files (*)");
896 if (Filename.length() <= 0) return;
897
898 QFile File(Filename);
899 if (!File.open(QFile::WriteOnly | QIODevice::Text | QFile::Truncate)) {
900 QMessageBox::warning(this, "Edd Message","Could not open file for writing.",QMessageBox::Ok);
901 return;
902 }
903
904 // Write x and y data for all signals to file
905 QTextStream Stream(&File);
906
907 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
908 Stream << QString("# ") + Items[ItemNo].Signal->title().text() + ".hist" << endl;
909 for (unsigned int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
910 //Stream << Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->y(i) << endl;
911 Stream << Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->sample(i).y() << endl;
912 }
913 }
914}
915
916// Print plot
917void EddBasePlot::MenuPrint() {
918
919 QPrinter *Printer = new QPrinter;
920 QPrintDialog *PrintDialog = new QPrintDialog(Printer, this);
921 if (PrintDialog->exec() == QDialog::Accepted) {
922 QPainter Painter(Printer);
923 QPixmap Pixmap = QPixmap::grabWidget(this);
924 Painter.drawPixmap(0, 0, Pixmap);
925 }
926 delete Printer; delete PrintDialog;
927}
928
929// Save plot as image
930void EddBasePlot::MenuSave() {
931
932 QString Filename = QFileDialog::getSaveFileName(this,
933 "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)");
934 if (Filename.length()>0) {
935 QPixmap Pixmap = QPixmap::grabWidget(this);
936 if(!Pixmap.save(Filename)) {
937 QMessageBox::warning(this, "Edd Message","Could not write image file.",QMessageBox::Ok);
938 remove(Filename.toAscii().data());
939 }
940 }
941}
942
943// Help text
944void EddBasePlot::MenuPlotHelp() {
945
946 QMessageBox::about(this, "Edd - Plot help",
947 "Zoom\tMouse wheel\n"
948 "\tKeys m and shift-m\n"
949 "\tSelecting region with left mouse button\n"
950 "\tMiddle button zooms out one level\n"
951 "\tSelecting a range on an axis\n"
952 "\tSelecting whole canvas\n\n"
953 "Pan\tShift and left mouse button\n\n"
954 "ESC cancels selection\n"
955 "Clicking near a curve or on legend highlights\n"
956 "Cursor keys move mouse\n\n"
957 "Statistics are calculated over the current x axis extend\n\n"
958 "Items can be added to history plot by drag&drop from\n"
959 "DIM service displays or from other plots legends");
960}
961
962
963////////////////////////////////
964// Edd Legend and LegendLabel //
965////////////////////////////////
966
967// Reimplementation to return EddLegendLabel widget
968QWidget *EddLegend::createWidget(const QwtLegendData &) const {
969
970 return new EddLegendLabel();
971}
972
973// Constructor for EddLegendLabel class
974EddLegendLabel::EddLegendLabel() {
975
976 // Context menu
977 Menu = new QMenu(this);
978 Menu->addAction("Open in new history", this, SLOT(MenuOpenHistory()));
979 Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
980
981 ThickLineAction = new QAction("Thick line", this);
982 ThickLineAction->setCheckable(true);
983 connect(ThickLineAction, SIGNAL(toggled(bool)), SLOT(MenuThickLine(bool)));
984 Menu->addAction(ThickLineAction);
985
986 Menu->addAction("Remove curve", this, SLOT(MenuRemove()));
987
988 Curve = NULL;
989}
990
991// Opening context menu
992void EddLegendLabel::contextMenuEvent(QContextMenuEvent *Event) {
993
994 Menu->exec(Event->globalPos());
995}
996
997// Handling of mouse press event: Register start position for drag
998void EddLegendLabel::mousePressEvent(QMouseEvent *Event) {
999
1000 if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
1001}
1002
1003// Handling of dragging (Drag and MimeData will be deleted by Qt)
1004void EddLegendLabel::mouseMoveEvent(QMouseEvent *Event) {
1005
1006 if ((Event->buttons() & Qt::LeftButton) == 0) return;
1007 if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
1008
1009 QString D(text().text());
1010 D.replace(':',' ');
1011
1012 QDrag *Drag = new QDrag(this);
1013 QMimeData *MimeData = new QMimeData;
1014 QByteArray Data;
1015 MimeData->setData("Edd/Service", Data.append(D));
1016 Drag->setMimeData(MimeData);
1017 Drag->exec();
1018}
1019
1020// Handling of mouse release event: Mark line
1021void EddLegendLabel::mouseReleaseEvent(QMouseEvent *Event) {
1022
1023 if (Event->button() != Qt::LeftButton) return;
1024
1025 ThickLineAction->toggle();
1026 }
1027
1028// Menu: Open history plot
1029void EddLegendLabel::MenuOpenHistory() {
1030
1031 QString D(text().text());
1032 D.replace(':',' ');
1033 QStringList A = D.split(" ");
1034
1035 OpenHistory(A[0].toAscii().data(), A[1].toInt());
1036}
1037
1038// Menu: Copy service name
1039void EddLegendLabel::MenuCopyService() {
1040
1041 QString D(text().text());
1042 D.replace(':',' ');
1043
1044 QMimeData *MimeData = new QMimeData;
1045 QByteArray Data;
1046 MimeData->setData("Edd/Service", Data.append(D));
1047 QApplication::clipboard()->setMimeData(MimeData);
1048}
1049
1050// Menu: Thick line width
1051void EddLegendLabel::MenuThickLine(bool State) {
1052
1053 if (Curve == NULL) {
1054 printf("Warning: No QwtPlotCurve set in EddLegendLabel::MenuThickLine(), programming error\n");
1055 return;
1056 }
1057
1058 // Set pxel width of curve
1059 QPen Pen = Curve->pen();
1060 Pen.setWidth(State ? 4:1);
1061 Curve->setPen(Pen);
1062 Curve->plot()->replot();
1063
1064 // Highlight legend entry
1065 QFont Font = font();
1066 Font.setWeight(State ? QFont::Bold:QFont::Normal);
1067 setFont(Font);
1068}
1069
1070// Menu: Normal line width
1071void EddLegendLabel::MenuRemove() {
1072
1073 emit(DeleteCurve(Curve));
1074}
1075
1076//////////////////////////////////////
1077// History text box for DIM service //
1078//////////////////////////////////////
1079
1080// Constructor
1081EddText::EddText(QString Name, bool Pure, QWidget *P):
1082 QTextEdit(P), Name(Name), Pure(Pure) {
1083
1084 // Widget properties
1085 setReadOnly(true);
1086 setAttribute(Qt::WA_DeleteOnClose);
1087 setAutoFillBackground(true);
1088 document()->setMaximumBlockCount(1000);
1089 Accumulate = true;
1090
1091 // Get history for this service
1092 if (!Pure) {
1093 struct EddDim::HistItem Hist = Handler->GetHistory(Name);
1094
1095 for (int i=0; i<Hist.DataText.size(); i++) {
1096 moveCursor (QTextCursor::Start);
1097 insertPlainText(QString("(")+QDateTime::fromTime_t(Hist.DataText[i].first).toString()+") " +
1098 QString(Hist.DataRaw[i].second) + "\n");
1099 }
1100 }
1101
1102 // DIM client
1103 Handler->Subscribe(Name, this);
1104}
1105
1106// Destructor
1107EddText::~EddText() {
1108
1109 Handler->Unsubscribe(Name, this);
1110}
1111
1112
1113// Update widget (must happen in GUI thread)
1114void EddText::Update(const QString &, int Time, const QByteArray &, const QString &Format, const QString &Text, int) {
1115
1116 QPalette Pal = palette();
1117
1118 // Check if service available
1119 if (!SetStatus(this, Name, Time, Format)) {
1120 Pal.setColor(QPalette::Base, Qt::lightGray);
1121 setPalette(Pal);
1122 setText("n/a");
1123 return;
1124 }
1125
1126 Pal.setColor(QPalette::Base, Qt::white);
1127 setPalette(Pal);
1128 QDateTime Timex = QDateTime::fromTime_t(Time);
1129
1130 // Clear display in case text should not accumulate
1131 if (Accumulate == false) clear();
1132
1133 if (!Pure) {
1134 moveCursor(QTextCursor::Start);
1135 insertPlainText(QString("(")+Timex.toString()+QString(") "));
1136 insertPlainText(Text + "\n");
1137 }
1138 else if (Format == "C") insertPlainText(Text);
1139}
1140
1141
1142/////////////////////////////
1143// Interface to Dim system //
1144/////////////////////////////
1145EddDim::EddDim() {
1146
1147 Volume = 0;
1148 Period = 10;
1149 Mutex = new QMutex(QMutex::Recursive);
1150
1151 // Timer to calculate data rates
1152 QTimer *Timer = new QTimer(this);
1153 Timer->connect(Timer, SIGNAL(timeout()), this, SLOT(UpdateStatistics()));
1154 Timer->start(Period*1000);
1155
1156 // Connect to DIM handler
1157 if (connect(this, SIGNAL(INT(QString, int, QByteArray, QString)), SLOT(Update(QString, int, QByteArray, QString))) == false) {
1158 printf("Failed connection in EddDim()\n");
1159 }
1160}
1161
1162// Destructor
1163EddDim::~EddDim() {
1164
1165 delete Mutex;
1166}
1167
1168// Subscribe to DIM service
1169void EddDim::Subscribe(QString Name, class EddWidget *Instance, int Index) {
1170
1171 // Lock before accessing list
1172 QMutexLocker Locker(Mutex);
1173
1174 // Check if already subscribed to service
1175 if (ServiceList.contains(Name)) {
1176 ServiceList[Name].Subscribers.append(QPair<class EddWidget *, int>(Instance, Index));
1177
1178 if (Index>=0 && Index<ServiceList[Name].Items.size()) {
1179 Instance->Update(Name, ServiceList[Name].TimeStamp, ServiceList[Name].ByteArray, ServiceList[Name].Format, ServiceList[Name].Items[Index]);
1180 }
1181 else Instance->Update(Name, ServiceList[Name].TimeStamp, ServiceList[Name].ByteArray, ServiceList[Name].Format, ServiceList[Name].Text);
1182
1183 return;
1184 }
1185
1186 // Create new entry in service list
1187 ServiceList[Name].ByteArray = QByteArray();
1188 ServiceList[Name].TimeStamp = -1;
1189 ServiceList[Name].Subscribers.append(QPair<class EddWidget *, int>(Instance, Index));
1190 ServiceList[Name].DIMService = new DimStampedInfo(Name.toAscii().data(), INT_MAX, NO_LINK, this);
1191}
1192
1193
1194// Unsubscribe from DIM service
1195void EddDim::Unsubscribe(QString Name, class EddWidget *Instance, int Index) {
1196
1197 // Lock before accessing list
1198 QMutexLocker Locker(Mutex);
1199
1200 if (!ServiceList.contains(Name)) return;
1201
1202 QPair<class EddWidget *, int> P(Instance, Index);
1203
1204 if (ServiceList[Name].Subscribers.contains(P)) {
1205 ServiceList[Name].Subscribers.removeAt(ServiceList[Name].Subscribers.indexOf(P));
1206 }
1207
1208 // If no more needed, drop DIM subsription
1209 if (ServiceList[Name].Subscribers.isEmpty()) {
1210 delete ServiceList[Name].DIMService;
1211 ServiceList.remove(Name);
1212 }
1213}
1214
1215// Ignore service in update
1216void EddDim::Ignore(QString Name, bool Ignore) {
1217
1218 QMutexLocker Locker(&IgnoreMutex);
1219 IgnoreMap[Name] = Ignore;
1220}
1221
1222// Get history buffer
1223struct EddDim::HistItem EddDim::GetHistory(QString Name) {
1224
1225 // History already available?
1226 if (HistoryList.contains(Name)) return HistoryList[Name];
1227
1228 // Create history class to retrieve history data
1229 const struct EvidenceHistory::Item *R;
1230 class EvidenceHistory *Hist = new EvidenceHistory(Name.toStdString());
1231 Hist->GetHistory();
1232 HistoryList[Name].Time = time(NULL);
1233 HistoryList[Name].Format = Hist->GetFormat();
1234
1235 while ((R = Hist->Next()) != NULL) {
1236 HistoryList[Name].DataRaw.push_back(QPair<int, QByteArray>(R->Time, QByteArray(R->Data, R->Size)));
1237 HistoryList[Name].DataText.push_back(QPair<int, QStringList>(R->Time, QString::fromStdString(EvidenceServer::ToString(Hist->GetFormat(), R->Data, R->Size)).split(' ')));
1238 }
1239
1240 delete Hist;
1241
1242 return HistoryList[Name];
1243}
1244
1245
1246// Update throughput statistics and clear up history memory
1247void EddDim::UpdateStatistics() {
1248
1249 // Lock before accessing internal variables
1250 QMutexLocker Locker(Mutex);
1251
1252 // Remove unused histories after not less than 5 seconds
1253 QList<QString> L = HistoryList.keys();
1254 for(int i=0; i<L.size(); i++) {
1255 if ((time(NULL)-HistoryList[L[i]].Time) > 60) HistoryList.remove(L[i]);
1256 }
1257
1258 float Rate = Volume/1024.0/Period;
1259 Volume = 0;
1260
1261 // No unlock because Mutex is recursive
1262 Update("Edd/Rate_kBSec", time(NULL), QByteArray((char *) &Rate, sizeof(Rate)), "F");
1263}
1264
1265
1266// Store service information for usage by Subscribe(), update statistics and emit signal to widgets
1267void EddDim::Update(QString Name, int Time, QByteArray Data, QString Format) {
1268
1269 // Lock before accessing list
1270 QMutexLocker Locker(Mutex);
1271
1272 Volume += Data.size();
1273
1274 // Store service data and update all subscribers
1275 if (ServiceList.contains(Name)) {
1276 ServiceList[Name].TimeStamp = Time;
1277 ServiceList[Name].ByteArray = Data;
1278 ServiceList[Name].Format = Format;
1279 ServiceList[Name].Text = QString::fromStdString(EvidenceServer::ToString(Format.toAscii().data(), Data.data(), Data.size()));
1280 ServiceList[Name].Items = ServiceList[Name].Text.split(" ");
1281
1282 for (int i=0; i<ServiceList[Name].Subscribers.size(); i++) {
1283 QPair<class EddWidget *, int> P = ServiceList[Name].Subscribers[i];
1284
1285 if (P.second >=0 && P.second < ServiceList[Name].Items.size()) {
1286 P.first->Update(Name, Time, Data, Format, ServiceList[Name].Items[P.second], P.second);
1287 }
1288 else P.first->Update(Name, Time, Data, Format, ServiceList[Name].Text, P.second);
1289 }
1290 }
1291}
1292
1293// Handling of DIM service update (Data asynchronouly send to EddDim::Update())
1294void EddDim::infoHandler() {
1295
1296 QMutexLocker Locker(&IgnoreMutex);
1297 bool Ignore = IgnoreMap[getInfo()->getName()];
1298 Locker.unlock();
1299
1300 if (!EvidenceServer::ServiceOK(getInfo())) INT(getInfo()->getName(), -1);
1301 else if (!Ignore) INT(getInfo()->getName(), getInfo()->getTimestamp(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), getInfo()->getFormat());
1302}
1303
1304
1305/////////////////////
1306// Open new window //
1307/////////////////////
1308
1309// Constructor
1310EddWindow::EddWindow(QString ButtonName, QString WindowName): QPushButton(ButtonName) {
1311
1312 M = new QMainWindow;
1313 M->setCentralWidget(new QWidget);
1314 M->setStatusBar(new QStatusBar(M));
1315 M->setWindowTitle(WindowName);
1316 L = new QGridLayout(M->centralWidget());
1317
1318 connect(this, SIGNAL(pressed()), M, SLOT(show()));
1319 connect(this, SIGNAL(pressed()), M, SLOT(raise()));
1320}
1321
1322// Return layout
1323QGridLayout *EddWindow::Layout() {
1324
1325 return L;
1326}
1327
1328// Return window
1329QMainWindow *EddWindow::Window() {
1330
1331 return M;
1332}
Note: See TracBrowser for help on using the repository browser.