GSI Object Oriented Online Offline (Go4)  GO4-5.3.2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
TGo4AnalysisWindow.cpp
Go to the documentation of this file.
1 // $Id: TGo4AnalysisWindow.cpp 1989 2017-02-24 11:45:30Z adamczew $
2 //-----------------------------------------------------------------------
3 // The GSI Online Offline Object Oriented (Go4) Project
4 // Experiment Data Processing at EE department, GSI
5 //-----------------------------------------------------------------------
6 // Copyright (C) 2000- GSI Helmholtzzentrum für Schwerionenforschung GmbH
7 // Planckstr. 1, 64291 Darmstadt, Germany
8 // Contact: http://go4.gsi.de
9 //-----------------------------------------------------------------------
10 // This software can be used under the license agreements as stated
11 // in Go4License.txt file which is part of the distribution.
12 //-----------------------------------------------------------------------
13 
14 #include "TGo4AnalysisWindow.h"
15 
16 #include "TSystem.h"
17 #include "TList.h"
18 #include "Riostream.h"
19 
20 #include "qimage.h"
21 #include "qlabel.h"
22 #include "qpushbutton.h"
23 
24 #include <QtCore/QTimer>
25 #include <QtCore/QTextStream>
26 #include <QtCore/QProcess>
27 
28 #include <QHBoxLayout>
29 #include <QGridLayout>
30 #include <QTextEdit>
31 #include <QToolTip>
32 #include <QMessageBox>
33 #include <QFileDialog>
34 #include <QToolButton>
35 #include <QDateTime>
36 #include <QString>
37 
38 #include "TGo4QSettings.h"
39 #include "TGo4AnalysisProxy.h"
40 #include "TGo4BrowserProxy.h"
42 #include "QGo4CommandsHistory.h"
43 
44 TGo4AnalysisWindow::TGo4AnalysisWindow(QWidget* parent, const char* name, bool needoutput, bool needkillbtn) :
45  QGo4Widget( parent, name)
46 {
47 
48  setCanDestroyWidget(false);
49  setAcceptDrops(false);
50  fAnalysisProcess = 0;
51  fxOutput = 0;
52  outputBuffer = "";
53  fiMaxOuputSize = 0;
54  fbShowTimestamps=kFALSE;
55  fxTimeFormat="yyyy-MM-dd·hh:mm:ss";
56  fxCmdHist = 0;
57  fHasLink = false;
58  fTerminateOnClose = false;
59 
60  fNewObjectForEditor = true;
61 
62  setWindowTitle("Analysis Terminal");
63 
64  setFont(go4sett->getTermFont());
65 
66  if (needoutput) {
67 
68  resize(700, 400);
69  setWindowIcon(QIcon(":/icons/analysiswin.png"));
70  QGridLayout* layout = new QGridLayout( this );
71  layout->setMargin(11);
72  layout->setSpacing(6);
73 
74  fxOutput = new QTextEdit();
75  fxOutput->setUndoRedoEnabled(false);
76  fxOutput->setAutoFormatting(QTextEdit::AutoNone);
77  fxOutput->setWordWrapMode(QTextOption::NoWrap);
78  fxOutput->setReadOnly(true);
79  layout->addWidget( fxOutput, 0, 0 );
80 
83 
84 
85 
86  QHBoxLayout *box1 = new QHBoxLayout();
87  box1->addWidget(new QLabel("Press enter to execute.", this), 1);
88  CreateCmdLine(box1);
89  layout->addLayout(box1, 1, 0);
90 
91  QHBoxLayout *box2 = new QHBoxLayout();
92  CreateButtons(box2, needkillbtn);
93  layout->addLayout(box2, 2, 0);
94 
96  } else {
97 
98  QHBoxLayout *box = new QHBoxLayout(this);
99 
100  CreateButtons(box, needkillbtn);
101 
102  CreateCmdLine(box);
103 
104  adjustSize();
105  }
106 }
107 
108 
110 {
113 }
114 
115 
116 void TGo4AnalysisWindow::CreateCmdLine(QHBoxLayout* box)
117 {
118  fxCmdHist = new QGo4CommandsHistory(this, "commandslist");
119  fxCmdHist->setToolTip("CINT command for analysis process. Note: '@' means 'TGo4Analysis::Instance()->' . A leading '$' will invoke Python script.");
120  connect(fxCmdHist, SIGNAL(enterPressedSingal()), this, SLOT(CommandSlot()));
121  fxCmdHist->setMinimumSize( QSize( 220, 25 ) );
122 
123  QStringList histlist = go4sett->getCommandsHistoryAnalysis();
124  fxCmdHist->addItems(histlist);
125  fxCmdHist->setEditText(QString());
126 
127  box->addWidget(fxCmdHist, HasOutput() ? 3 : 1);
128 
129  QToolButton* MacroSearch = new QToolButton( this );
130  MacroSearch->setMinimumSize( QSize( 30, 25 ) );
131  MacroSearch->setMaximumSize( QSize( 30, 25 ) );
132  MacroSearch->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
133  MacroSearch->setIcon( QIcon(":/icons/findfile.png" ) );
134  MacroSearch->setToolTip("Search root or python macro on disk.");
135  connect(MacroSearch, SIGNAL(clicked()), this, SLOT(FileDialog_Macro()));
136  box->addWidget(MacroSearch,1);
137 }
138 
139 void TGo4AnalysisWindow::CreateButtons(QHBoxLayout* box, bool needkillbtn)
140 {
141  if (needkillbtn) {
142  QToolButton* KillProcess = new QToolButton( this );
143  KillProcess->setMinimumSize( QSize( 30, 25 ) );
144  KillProcess->setMaximumSize( QSize( 30, 25 ) );
145  KillProcess->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
146  KillProcess->setIcon( QIcon( ":/icons/killanal.png" ) );
147  KillProcess->setToolTip("Apply Ctrl+C in the analysis terminal.");
148  connect(KillProcess, SIGNAL(clicked()), this, SLOT(RequestTerminate()));
149  box->addWidget(KillProcess);
150  }
151 
152  if (HasOutput()) {
153  QToolButton* ClearButton = new QToolButton( this );
154  ClearButton->setMinimumSize( QSize( 30, 25 ) );
155  ClearButton->setMaximumSize( QSize( 30, 25 ) );
156  ClearButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
157  ClearButton->setIcon( QIcon( ":/icons/clear.png" ) );
158  ClearButton->setToolTip("Clear Terminal Window.");
159  connect(ClearButton, SIGNAL(clicked()), this, SLOT(ClearAnalysisOutput()));
160  box->addItem(new QSpacerItem(1,1));
161  box->addWidget(ClearButton,1);
162 
163  QToolButton* ScrollEndButton = new QToolButton( this );
164  ScrollEndButton->setMinimumSize( QSize( 30, 25 ) );
165  ScrollEndButton->setMaximumSize( QSize( 30, 25 ) );
166  ScrollEndButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
167  ScrollEndButton->setIcon( QIcon( ":/icons/shiftdown.png" ) );
168  ScrollEndButton->setToolTip("Scroll to end of Terminal Window. (keyboard: end)");
169  connect(ScrollEndButton, SIGNAL(clicked()), this, SLOT(ScrollEndAnalysisOutput()));
170  box->addWidget(ScrollEndButton,1);
171 
172 
173 
174  }
175 
176  QToolButton* PrintHistoButton = new QToolButton( this );
177  PrintHistoButton->setMinimumSize( QSize( 30, 25 ) );
178  PrintHistoButton->setMaximumSize( QSize( 30, 25 ) );
179  PrintHistoButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
180  PrintHistoButton->setIcon( QIcon( ":/icons/hislist.png" ) );
181  PrintHistoButton->setToolTip("Print list of all histograms.");
182  connect(PrintHistoButton, SIGNAL(clicked()), this, SLOT(PrintHistograms()));
183  box->addWidget(PrintHistoButton,1);
184 
185  QToolButton* PrintConnyButton = new QToolButton( this );
186  PrintConnyButton->setMinimumSize( QSize( 30, 25 ) );
187  PrintConnyButton->setMaximumSize( QSize( 30, 25 ) );
188  PrintConnyButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
189  PrintConnyButton->setIcon( QIcon( ":/icons/condlist.png" ) );
190  PrintConnyButton->setToolTip("Print list of all conditions.");
191  connect(PrintConnyButton, SIGNAL(clicked()), this, SLOT(PrintConditions()));
192  box->addWidget(PrintConnyButton,1);
193 
194  QToolButton* PrintEventButton = new QToolButton( this );
195  PrintEventButton->setMinimumSize( QSize( 30, 25 ) );
196  PrintEventButton->setMaximumSize( QSize( 30, 25 ) );
197  PrintEventButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
198  PrintEventButton->setIcon( QIcon( ":/icons/zoom.png" ) );
199  PrintEventButton->setToolTip("Start Event Inspection panel");
200  connect(PrintEventButton, SIGNAL(clicked()), this, SLOT(PrintEvent()));
201  box->addWidget(PrintEventButton,1);
202 }
203 
205 {
206  //if (Browser()!=0)
207  // Browser()->ToggleMonitoring(0);
208  CloseMDIParentSlot(); // JAM remove top level window when changing connection from client to server
209 }
210 
211 
213 {
214  return fxOutput!=0;
215 }
216 
218 {
219  fiMaxOuputSize = sz;
220 }
221 
223 {
224  if (fxOutput == 0)
225  return;
226 
227  unsigned int buflen = outputBuffer.length();
228 
229  if (fiMaxOuputSize > 0)
230  {
231 
232  // size remaining after cut of text
233  unsigned int cutlength = fiMaxOuputSize / 2;
234 
235  if (buflen > 0)
236  {
237  unsigned int outlen = fxOutput->toPlainText().length();
238  if (buflen + outlen < fiMaxOuputSize)
239  {
240  fxOutput->append(outputBuffer);
241  //fxOutput->moveCursor(QTextCursor::End);
242  // ^JAM just for test, we dont keep this since one may want inspect history during new printouts
243  }
244  else if (buflen >= cutlength)
245  {
246  outputBuffer.remove(0, buflen - cutlength);
247  fxOutput->setText(outputBuffer);
248  fxOutput->moveCursor(QTextCursor::End);
249  }
250  else
251  {
252  QString curr = fxOutput->toPlainText();
253  curr.remove(0, cutlength - buflen);
254  curr += outputBuffer;
255  fxOutput->setText(curr);
256  fxOutput->moveCursor(QTextCursor::End);
257  }
258  }
259  }
260  else
261  {
262  if (buflen > 0)
263  {
264  fxOutput->append(outputBuffer);
265  //fxOutput->moveCursor(QTextCursor::End);
266  // ^JAM just for test, we dont keep this since one may want inspect history during new printouts
267  }
268  }
269  outputBuffer = "";
270  QTimer::singleShot(100, this, SLOT(updateTerminalOutput()));
271 }
272 
274 {
275  if (fAnalysisProcess!=0) {
276  QByteArray ba = fAnalysisProcess->readAllStandardOutput();
277  QString buf(ba);
278  AppendOutputBuffer(buf,0);
279  }
280 }
281 
282 
284 {
285  if (fAnalysisProcess!=0) {
286  QByteArray ba = fAnalysisProcess->readAllStandardError();
287  QString buf(ba);
288  AppendOutputBuffer(buf,1);
289  }
290 }
291 
292 
293 void TGo4AnalysisWindow::AddTimeStamp(QString& buf, int prio)
294 {
295  QString pre = prio>0 ? "!":"*";
296  QString format= QString("GO4-%1> [%2]\n").arg(pre).arg(fxTimeFormat);
297  buf = QDateTime::currentDateTime().toString(format)+ buf;
298 }
299 
300 
301 void TGo4AnalysisWindow::AppendOutputBuffer(const QString& value, int prio)
302 {
303  if(fbShowTimestamps || prio>1)
304  {
305  QString buf=value;
306  AddTimeStamp(buf,prio);
307  outputBuffer.append(buf);
308  }
309  else
310  {
311  outputBuffer.append(value);
312  }
313 }
314 
315 void TGo4AnalysisWindow::StartAnalysisShell(const char* text, const char* workdir, bool aschildprocess)
316 {
317  if (fAnalysisProcess!=0) delete fAnalysisProcess;
318 
319  setWindowTitle("Analysis Terminal");
320 
321  // IMPORTANT - process should be child of analysis window
322  // to be terminated when analysis window closed or Ctrl-C is pressed
323 
324  fAnalysisProcess = new QProcess(aschildprocess ? this : 0);
325  connect(fAnalysisProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(readFromStdout()));
326  connect(fAnalysisProcess, SIGNAL(readyReadStandardError()), this, SLOT(readFromStderr()));
327  if (workdir!=0) fAnalysisProcess->setWorkingDirectory(workdir);
328 
329  fAnalysisProcess->start(text);
330 
331  if (fAnalysisProcess->state() == QProcess::NotRunning) {
332  std::cerr << "Fatal error. Could not start the Analysis" << std::endl;
334  } else {
335  fTerminateOnClose = aschildprocess;
336 #ifdef WIN32
337  setWindowTitle("Analysis Terminal [with QProcess]");
338 #else
339  setWindowTitle(QString("Analysis Terminal [pid:%1]").arg((qint64)fAnalysisProcess->pid()));
340 #endif
341  }
342 }
343 
345 {
346  ServiceCall("TerminateAnalysis");
347 }
348 
350 {
351  if (fAnalysisProcess==0) return;
352  AppendOutputBuffer("\nTerminate process ... \n\n",2);
353  fAnalysisProcess->terminate();
354  if (fAnalysisProcess->state() == QProcess::Running) fAnalysisProcess->kill();
355  delete fAnalysisProcess;
356  fAnalysisProcess = 0;
357 }
358 
360 {
361  if (fxOutput!=0)
362  fxOutput->clear();
363 }
364 
366 {
367  if (fxOutput!=0)
368  fxOutput->moveCursor(QTextCursor::End);
369 }
370 
371 
372 
374 {
375  if (fxOutput==0) return;
376  QFileDialog fd(this,
377  "Save analysis terminal output",
378  "", "Plain text (*.txt)");
379  fd.setFileMode( QFileDialog::AnyFile );
380  fd.setAcceptMode(QFileDialog::AcceptSave);
381 
382  if (fd.exec() != QDialog::Accepted) return;
383 
384  QStringList flst = fd.selectedFiles();
385  if (flst.isEmpty()) return;
386  QString fileName = flst[0];
387 
388  if(!fileName.endsWith(".txt")) fileName.append(".txt");
389  QFile NewFile(fileName);
390  NewFile.open( QIODevice::ReadWrite | QIODevice::Append );
391  QTextStream t( &NewFile );
392  t << fxOutput->toPlainText() << "\n";
393  NewFile.close();
394 }
395 
397 {
398  QString cmd = fxCmdHist->currentText();
399  if (cmd.length()==0) return;
400 
401  TGo4ServerProxy* anal = GetAnalysis();
402  if (anal!=0) {
403  anal->ExecuteLine(cmd.toLatin1().constData());
405  }
406 }
407 
408 
410 {
411  QFileDialog fd( this,
412  "Select ROOT macro for analysis task",
413  "", "CINT Macro(*.C);;Python script(*.py)");
414  fd.setFileMode( QFileDialog::ExistingFile);
415 
416  if (fd.exec() != QDialog::Accepted) return;
417  //std::cout <<"accepted fd.exec " << std::endl;
418  QStringList flst = fd.selectedFiles();
419  if (flst.isEmpty()) return;
420  //std::cout <<"Got file name "<< flst[0].toLatin1().constData() << std::endl;
421  bool iscint = fd.selectedNameFilter().contains(".C");
422  bool ispyth = fd.selectedNameFilter().contains(".py");
423  QString cmd;
424  if(iscint)
425  {
426  cmd = QString(".x ") + flst[0];
427  }
428  else if(ispyth)
429  {
430  cmd = QString("$") + flst[0];
431  }
432  else
433  {
434  // never come here, but anyway...
435  cmd = QString(".x ") + flst[0];
436  if(!cmd.endsWith(".C")) cmd.append(".C");
437  }
438  int index=fxCmdHist->findText(cmd);
439  if(index<0)
440  {
441  fxCmdHist->insertItem(-1,cmd);
442  index=fxCmdHist->findText(cmd);
443  }
444  //std::cout <<"inserted item "<< cmd.toLatin1().constData() << std::endl;
445  fxCmdHist->setCurrentIndex(index);
446 
447 }
448 
450 {
451  const QString cmd = "@PrintHistograms()";
452  int index=fxCmdHist->findText(cmd);
453  if(index<0)
454  {
455  fxCmdHist->insertItem(-1,cmd);
456  index=fxCmdHist->findText(cmd);
457  }
458  fxCmdHist->setCurrentIndex(index);
459  CommandSlot();
460 }
461 
463 {
464  const QString cmd = "@PrintConditions()";
465  int index=fxCmdHist->findText(cmd);
466  if(index<0)
467  {
468  fxCmdHist->insertItem(-1,cmd);
469  index=fxCmdHist->findText(cmd);
470  }
471  fxCmdHist->setCurrentIndex(index);
472  CommandSlot();
473 }
474 
476 {
477  ServiceCall("StartEventInfo");
478 }
479 
481 {
482  // can be called only once when window is created
483  AddLink(slot, "DebugOutput");
484  fHasLink = true;
485 }
486 
488 {
489  // can be called only once when window is created
490  AddLink(slot, "ObjectUpdateCmd");
491  fHasLink = true;
492 }
493 
494 void TGo4AnalysisWindow::WaitForNewObject(bool isobjectforeditor)
495 {
496  fNewObjectForEditor = isobjectforeditor;
497 }
498 
499 void TGo4AnalysisWindow::linkedObjectUpdated(const char* linkname, TObject* obj)
500 {
501  if (strcmp(linkname, "ObjectUpdateCmd")==0) {
502  TGo4AnalysisObjectResult* res = dynamic_cast<TGo4AnalysisObjectResult*>(obj);
503  if (res==0) return;
505  const char* itemname = res->GetObjectFullName();
506  TClass* cl = Browser()->ItemClass(itemname);
507  if (cl!=0) InformThatObjectCreated(itemname, cl);
508  if (!fNewObjectForEditor) EditItem(itemname);
509  fNewObjectForEditor = true;
510  }
511 
512  if (strcmp(linkname, "DebugOutput")==0) {
513 
514  TList* lst = dynamic_cast<TList*> (obj);
515 
516  TListIter iter(lst, kFALSE);
517  TObject* obj = 0;
518 
519  while ((obj = iter()) != 0) {
520  if (obj == lst->First()) continue;
521  AppendOutputBuffer(obj->GetName());
522  AppendOutputBuffer("\n");
523  }
524  }
525 }
526 
527 void TGo4AnalysisWindow::linkedObjectRemoved(const char* linkname)
528 {
529  fHasLink = false;
530 
531  if (!HasOutput() || (strcmp(linkname, "DebugOutput")==0))
532  ServiceCall("CloseAnalysisWindow");
533 }
534 
535 void TGo4AnalysisWindow::resizeEvent(QResizeEvent * e)
536 {
537  // store size of top widget - JAM only if not within dock window (analysis server)
538  // size of top widget will be restored when new panel is created
539  if(HasOutput())
540  go4sett->storePanelSize(parentWidget(), "AnalysisWindow");
541 }
542 
543 void TGo4AnalysisWindow::closeEvent(QCloseEvent* e)
544 {
545  e->ignore(); // destroying this would mix up the upper level management
546  QWidget* mdi = parentWidget();
547  if (mdi==0) return;
548  mdi->hide(); // instead of destroying, we just hide it when X is clicked. JAM
549 }
550 
QStringList getCommandsHistoryAnalysis()
void setCommandsHistoryAnalysis(const QStringList &commands)
void CreateCmdLine(QHBoxLayout *)
QStringList getHistory(int max=50)
const char * GetObjectFullName() const
void AddTimeStamp(QString &buf, int prio=0)
QGo4CommandsHistory * fxCmdHist
QString getTermTimeFormat()
void storePanelSize(QWidget *w, const QString &kind="ViewPanel")
virtual void resizeEvent(QResizeEvent *)
void setCanDestroyWidget(bool on=true)
Definition: QGo4Widget.h:198
TClass * ItemClass(const char *name)
void StartAnalysisShell(const char *cmd, const char *workdir=0, bool aschildprocess=false)
virtual void closeEvent(QCloseEvent *e)
TGo4BrowserProxy * Browser()
Definition: QGo4Widget.cpp:223
void EditItem(const QString &itemname)
Definition: QGo4Widget.cpp:351
TGo4ServerProxy * GetAnalysis(const char *itemname=0)
Definition: QGo4Widget.cpp:390
TGo4AnalysisWindow(QWidget *parent=0, const char *name=0, bool needoutput=false, bool needkillbtn=true)
void WorkWithDebugOutput(TGo4Slot *slot)
virtual void ExecuteLine(const char *line)
bool getTermShowTimestamp()
void ServiceCall(const char *name, void *par=0)
Definition: QGo4Widget.cpp:402
TGo4QSettings * go4sett
void WorkWithUpdateObjectCmd(TGo4Slot *slot)
void InformThatObjectCreated(const char *itemname, TClass *cl)
Definition: QGo4Widget.cpp:288
virtual void linkedObjectUpdated(const char *linkname, TObject *obj)
virtual void linkedObjectRemoved(const char *linkname)
unsigned int fiMaxOuputSize
void AddLink(const char *itemname, const char *linkname)
Definition: QGo4Widget.cpp:121
void AppendOutputBuffer(const QString &value, int prio=0)
void CreateButtons(QHBoxLayout *, bool)
void CloseMDIParentSlot()
Definition: QGo4Widget.cpp:78
int getTermHistorySize()
void WaitForNewObject(bool isobjectforeditor)