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