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