source: Evidence/Edd/Edd.cc @ 139

Last change on this file since 139 was 139, checked in by ogrimm, 11 years ago
Updates
File size: 19.2 KB
Line 
1/* ============================================================
2
3Edd - Evidence Data Display
4
5Qt-based graphical user interface for the Evidence contron system
6
7Edd_Indicator changes its background colour in case it display
8a DIM status service
9
10December 2009, Oliver Grimm
11
12============================================================ */
13
14#include "Edd.h"
15
16Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white,
17        Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan,
18        Qt::darkCyan, Qt::magenta, Qt::darkMagenta,
19        Qt::gray, Qt::darkGray, Qt::lightGray};
20
21//////////////////////////////////////////
22// Text display for arbitary DIM service//
23//////////////////////////////////////////
24
25// Constructor
26Edd_Indicator::Edd_Indicator(QString DIMService, QWidget *P): QLineEdit(P) {
27
28  ServiceName = qstrdup(DIMService.toAscii().data());
29 
30  // Widget properties
31  setReadOnly(true);
32  setMaximumWidth(100);
33  connect(this, SIGNAL(YEP(QString)), this, SLOT(setText(QString)));
34
35  // Context menu
36  Menu = new QMenu(this);
37  Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
38
39  // DIM client
40  Data = new DimStampedInfo(ServiceName, INT_MAX, (char *) NO_LINK, this);
41}
42
43// Destructor
44Edd_Indicator::~Edd_Indicator() {
45  delete Data;
46  delete[] ServiceName;
47}
48
49// Handling of DIM service update
50void Edd_Indicator::infoHandler() {
51
52  QPalette Pal = palette(); 
53  QString S;
54
55  // Check if service available
56  if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
57    emit(YEP(QString("n/a")));
58    setStatusTip(QString("%1:  unavailable").arg(ServiceName));
59    Pal.setColor(backgroundRole(), Qt::red);
60    setPalette(Pal);
61    return;
62  }
63  Pal.setColor(backgroundRole(), Qt::white);
64
65  // Translate data into ASCII
66  char *Text = EvidenceServer::ToString(getInfo());
67
68  // If this is a status indicator, adapt background colour
69  if (getInfo()->getSize() == (int) strlen(Text)+3) {
70    switch (*((char *) getInfo()->getData() + strlen(Text) + 2)) {
71      case 0:  Pal.setColor(backgroundRole(), Qt::white); break;
72      case 1:  Pal.setColor(backgroundRole(), Qt::cyan); break;
73      case 2:  Pal.setColor(backgroundRole(), Qt::red); break;
74      case 3:  Pal.setColor(backgroundRole(), Qt::red); break;
75      default: break;
76    }
77  }
78  setPalette(Pal);
79 
80  if (Text != NULL) {
81    QTextStream(&S) << Text;
82        free(Text);
83  }
84  else QTextStream(&S) << "Cannot interpret format identifier";
85   
86  if (strlen(getInfo()->getFormat()) > 1) {
87    QTextStream(&S) << " (DIM format string longer)";
88  }
89 
90  // Trigger display update
91  emit(YEP(S));
92 
93  // Update status tip
94  QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp()); 
95  setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(ServiceName, Time.toString()).arg(getInfo()->getFormat()));
96}
97
98// Open plot if mouse release within widget
99void Edd_Indicator::mouseReleaseEvent(QMouseEvent *Event) {
100
101  if (Event->button()!=Qt::LeftButton || !contentsRect().contains(Event->pos())) return;
102
103  // Check if last history plot still open, then raise
104  foreach (QWidget *Widget, QApplication::allWidgets()) {
105    if (Widget == LastPlot) {
106      Widget->activateWindow();
107      Widget->raise();
108      return;
109    }
110  }
111
112  // If not, open new plot
113  LastPlot = new Edd_Plot(ServiceName);
114  LastPlot->show();
115}
116
117// Handling of mouse press event: Register start position for drag
118void Edd_Indicator::mousePressEvent(QMouseEvent *Event) {
119
120  if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
121}
122
123// Handling of dragging (Drag and MimeData will be deleted by Qt)
124void Edd_Indicator::mouseMoveEvent(QMouseEvent *Event) {
125
126  if ((Event->buttons() & Qt::LeftButton) == 0) return;
127  if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
128
129  QDrag *Drag = new QDrag(this);
130  QMimeData *MimeData = new QMimeData;
131  MimeData->setText(QString(ServiceName));
132  Drag->setMimeData(MimeData);
133  Drag->exec();
134}
135
136//
137// Opening context menu
138//
139void Edd_Indicator::contextMenuEvent(QContextMenuEvent *Event) {
140
141  Menu->exec(Event->globalPos());
142}
143
144// Copy service name
145void Edd_Indicator::MenuCopyService() {
146 
147  QApplication::clipboard()->setText(QString(ServiceName));
148}
149
150//////////////////////////////////
151// History plot for DIM service //
152//////////////////////////////////
153
154//
155// Constructor
156//
157Edd_Plot::Edd_Plot(QString DIMService, QWidget *P): QwtPlot(P) {
158
159  setAcceptDrops(true);
160  setAttribute(Qt::WA_DeleteOnClose);
161 
162  // Graph properties
163  QwtText XAxisTitle("Time (RJD-55000)");
164  XAxisTitle.setFont(QFont("Helvetica", 10));
165  setAxisTitle(QwtPlot::xBottom, XAxisTitle);
166  setAutoReplot(false);
167  setCanvasBackground(QColor(Qt::yellow));
168 
169  Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
170  connect(Zoomer, SIGNAL(zoomed(const QwtDoubleRect &)), this, SLOT(HandleZoom(const QwtDoubleRect &)));
171  Panner = new QwtPlotPanner(canvas());
172  Panner->setMouseButton(Qt::LeftButton, Qt::ShiftModifier);
173  Grid = new QwtPlotGrid;
174  Grid->setMajPen(QPen(Qt::gray, 0, Qt::DotLine));
175  Grid->attach(this);
176  Legend = new QwtLegend();
177  insertLegend(Legend, QwtPlot::TopLegend);
178   
179  // Threads may not call replot directly, but only through this signal
180  connect(this, SIGNAL(YEP()), this, SLOT(UpdatePlot()));
181
182  // Context menu
183  Menu = new QMenu(this);
184  YLogAction = Menu->addAction("y scale log", this, SLOT(UpdatePlot()));
185  YLogAction->setCheckable(true);
186  NormAction = Menu->addAction("Normalize", this, SLOT(UpdatePlot()));
187  NormAction->setCheckable(true);
188  StyleAction = Menu->addAction("Draw dots", this, SLOT(UpdatePlot()));
189  StyleAction->setCheckable(true);
190  Menu->addAction("Zoom out", this, SLOT(MenuZoomOut()));
191  Menu->addAction("Single trace", this, SLOT(MenuSingleTrace()));
192  Menu->addSeparator();
193  Menu->addAction("Save as ASCII", this, SLOT(MenuSaveASCII()));
194  Menu->addAction("Save plot", this, SLOT(MenuSave()));
195  Menu->addAction("Print plot", this, SLOT(MenuPrint()));
196  Menu->addSeparator();
197  Menu->addAction("Paste service", this, SLOT(MenuPasteService()));
198
199  // DIM client
200  if (!DIMService.isEmpty()) AddService(DIMService);
201}
202
203//
204// Destructor (items with parent widget are automatically deleted)
205//
206Edd_Plot::~Edd_Plot() {
207
208  for (int i=0; i<Items.size(); i++) {
209    delete Items[i].Data;
210    delete Items[i].LiveData;
211    delete Items[i].Signal;
212    delete[] Items[i].x;
213    delete[] Items[i].y;
214  } 
215  delete Grid;
216}
217
218//
219// Add history service to plot
220//
221void Edd_Plot::AddService(QString Name) {
222
223  QString HistName = Name+".hist";
224 
225  // Lock before accessing Items list
226  QMutexLocker Locker(&Mutex);
227
228  // Check if already subsribed to service
229  for (int i=0; i<Items.size(); i++) {
230    if (HistName == Items[i].Data->getName()) {
231      QMessageBox::warning(this, "Edd Message",HistName+" already present",QMessageBox::Ok);
232      return;
233    }
234  } 
235
236  // Generate new curve and subscribe to service
237  struct PlotItem New;
238  New.Signal = new QwtPlotCurve;
239  New.Signal->attach(this);
240  New.Signal->setTitle(HistName);
241  New.Signal->setPen(QColor(LineColors[Items.size()%(sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
242  New.x = NULL;
243  New.y = NULL;
244  New.Count = 0;
245  New.Data = new DimStampedInfo(HistName.toAscii(), NO_LINK, this); 
246  New.LiveData = new DimStampedInfo(Name.toAscii(), NO_LINK, this); 
247
248  Items.append(New);
249}
250
251//
252// Handle update of DIM service
253//
254void Edd_Plot::infoHandler() {
255
256  // Check if service available
257  if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
258    setStatusTip(QString("%1:  unavailable").arg(getInfo()->getName()));
259    return;
260  }
261
262  // Lock before accessing Items list
263  QMutexLocker Locker(&Mutex);
264
265  // Determine which plot item this call belongs to
266  int ItemNo;
267  for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Items[ItemNo].Data == getInfo()) {
268    // This is a history service 
269    EvidenceHistoryItem *Curr = (EvidenceHistoryItem *) getInfo()->getData();
270    int Count=0, DataPoints = getInfo()->getSize()/sizeof(struct EvidenceHistoryItem);
271
272    delete[] Items[ItemNo].x;
273    delete[] Items[ItemNo].y;
274    Items[ItemNo].x = new int [DataPoints];
275    Items[ItemNo].y = new double [DataPoints];
276
277    // Find oldest item
278    int Oldest;
279    for (Oldest=1; Oldest<DataPoints; Oldest++) {
280      if (Curr[Oldest].Seconds < Curr[Oldest-1].Seconds) break;
281    }
282
283    // Plot data starting with oldest value
284    for (int i=0; i<DataPoints; i++) {
285      Items[ItemNo].x[Count] = Curr[(i+Oldest)%DataPoints].Seconds;
286      Items[ItemNo].y[Count] = Curr[(i+Oldest)%DataPoints].Value;
287      if (Items[ItemNo].x[Count] != 0) Count++;
288    }
289
290    // Find smallest and largest item
291    double Smallest = DBL_MAX, Largest = DBL_MIN;
292    for (int i=0; i<Count; i++) {
293      if (Largest < Items[ItemNo].y[i]) Largest = Items[ItemNo].y[i];
294      if (Smallest > Items[ItemNo].y[i]) Smallest = Items[ItemNo].y[i];
295    }
296    Items[ItemNo].Smallest = Smallest;
297    Items[ItemNo].Largest = Largest;
298    Items[ItemNo].Count = Count;
299
300    // Clear local history
301    Items[ItemNo].Live.clear();
302
303    // Update status tip
304    QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp()); 
305    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(getInfo()->getName(), Time.toString()).arg(getInfo()->getFormat()));
306
307  } else if (Items[ItemNo].LiveData == getInfo()) {
308    // This is a live service
309
310    // Limit size of list
311    if (Items[ItemNo].Live.size() > 1000) Items[ItemNo].Live.removeFirst();
312
313    // Append data
314    struct EvidenceHistoryItem Data;
315    Data.Seconds = getInfo()->getTimestamp();
316    Data.Value = atof(EvidenceServer::ToString(getInfo()));           
317    Items[ItemNo].Live.append(Data);
318   
319    // Update largest and smallest value   
320    if (Data.Value > Items[ItemNo].Largest) Items[ItemNo].Largest = Data.Value;
321    if (Data.Value < Items[ItemNo].Smallest) Items[ItemNo].Smallest = Data.Value;   
322  }
323
324  Locker.unlock();
325
326  // Do not call replot() directly from this thread!
327  emit(YEP());
328}
329
330
331//
332// Update all curves in plot
333//
334void Edd_Plot::UpdatePlot() {
335
336  if (!YLogAction->isChecked()) {
337    setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
338  }
339  else setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine);
340
341  // Lock before accessing Items list
342  QMutexLocker Locker(&Mutex);
343
344  for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
345
346    if (Items[ItemNo].Count == 0) continue;
347
348    if (StyleAction->isChecked()) Items[ItemNo].Signal->setStyle(QwtPlotCurve::Dots);
349    else Items[ItemNo].Signal->setStyle(QwtPlotCurve::Lines);
350
351    int DataPoints = Items[ItemNo].Count + Items[ItemNo].Live.size();     
352    double *x = new double [DataPoints];
353    double *y = new double [DataPoints];
354   
355    // Adapt time scale and normalize y scale if requested
356    for (int i=0; i<DataPoints; i++) {
357      if (i < Items[ItemNo].Count) {
358        x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55000;
359        y[i] = Items[ItemNo].y[i];
360      }
361      else {
362        x[i]= Items[ItemNo].Live[i-Items[ItemNo].Count].Seconds / 86400.0 + 40587.5 - 55000;
363        y[i] = Items[ItemNo].Live[i-Items[ItemNo].Count].Value;
364      }
365
366      if (NormAction->isChecked()) {
367        if (Items[ItemNo].Smallest != Items[ItemNo].Largest) y[i] = (y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
368        else y[i] = 1;
369      } 
370    }
371
372    // Plot datas
373    Items[ItemNo].Signal->setData(x, y, DataPoints);
374    Items[ItemNo].Signal->show();
375    Zoomer->setZoomBase(Items[ItemNo].Signal->boundingRect());
376
377    delete[] x;
378    delete[] y;
379  }
380  replot();
381}
382
383//
384// Reset graph axes to autoscale when fully unzoomed
385//
386void Edd_Plot::HandleZoom(const QwtDoubleRect &) {
387
388  if(Zoomer->zoomRectIndex() == 0) {
389    setAxisAutoScale(QwtPlot::xBottom);
390    setAxisAutoScale(QwtPlot::yLeft);
391  }
392}
393
394//
395// Drag and drop methods
396//
397
398void Edd_Plot::dragEnterEvent(QDragEnterEvent *Event) {
399   
400  if (Event->mimeData()->hasFormat("text/plain")) Event->acceptProposedAction();
401}
402
403void Edd_Plot::dropEvent(QDropEvent *Event) {
404
405  AddService(Event->mimeData()->text().toAscii().data());
406}
407   
408//
409// Opening context menu
410//
411void Edd_Plot::contextMenuEvent(QContextMenuEvent *Event) {
412
413  Menu->exec(Event->globalPos());
414}
415
416void Edd_Plot::MenuZoomOut() {
417
418  Zoomer->zoom(0);
419  UpdatePlot();
420}
421
422// Remove all items except last
423void Edd_Plot::MenuSingleTrace() {
424
425  // Lock before accessing Items list
426  QMutexLocker Locker(&Mutex);
427
428  while (Items.size() > 1) { 
429    delete Items.last().Data;
430    delete Items.last().LiveData;
431    delete Items.last().Signal;
432    delete[] Items.last().x;
433    delete[] Items.last().y;
434    Items.takeLast();
435  }
436 
437  Locker.unlock();
438  UpdatePlot();
439}
440
441// Save data of plot as test
442void Edd_Plot::MenuSaveASCII() {
443  QString Filename = QFileDialog::getSaveFileName(this,
444     "Filename", ".", "Text files (*.txt *.ascii *.asc);;All files (*)");
445  if (Filename.length() <= 0) return;
446 
447  QFile File(Filename);
448  if (!File.open(QFile::WriteOnly | QIODevice::Text | QFile::Truncate)) {
449    QMessageBox::warning(this, "Edd Message","Could not open file for writing.",QMessageBox::Ok);
450    return;
451  }
452 
453  // Lock before accessing Items list
454  QMutexLocker Locker(&Mutex);
455  QTextStream Stream(&File);
456
457   // Write x and y data for all signals to file
458  for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
459    Stream << QString("# ")+Items[ItemNo].Data->getName() << endl;
460    for (int i=0; i<Items[ItemNo].Signal->dataSize(); i++) {
461      Stream << (int) Items[ItemNo].x[i] << " " << Items[ItemNo].Signal->y(i) << endl;
462    }
463  }
464}
465
466// Print plot
467void Edd_Plot::MenuPrint() {
468
469  QPrinter *Printer = new QPrinter;
470  QPrintDialog *PrintDialog = new QPrintDialog(Printer, this);
471  if (PrintDialog->exec() == QDialog::Accepted) {
472    QPainter Painter(Printer);
473    QPixmap Pixmap = QPixmap::grabWidget(this);
474    Painter.drawPixmap(0, 0, Pixmap);
475  }
476  delete Printer;       delete PrintDialog;
477}
478
479// Save plot as image
480void Edd_Plot::MenuSave() {
481
482  QString Filename = QFileDialog::getSaveFileName(this,
483     "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)");
484  if (Filename.length()>0) {
485    QPixmap Pixmap = QPixmap::grabWidget(this);
486    if(!Pixmap.save(Filename)) {
487      QMessageBox::warning(this, "Edd Message","Could not write image file.",QMessageBox::Ok);
488      remove(Filename.toAscii().data());
489    }
490  }
491}
492
493// Add new service by pasting name
494void Edd_Plot::MenuPasteService() {
495
496  AddService(QApplication::clipboard()->text().toAscii().data());
497}
498
499
500//
501// Main GUI (all widgets have ultimately Central as parent)
502//
503GUI::GUI() {
504
505  Edd_Indicator *Value;
506  Edd_Plot *Graph;
507  QString Text;
508 
509  // Set features of main window
510  Central = new QWidget(this);
511  setCentralWidget(Central);
512  setStatusBar(new QStatusBar(this));
513 
514   // TextBox for value
515  //Value = new Edd_Indicator((char *) "SQM/NSB", Central);
516  //Graph = new Edd_Plot((char *) "SQM/NSB", Central);
517  //Graph->AddService("BIAS/VOLT/ID00/00-000");
518 
519
520  // Clock (updated every second)
521  //Clock = new QwtAnalogClock(Central);
522  //Clock->scaleDraw()->setPenWidth(3);
523  //Clock->setLineWidth(6);
524  //Clock->setFrameShadow(QwtDial::Sunken);
525  //Clock->setGeometry(0,0,10,10);
526  //Clock->setTime();
527 
528  //QTimer *Timer = new QTimer(Clock);
529  //Timer->connect(Timer, SIGNAL(timeout()), Clock, SLOT(setCurrentTime()));
530  //Timer->start(1000);
531
532  MainWidget = new QWidget();
533  MainLayout = new QGridLayout(MainWidget);
534
535  // Layout of all widgets
536  //MainLayout->addWidget(Value, 0, 0, 1, 1);
537  //MainLayout->addWidget(Clock, 0, 1, 1, 1);
538  //MainLayout->addWidget(Graph, 1, 0, 1, 2);
539  //MainLayout->setColumnStretch(1, 10);
540 
541  //MainLayout->addWidget(Clock, 0, 1, 1, 1);
542  //MainLayout->addWidget(Graph1, 2, 0, 1, 2);
543
544  // Bias voltage page
545  BiasWidget = new QWidget();
546  BiasLayout = new QGridLayout(BiasWidget);
547  Graph = new Edd_Plot();
548  for (int i=0; i<18; i++) {
549    Text = Text.sprintf("BIAS/VOLT/ID00/00-%.3d",i);
550    Value = new Edd_Indicator(Text);
551    BiasLayout->addWidget(Value, i%9+1, 0+i/9, 1, 1);
552    Graph->AddService(Text);
553
554    Text = Text.sprintf("BIAS/VOLT/ID00/01-%.3d",i);
555    Value = new Edd_Indicator(Text);
556    BiasLayout->addWidget(Value, i%9+1, 2+i/9, 1, 1);
557    Graph->AddService(Text);
558  }
559  BiasLayout->addWidget(Graph, 0, 4, 12, 3);
560  Value = new Edd_Indicator("BIAS/Status");
561  Value->setMaximumWidth(200);
562  BiasLayout->addWidget(Value, 0, 0, 1, 3);     
563
564  // Environment page
565  EnvironmentWidget = new QWidget();
566  EnvironmentLayout = new QGridLayout(EnvironmentWidget);
567  Value = new Edd_Indicator("ARDUINO/Status");
568  Value->setMaximumWidth(200);
569  EnvironmentLayout->addWidget(Value, 0, 0, 1, 3);     
570
571  Graph = new Edd_Plot();
572  for (int i=0; i<10; i++) {
573    Text = Text.sprintf("ARDUINO/VAL%.2d", i);
574    Value = new Edd_Indicator(Text);
575    EnvironmentLayout->addWidget(Value, i%5+1, i/5, 1, 1);
576    Graph->AddService(Text);
577  }
578  EnvironmentLayout->addWidget(Graph, 0, 3, 7, 4);     
579
580  Value = new Edd_Indicator("SQM/NSB");
581  EnvironmentLayout->addWidget(Value, 6, 0, 1, 1);     
582   
583  // Tab widget
584  TabWidget = new QTabWidget(Central);
585  TabWidget->addTab(MainWidget, "&Main");
586  TabWidget->addTab(BiasWidget, "&Bias");
587  TabWidget->addTab(EnvironmentWidget, "&Environment");
588
589  // Menu bar
590  QMenu* Menu = menuBar()->addMenu("&Menu");
591  Menu->addAction("New history plot", this, SLOT(MenuNewHistory()));
592  Menu->addSeparator();
593  Menu->addAction("About", this, SLOT(MenuAbout()));
594  Menu->addSeparator();
595  QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit()));
596  QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
597} 
598   
599GUI::~GUI() {
600  delete Central;
601}
602
603void GUI::MenuAbout() {
604  QString Rev(SVN_REVISION);
605  Rev.remove(0,1).chop(1);
606 
607  QMessageBox::about(this, "About Edd","Evidence Data Display\n\n"
608    "Written by Oliver Grimm, IPP, ETH Zurich\n"
609    "This version compiled "__DATE__" ("+Rev+")\n\n"
610    "Graphical user interface implemented with Qt.\n"
611    "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n"
612    "Comments to oliver.grimm@phys.ethz.ch.");
613}
614
615// Open request for new history plot
616void GUI::MenuNewHistory() {
617
618  QStringList List;
619  char *Name, *Format;
620  int Type;
621  bool OK;
622
623  // Find all services that are not history services and sort
624  getServices("*");
625  while ((Type = getNextService(Name, Format)) != 0) {
626    if (Type==DimSERVICE && strstr(Name, ".hist")==NULL) List.append(Name);
627  }
628  List.sort();
629
630  // Open dialog and open history window
631  QString Result = QInputDialog::getItem(this, "Edd Request",
632    "Enter DIM service name", List, 0, true, &OK);
633  if (OK && !Result.isEmpty()) {
634    Result = Result.trimmed();
635    if (Result.endsWith(".hist")) Result.chop(5);
636    Edd_Plot *Plot = new Edd_Plot(Result);
637    Plot->show();
638  }
639}
640
641//---------------------------------------------------------------------
642//************************ All functions ****************************
643//-------------------------------------------------------------------
644
645// Quit application when clicking close button on window
646void GUI::closeEvent(QCloseEvent *) {
647  qApp->quit();
648}
649
650
651//---------------------------------------------------------------------
652//**************************** Main program ***************************
653//---------------------------------------------------------------------
654
655int main(int argc, char *argv[]) {
656  QApplication app(argc, argv);
657 
658  GUI MainWindow;
659  MainWindow.setGeometry(100, 100, 800, 650);
660  MainWindow.setWindowTitle("Edd - Evidence Data Display");
661  MainWindow.show();
662 
663  return app.exec();
664}
Note: See TracBrowser for help on using the repository browser.