source: Evidence/Edd/Edd.cc@ 139

Last change on this file since 139 was 139, checked in by ogrimm, 15 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.