source: Evidence/Edd/Edd.cc@ 138

Last change on this file since 138 was 138, checked in by ogrimm, 15 years ago
Updates
File size: 13.9 KB
Line 
1/* ============================================================
2
3Edd - Evidence Data Display
4
5Qt-based graphical user interface for the Evidence contron system
6
7December 2009, Oliver Grimm
8
9============================================================ */
10
11#include "Edd.h"
12
13Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white,
14 Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan,
15 Qt::darkCyan, Qt::magenta, Qt::darkMagenta,
16 Qt::gray, Qt::darkGray, Qt::lightGray};
17
18//////////////////////////////////////////
19// Text display for arbitary DIM service//
20//////////////////////////////////////////
21
22// Constructor
23Edd_Indicator::Edd_Indicator(char *DIMService, QWidget *P): QLineEdit(P) {
24
25 ServiceName = new char [strlen(DIMService)+1];
26 strcpy(ServiceName, DIMService);
27
28 // Widget properties
29 setReadOnly(true);
30 setMaximumWidth(100);
31 connect(this, SIGNAL(YEP(QString)), this, SLOT(setText(QString)));
32
33 // DIM client
34 Data = new DimStampedInfo(DIMService, INT_MAX, (char *) NO_LINK, this);
35}
36
37// Destructor
38Edd_Indicator::~Edd_Indicator() {
39 delete Data;
40 delete[] ServiceName;
41}
42
43// Handling of DIM service update
44void Edd_Indicator::infoHandler() {
45
46 // Check if service available
47 QPalette Pal = palette();
48 if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
49 emit(YEP(QString("n/a")));
50 setStatusTip(QString("%1: unavailable").arg(ServiceName));
51 Pal.setColor(backgroundRole(), Qt::red);
52 setPalette(Pal);
53 return;
54 }
55 Pal.setColor(backgroundRole(), Qt::white);
56 setPalette(Pal);
57
58 // Translate data into ASCII
59 char *Text = EvidenceServer::ToString(getInfo());
60 QString S;
61
62 if (Text != NULL) {
63 QTextStream(&S) << Text;
64 free(Text);
65 }
66 else QTextStream(&S) << "Cannot interpret format identifier";
67
68 if (strlen(getInfo()->getFormat()) > 1) {
69 QTextStream(&S) << " (DIM format string longer)";
70 }
71
72 // Trigger display update
73 emit(YEP(S));
74
75 // Update status tip
76 QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp());
77 setStatusTip(QString("%1: Last update %2 Format '%3'").arg(ServiceName, Time.toString()).arg(getInfo()->getFormat()));
78}
79
80// Open plot if mouse release within widget
81void Edd_Indicator::mouseReleaseEvent(QMouseEvent *Event) {
82
83 if (Event->button()==Qt::LeftButton && contentsRect().contains(Event->pos())) {
84 Edd_Plot *Graph = new Edd_Plot(ServiceName, NULL);
85 Graph->show();
86 }
87}
88
89// Handling of mouse press event: Register start position for drag
90void Edd_Indicator::mousePressEvent(QMouseEvent *Event) {
91
92 if (Event->button() == Qt::LeftButton) dragStart = Event->pos();
93}
94
95// Handling of dragging (Drag and MimeData will be deleted by Qt)
96void Edd_Indicator::mouseMoveEvent(QMouseEvent *Event) {
97
98 if ((Event->buttons() & Qt::LeftButton) == 0) return;
99 if ((Event->pos()-dragStart).manhattanLength() < QApplication::startDragDistance()) return;
100
101 QDrag *Drag = new QDrag(this);
102 QMimeData *MimeData = new QMimeData;
103 MimeData->setText(QString(ServiceName));
104 Drag->setMimeData(MimeData);
105 Drag->exec();
106}
107
108
109//////////////////////////////////
110// History plot for DIM service //
111//////////////////////////////////
112
113//
114// Constructor
115//
116Edd_Plot::Edd_Plot(char *DIMService, QWidget *P): QwtPlot(P) {
117
118 setAcceptDrops(true);
119
120 // Graph properties
121 QwtText XAxisTitle("Time (RJD-55000)");
122 XAxisTitle.setFont(QFont("Helvetica", 10));
123 setAxisTitle(QwtPlot::xBottom, XAxisTitle);
124 setAutoReplot(false);
125 setCanvasBackground(QColor(Qt::yellow));
126
127 Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
128 connect(Zoomer, SIGNAL(zoomed(const QwtDoubleRect &)), this, SLOT(HandleZoom(const QwtDoubleRect &)));
129 Panner = new QwtPlotPanner(canvas());
130 Panner->setMouseButton(Qt::LeftButton, Qt::ShiftModifier);
131 Grid = new QwtPlotGrid;
132 Grid->setMajPen(QPen(Qt::gray, 0, Qt::DotLine));
133 Grid->attach(this);
134 Legend = new QwtLegend();
135 insertLegend(Legend, QwtPlot::TopLegend);
136
137 // Threads may not call replot directly, but only through this signal
138 connect(this, SIGNAL(YEP()), this, SLOT(UpdatePlot()));
139
140 // Context menu
141 Menu = new QMenu(this);
142 YLogAction = Menu->addAction("y scale log", this, SLOT(UpdatePlot()));
143 YLogAction->setCheckable(true);
144 NormAction = Menu->addAction("Normalize", this, SLOT(UpdatePlot()));
145 NormAction->setCheckable(true);
146 Menu->addAction("Zoom out", this, SLOT(MenuZoomOut()));
147 Menu->addAction("Single trace", this, SLOT(MenuSingleTrace()));
148 Menu->addSeparator();
149 Menu->addAction("Save plot", this, SLOT(MenuSave()));
150 Menu->addAction("Print plot", this, SLOT(MenuPrint()));
151
152 // DIM client
153 AddService(DIMService);
154}
155
156//
157// Destructor (items with parent widget are automatically deleted)
158//
159Edd_Plot::~Edd_Plot() {
160
161 for (int i=0; i<Items.size(); i++) {
162 delete Items[i].Data;
163 delete Items[i].Signal;
164 delete[] Items[i].x;
165 delete[] Items[i].y;
166 }
167 delete Grid;
168}
169
170//
171// Add history service to plot
172//
173void Edd_Plot::AddService(char *DIMService) {
174
175 // Generate name of DIM service
176 QString Name(DIMService);
177 Name.append(".hist");
178
179 // Lock this thread (because infoHandler() might be called before list insertion)
180 QMutexLocker Locker(&Mutex);
181
182 // Generate new curve and subscribe to service
183 struct PlotItem New;
184 New.Signal = new QwtPlotCurve;
185 New.Signal->attach(this);
186 New.Signal->setTitle(Name);
187 New.Signal->setPen(QColor(LineColors[Items.size()%(sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
188 New.x = NULL;
189 New.y = NULL;
190 New.Data = new DimStampedInfo(Name.toAscii(), NO_LINK, this);
191
192 Items.append(New);
193}
194
195//
196// Handle update of DIM service
197//
198void Edd_Plot::infoHandler() {
199
200 // Check if service available
201 if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
202 setStatusTip(QString("%1: unavailable").arg(getInfo()->getName()));
203 return;
204 }
205
206 // Lock this thread (see AddService())
207 QMutexLocker Locker(&Mutex);
208
209 // Determine which plot item this call belongs to
210 int ItemNo;
211 for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Items[ItemNo].Data == getInfo()) break;
212 if (ItemNo == Items.size()) return; // safety check
213
214 EvidenceHistoryItem *Curr = (EvidenceHistoryItem *) getInfo()->getData();
215 int Count=0, DataPoints = getInfo()->getSize()/sizeof(struct EvidenceHistoryItem);
216
217 delete[] Items[ItemNo].x;
218 delete[] Items[ItemNo].y;
219 Items[ItemNo].x = new double [DataPoints];
220 Items[ItemNo].y = new double [DataPoints];
221
222 // Find oldest item
223 int Oldest;
224 for (Oldest=1; Oldest<DataPoints; Oldest++) {
225 if (Curr[Oldest].Seconds < Curr[Oldest-1].Seconds) break;
226 }
227
228 // Plot data starting with oldest value
229 for (int i=0; i<DataPoints; i++) {
230 Items[ItemNo].x[Count] = (double) Curr[(i+Oldest)%DataPoints].Seconds;
231 Items[ItemNo].y[Count] = Curr[(i+Oldest)%DataPoints].Value;
232 if (Items[ItemNo].x[Count] != 0) Count++;
233 }
234
235 // Find smallest and largest item
236 double Smallest = DBL_MAX, Largest = DBL_MIN;
237 for (int i=0; i<Count; i++) {
238 if (Largest < Items[ItemNo].y[i]) Largest = Items[ItemNo].y[i];
239 if (Smallest > Items[ItemNo].y[i]) Smallest = Items[ItemNo].y[i];
240 }
241 Items[ItemNo].Smallest = Smallest;
242 Items[ItemNo].Largest = Largest;
243 Items[ItemNo].Count = Count;
244
245 // Update status tip
246 QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp());
247 setStatusTip(QString("%1: Last update %2 Format '%3'").arg(getInfo()->getName(), Time.toString()).arg(getInfo()->getFormat()));
248
249 emit(YEP());
250}
251
252
253//
254// Update all curves in plot
255//
256void Edd_Plot::UpdatePlot() {
257
258 QMutexLocker Locker(&Mutex);
259
260 if (!YLogAction->isChecked()) {
261 setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine);
262 }
263 else setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine);
264
265 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
266
267 double *x = new double [Items[ItemNo].Count];
268 double *y = new double [Items[ItemNo].Count];
269
270 // Adapt time scale and normalize y scale if requested
271 for (int i=0; i<Items[ItemNo].Count; i++) {
272 x[i] = Items[ItemNo].x[i]/86400 + 40587.5 - 55000;
273 y[i] = Items[ItemNo].y[i];
274 if (NormAction->isChecked()) {
275 if (Items[ItemNo].Smallest != Items[ItemNo].Largest) y[i] = (Items[ItemNo].y[i] - Items[ItemNo].Smallest)/(Items[ItemNo].Largest-Items[ItemNo].Smallest);
276 else y[i] = 1;
277 }
278 }
279
280 // Plot datas
281 Items[ItemNo].Signal->setData(x, y, Items[ItemNo].Count);
282 Items[ItemNo].Signal->show();
283 Zoomer->setZoomBase(Items[ItemNo].Signal->boundingRect());
284
285 delete[] x;
286 delete[] y;
287 }
288 replot();
289}
290
291//
292// Reset graph axes to autoscale when fully unzoomed
293//
294void Edd_Plot::HandleZoom(const QwtDoubleRect &) {
295
296 if(Zoomer->zoomRectIndex() == 0) {
297 setAxisAutoScale(QwtPlot::xBottom);
298 setAxisAutoScale(QwtPlot::yLeft);
299 }
300}
301
302//
303// Drag and drop methods
304//
305
306void Edd_Plot::dragEnterEvent(QDragEnterEvent *Event) {
307
308 if (Event->mimeData()->hasFormat("text/plain")) Event->acceptProposedAction();
309}
310
311void Edd_Plot::dropEvent(QDropEvent *Event) {
312
313 AddService(Event->mimeData()->text().toAscii().data());
314}
315
316//
317// Context menu and its functions
318//
319void Edd_Plot::contextMenuEvent(QContextMenuEvent *Event) {
320
321 Menu->exec(Event->globalPos());
322}
323
324void Edd_Plot::MenuZoomOut() {
325
326 Zoomer->zoom(0);
327 emit YEP();
328}
329
330void Edd_Plot::MenuSingleTrace() {
331
332 QMutexLocker Locker(&Mutex);
333
334 while (Items.size() > 1) {
335 delete Items.last().Data;
336 delete Items.last().Signal;
337 delete[] Items.last().x;
338 delete[] Items.last().y;
339 Items.takeLast();
340 }
341 emit(YEP());
342}
343
344void Edd_Plot::MenuPrint() {
345
346 QPrinter *Printer = new QPrinter;
347 QPrintDialog *PrintDialog = new QPrintDialog(Printer, this);
348 if (PrintDialog->exec() == QDialog::Accepted) {
349 QPainter Painter(Printer);
350 QPixmap Pixmap = QPixmap::grabWidget(this);
351 Painter.drawPixmap(0, 0, Pixmap);
352 }
353 delete Printer; delete PrintDialog;
354}
355
356void Edd_Plot::MenuSave() {
357
358 QString Filename = QFileDialog::getSaveFileName(this,
359 "Filename of image", "/home/ogrimm/ddd", "Image files (*.bmp *.jpg *.png *.ppm *.tiff *.xbm *.xpm);;All files (*)");
360 if (Filename.length()>0) {
361 QPixmap Pixmap = QPixmap::grabWidget(this);
362 if(!Pixmap.save(Filename)) {
363 QMessageBox::warning(this, "ddd Message","Could not write image file.",QMessageBox::Ok);
364 remove(Filename.toAscii().data());
365 }
366 }
367}
368
369//
370// Main GUI
371//
372GUI::GUI() {
373
374 // Set features of main window
375 Central = new QWidget(this);
376 setCentralWidget(Central);
377 setStatusBar (new QStatusBar(this));
378
379 // TextBox for value
380 Value = new Edd_Indicator((char *) "SQM/NSB", Central);
381 Graph = new Edd_Plot((char *) "SQM/NSB", Central);
382
383 Value1 = new Edd_Indicator((char *) "BIAS/VOLT/ID00/00-000", Central);
384 //Graph1 = new Edd_Plot((char *) "BIAS/VOLT/ID00/00-000", Central);
385
386 // Clock (updated every second)
387 //Clock = new QwtAnalogClock(Central);
388 //Clock->scaleDraw()->setPenWidth(3);
389 //Clock->setLineWidth(6);
390 //Clock->setFrameShadow(QwtDial::Sunken);
391 //Clock->setGeometry(0,0,10,10);
392 //Clock->setTime();
393
394 //QTimer *Timer = new QTimer(Clock);
395 //Timer->connect(Timer, SIGNAL(timeout()), Clock, SLOT(setCurrentTime()));
396 //Timer->start(1000);
397
398 MainWidget = new QWidget();
399
400 // Layout of all widgets
401 MainLayout = new QGridLayout(MainWidget);
402 MainLayout->addWidget(Value, 0, 0, 1, 1);
403 //MainLayout->addWidget(Clock, 0, 1, 1, 1);
404 MainLayout->addWidget(Graph, 1, 0, 1, 2);
405 //MainLayout->setColumnStretch(1, 10);
406
407 MainLayout->addWidget(Value1, 0, 1, 1, 1);
408 //MainLayout->addWidget(Clock, 0, 1, 1, 1);
409 //MainLayout->addWidget(Graph1, 2, 0, 1, 2);
410
411 BiasWidget = new QWidget();
412 BiasLayout = new QGridLayout(BiasWidget);
413 for (int i=0; i<19; i++) {
414 char *Item;
415 if (asprintf(&Item, "BIAS/VOLT/ID00/00-%.3d", i) != -1) {
416 Value = new Edd_Indicator(Item, NULL);
417 BiasLayout->addWidget(Value, i%10, 0+i/10, 1, 1);
418 free(Item);
419 }
420 if (asprintf(&Item, "BIAS/VOLT/ID00/01-%.3d", i) != -1) {
421 Value = new Edd_Indicator(Item, NULL);
422 BiasLayout->addWidget(Value, i%10, 2+i/10, 1, 1);
423 free(Item);
424 }
425 }
426
427 EnvironmentWidget = new QWidget();
428 EnvironmentLayout = new QGridLayout(EnvironmentWidget);
429 for (int i=0; i<10; i++) {
430 char *Item;
431 if (asprintf(&Item, "ARDUINO/VAL%.2d", i) != -1) {
432 Value = new Edd_Indicator(Item, NULL);
433 EnvironmentLayout->addWidget(Value, i%5, i/5, 1, 1);
434 free(Item);
435 }
436 }
437
438 // Tab widget
439 TabWidget = new QTabWidget(Central);
440 TabWidget->addTab(MainWidget, "&Main");
441 TabWidget->addTab(BiasWidget, "&Bias");
442 TabWidget->addTab(EnvironmentWidget, "&Environment");
443
444 // Menu bar
445 QMenu* Menu = menuBar()->addMenu("&Menu");
446 Menu->addAction("About", this, SLOT(MenuAbout()));
447 Menu->addSeparator();
448 QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit()));
449 QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
450}
451
452GUI::~GUI() {
453 delete Central;
454}
455
456void GUI::MenuAbout() {
457 QMessageBox::about(this, "About Edd","Evidence Data Display\n\n"
458 "Written by Oliver Grimm, IPP, ETH Zurich\n"
459 "EMail: oliver.grimm@phys.ethz.ch\n"
460 "This version compiled "__DATE__".\n"
461 "subversion revision "SVN_REVISION".\n\n"
462 "Graphical user interface implemented with Qt.\n"
463 "Evidence control system based on DIM (http://dim.web.cern.ch).");
464 printf("Revision: $Revision$\n");
465}
466
467//---------------------------------------------------------------------
468//************************ All functions ****************************
469//-------------------------------------------------------------------
470
471// Quit application when clicking close button on window
472void GUI::closeEvent(QCloseEvent *) {
473 qApp->quit();
474}
475
476
477//---------------------------------------------------------------------
478//**************************** Main program ***************************
479//---------------------------------------------------------------------
480
481int main(int argc, char *argv[]) {
482 QApplication app(argc, argv);
483
484 GUI MainWindow;
485 MainWindow.setGeometry(100, 100, 800, 500);
486 MainWindow.setWindowTitle("Edd - Evidence Data Display");
487 MainWindow.show();
488
489 return app.exec();
490}
Note: See TracBrowser for help on using the repository browser.