source: fact/Evidence/GUI.cc@ 18017

Last change on this file since 18017 was 18017, checked in by daqct3, 10 years ago
Increased numerical resolution of plot export to reproduce time stamp correctly
File size: 39.0 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 Stream.setRealNumberPrecision(14);
907
908 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
909 Stream << QString("# ") + Items[ItemNo].Signal->title().text() + ".hist" << endl;
910 for (unsigned int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
911 //Stream << Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->y(i) << endl;
912 Stream << Items[ItemNo].x.at(i) << " " << Items[ItemNo].Signal->sample(i).y() << endl;
913 }
914 }
915}
916
917// Print plot
918void EddBasePlot::MenuPrint() {
919
920 QPrinter *Printer = new QPrinter;
921 QPrintDialog *PrintDialog = new QPrintDialog(Printer, this);
922 if (PrintDialog->exec() == QDialog::Accepted) {
923 QPainter Painter(Printer);
924 QPixmap Pixmap = QPixmap::grabWidget(this);
925 Painter.drawPixmap(0, 0, Pixmap);
926 }
927 delete Printer; delete PrintDialog;
928}
929
930// Save plot as image
931void EddBasePlot::MenuSave() {
932
933 QString Filename = QFileDialog::getSaveFileName(this,
934 "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)");
935 if (Filename.length()>0) {
936 QPixmap Pixmap = QPixmap::grabWidget(this);
937 if(!Pixmap.save(Filename)) {
938 QMessageBox::warning(this, "Edd Message","Could not write image file.",QMessageBox::Ok);
939 remove(Filename.toAscii().data());
940 }
941 }
942}
943
944// Help text
945void EddBasePlot::MenuPlotHelp() {
946
947 QMessageBox::about(this, "Edd - Plot help",
948 "Zoom\tMouse wheel\n"
949 "\tKeys m and shift-m\n"
950 "\tSelecting region with left mouse button\n"
951 "\tMiddle button zooms out one level\n"
952 "\tSelecting a range on an axis\n"
953 "\tSelecting whole canvas\n\n"
954 "Pan\tShift and left mouse button\n\n"
955 "ESC cancels selection\n"
956 "Clicking near a curve or on legend highlights\n"
957 "Cursor keys move mouse\n\n"
958 "Statistics are calculated over the current x axis extend\n\n"
959 "Items can be added to history plot by drag&drop from\n"
960 "DIM service displays or from other plots legends");
961}
962
963
964////////////////////////////////
965// Edd Legend and LegendLabel //
966////////////////////////////////
967
968// Reimplementation to return EddLegendLabel widget
969QWidget *EddLegend::createWidget(const QwtLegendData &) const {
970
971 return new EddLegendLabel();
972}
973
974// Constructor for EddLegendLabel class
975EddLegendLabel::EddLegendLabel() {
976
977 // Context menu
978 Menu = new QMenu(this);
979 Menu->addAction("Open in new history", this, SLOT(MenuOpenHistory()));
980 Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
981
982 ThickLineAction = new QAction("Thick line", this);
983 ThickLineAction->setCheckable(true);
984 connect(ThickLineAction, SIGNAL(toggled(bool)), SLOT(MenuThickLine(bool)));
985 Menu->addAction(ThickLineAction);
986
987 Menu->addAction("Remove curve", this, SLOT(MenuRemove()));
988
989 Curve = NULL;
990}
991
992// Opening context menu
993void EddLegendLabel::contextMenuEvent(QContextMenuEvent *Event) {
994
995 Menu->exec(Event->globalPos());
996}
997
998// Handling of mouse press event: Register start position for drag
999void EddLegendLabel::mousePressEvent(QMouseEvent *Event) {
1000
1001 if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
1002}
1003
1004// Handling of dragging (Drag and MimeData will be deleted by Qt)
1005void EddLegendLabel::mouseMoveEvent(QMouseEvent *Event) {
1006
1007 if ((Event->buttons() & Qt::LeftButton) == 0) return;
1008 if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
1009
1010 QString D(text().text());
1011 D.replace(':',' ');
1012
1013 QDrag *Drag = new QDrag(this);
1014 QMimeData *MimeData = new QMimeData;
1015 QByteArray Data;
1016 MimeData->setData("Edd/Service", Data.append(D));
1017 Drag->setMimeData(MimeData);
1018 Drag->exec();
1019}
1020
1021// Handling of mouse release event: Mark line
1022void EddLegendLabel::mouseReleaseEvent(QMouseEvent *Event) {
1023
1024 if (Event->button() != Qt::LeftButton) return;
1025
1026 ThickLineAction->toggle();
1027 }
1028
1029// Menu: Open history plot
1030void EddLegendLabel::MenuOpenHistory() {
1031
1032 QString D(text().text());
1033 D.replace(':',' ');
1034 QStringList A = D.split(" ");
1035
1036 OpenHistory(A[0].toAscii().data(), A[1].toInt());
1037}
1038
1039// Menu: Copy service name
1040void EddLegendLabel::MenuCopyService() {
1041
1042 QString D(text().text());
1043 D.replace(':',' ');
1044
1045 QMimeData *MimeData = new QMimeData;
1046 QByteArray Data;
1047 MimeData->setData("Edd/Service", Data.append(D));
1048 QApplication::clipboard()->setMimeData(MimeData);
1049}
1050
1051// Menu: Thick line width
1052void EddLegendLabel::MenuThickLine(bool State) {
1053
1054 if (Curve == NULL) {
1055 printf("Warning: No QwtPlotCurve set in EddLegendLabel::MenuThickLine(), programming error\n");
1056 return;
1057 }
1058
1059 // Set pxel width of curve
1060 QPen Pen = Curve->pen();
1061 Pen.setWidth(State ? 4:1);
1062 Curve->setPen(Pen);
1063 Curve->plot()->replot();
1064
1065 // Highlight legend entry
1066 QFont Font = font();
1067 Font.setWeight(State ? QFont::Bold:QFont::Normal);
1068 setFont(Font);
1069}
1070
1071// Menu: Normal line width
1072void EddLegendLabel::MenuRemove() {
1073
1074 emit(DeleteCurve(Curve));
1075}
1076
1077//////////////////////////////////////
1078// History text box for DIM service //
1079//////////////////////////////////////
1080
1081// Constructor
1082EddText::EddText(QString Name, bool Pure, QWidget *P):
1083 QTextEdit(P), Name(Name), Pure(Pure) {
1084
1085 // Widget properties
1086 setReadOnly(true);
1087 setAttribute(Qt::WA_DeleteOnClose);
1088 setAutoFillBackground(true);
1089 document()->setMaximumBlockCount(1000);
1090 Accumulate = true;
1091
1092 // Get history for this service
1093 if (!Pure) {
1094 struct EddDim::HistItem Hist = Handler->GetHistory(Name);
1095
1096 for (int i=0; i<Hist.DataText.size(); i++) {
1097 moveCursor (QTextCursor::Start);
1098 insertPlainText(QString("(")+QDateTime::fromTime_t(Hist.DataText[i].first).toString()+") " +
1099 QString(Hist.DataRaw[i].second) + "\n");
1100 }
1101 }
1102
1103 // DIM client
1104 Handler->Subscribe(Name, this);
1105}
1106
1107// Destructor
1108EddText::~EddText() {
1109
1110 Handler->Unsubscribe(Name, this);
1111}
1112
1113
1114// Update widget (must happen in GUI thread)
1115void EddText::Update(const QString &, int Time, const QByteArray &, const QString &Format, const QString &Text, int) {
1116
1117 QPalette Pal = palette();
1118
1119 // Check if service available
1120 if (!SetStatus(this, Name, Time, Format)) {
1121 Pal.setColor(QPalette::Base, Qt::lightGray);
1122 setPalette(Pal);
1123 setText("n/a");
1124 return;
1125 }
1126
1127 Pal.setColor(QPalette::Base, Qt::white);
1128 setPalette(Pal);
1129 QDateTime Timex = QDateTime::fromTime_t(Time);
1130
1131 // Clear display in case text should not accumulate
1132 if (Accumulate == false) clear();
1133
1134 if (!Pure) {
1135 moveCursor(QTextCursor::Start);
1136 insertPlainText(QString("(")+Timex.toString()+QString(") "));
1137 insertPlainText(Text + "\n");
1138 }
1139 else if (Format == "C") insertPlainText(Text);
1140}
1141
1142
1143/////////////////////////////
1144// Interface to Dim system //
1145/////////////////////////////
1146EddDim::EddDim() {
1147
1148 Volume = 0;
1149 Period = 10;
1150 Mutex = new QMutex(QMutex::Recursive);
1151
1152 // Timer to calculate data rates
1153 QTimer *Timer = new QTimer(this);
1154 Timer->connect(Timer, SIGNAL(timeout()), this, SLOT(UpdateStatistics()));
1155 Timer->start(Period*1000);
1156
1157 // Connect to DIM handler
1158 if (connect(this, SIGNAL(INT(QString, int, QByteArray, QString)), SLOT(Update(QString, int, QByteArray, QString))) == false) {
1159 printf("Failed connection in EddDim()\n");
1160 }
1161}
1162
1163// Destructor
1164EddDim::~EddDim() {
1165
1166 delete Mutex;
1167}
1168
1169// Subscribe to DIM service
1170void EddDim::Subscribe(QString Name, class EddWidget *Instance, int Index) {
1171
1172 // Lock before accessing list
1173 QMutexLocker Locker(Mutex);
1174
1175 // Check if already subscribed to service
1176 if (ServiceList.contains(Name)) {
1177 ServiceList[Name].Subscribers.append(QPair<class EddWidget *, int>(Instance, Index));
1178
1179 if (Index>=0 && Index<ServiceList[Name].Items.size()) {
1180 Instance->Update(Name, ServiceList[Name].TimeStamp, ServiceList[Name].ByteArray, ServiceList[Name].Format, ServiceList[Name].Items[Index]);
1181 }
1182 else Instance->Update(Name, ServiceList[Name].TimeStamp, ServiceList[Name].ByteArray, ServiceList[Name].Format, ServiceList[Name].Text);
1183
1184 return;
1185 }
1186
1187 // Create new entry in service list
1188 ServiceList[Name].ByteArray = QByteArray();
1189 ServiceList[Name].TimeStamp = -1;
1190 ServiceList[Name].Subscribers.append(QPair<class EddWidget *, int>(Instance, Index));
1191 ServiceList[Name].DIMService = new DimStampedInfo(Name.toAscii().data(), INT_MAX, NO_LINK, this);
1192}
1193
1194
1195// Unsubscribe from DIM service
1196void EddDim::Unsubscribe(QString Name, class EddWidget *Instance, int Index) {
1197
1198 // Lock before accessing list
1199 QMutexLocker Locker(Mutex);
1200
1201 if (!ServiceList.contains(Name)) return;
1202
1203 QPair<class EddWidget *, int> P(Instance, Index);
1204
1205 if (ServiceList[Name].Subscribers.contains(P)) {
1206 ServiceList[Name].Subscribers.removeAt(ServiceList[Name].Subscribers.indexOf(P));
1207 }
1208
1209 // If no more needed, drop DIM subsription
1210 if (ServiceList[Name].Subscribers.isEmpty()) {
1211 delete ServiceList[Name].DIMService;
1212 ServiceList.remove(Name);
1213 }
1214}
1215
1216// Ignore service in update
1217void EddDim::Ignore(QString Name, bool Ignore) {
1218
1219 QMutexLocker Locker(&IgnoreMutex);
1220 IgnoreMap[Name] = Ignore;
1221}
1222
1223// Get history buffer
1224struct EddDim::HistItem EddDim::GetHistory(QString Name) {
1225
1226 // History already available?
1227 if (HistoryList.contains(Name)) return HistoryList[Name];
1228
1229 // Create history class to retrieve history data
1230 const struct EvidenceHistory::Item *R;
1231 class EvidenceHistory *Hist = new EvidenceHistory(Name.toStdString());
1232 Hist->GetHistory();
1233 HistoryList[Name].Time = time(NULL);
1234 HistoryList[Name].Format = Hist->GetFormat();
1235
1236 while ((R = Hist->Next()) != NULL) {
1237 HistoryList[Name].DataRaw.push_back(QPair<int, QByteArray>(R->Time, QByteArray(R->Data, R->Size)));
1238 HistoryList[Name].DataText.push_back(QPair<int, QStringList>(R->Time, QString::fromStdString(EvidenceServer::ToString(Hist->GetFormat(), R->Data, R->Size)).split(' ')));
1239 }
1240
1241 delete Hist;
1242
1243 return HistoryList[Name];
1244}
1245
1246
1247// Update throughput statistics and clear up history memory
1248void EddDim::UpdateStatistics() {
1249
1250 // Lock before accessing internal variables
1251 QMutexLocker Locker(Mutex);
1252
1253 // Remove unused histories after not less than 5 seconds
1254 QList<QString> L = HistoryList.keys();
1255 for(int i=0; i<L.size(); i++) {
1256 if ((time(NULL)-HistoryList[L[i]].Time) > 60) HistoryList.remove(L[i]);
1257 }
1258
1259 float Rate = Volume/1024.0/Period;
1260 Volume = 0;
1261
1262 // No unlock because Mutex is recursive
1263 Update("Edd/Rate_kBSec", time(NULL), QByteArray((char *) &Rate, sizeof(Rate)), "F");
1264}
1265
1266
1267// Store service information for usage by Subscribe(), update statistics and emit signal to widgets
1268void EddDim::Update(QString Name, int Time, QByteArray Data, QString Format) {
1269
1270 // Lock before accessing list
1271 QMutexLocker Locker(Mutex);
1272
1273 Volume += Data.size();
1274
1275 // Store service data and update all subscribers
1276 if (ServiceList.contains(Name)) {
1277 ServiceList[Name].TimeStamp = Time;
1278 ServiceList[Name].ByteArray = Data;
1279 ServiceList[Name].Format = Format;
1280 ServiceList[Name].Text = QString::fromStdString(EvidenceServer::ToString(Format.toAscii().data(), Data.data(), Data.size()));
1281 ServiceList[Name].Items = ServiceList[Name].Text.split(" ");
1282
1283 for (int i=0; i<ServiceList[Name].Subscribers.size(); i++) {
1284 QPair<class EddWidget *, int> P = ServiceList[Name].Subscribers[i];
1285
1286 if (P.second >=0 && P.second < ServiceList[Name].Items.size()) {
1287 P.first->Update(Name, Time, Data, Format, ServiceList[Name].Items[P.second], P.second);
1288 }
1289 else P.first->Update(Name, Time, Data, Format, ServiceList[Name].Text, P.second);
1290 }
1291 }
1292}
1293
1294// Handling of DIM service update (Data asynchronouly send to EddDim::Update())
1295void EddDim::infoHandler() {
1296
1297 QMutexLocker Locker(&IgnoreMutex);
1298 bool Ignore = IgnoreMap[getInfo()->getName()];
1299 Locker.unlock();
1300
1301 if (!EvidenceServer::ServiceOK(getInfo())) INT(getInfo()->getName(), -1);
1302 else if (!Ignore) INT(getInfo()->getName(), getInfo()->getTimestamp(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), getInfo()->getFormat());
1303}
1304
1305
1306/////////////////////
1307// Open new window //
1308/////////////////////
1309
1310// Constructor
1311EddWindow::EddWindow(QString ButtonName, QString WindowName): QPushButton(ButtonName) {
1312
1313 M = new QMainWindow;
1314 M->setCentralWidget(new QWidget);
1315 M->setStatusBar(new QStatusBar(M));
1316 M->setWindowTitle(WindowName);
1317 L = new QGridLayout(M->centralWidget());
1318
1319 connect(this, SIGNAL(pressed()), M, SLOT(show()));
1320 connect(this, SIGNAL(pressed()), M, SLOT(raise()));
1321}
1322
1323// Return layout
1324QGridLayout *EddWindow::Layout() {
1325
1326 return L;
1327}
1328
1329// Return window
1330QMainWindow *EddWindow::Window() {
1331
1332 return M;
1333}
Note: See TracBrowser for help on using the repository browser.