GSI Object Oriented Online Offline (Go4) GO4-6.4.5
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"
37#include "TGo4AnalysisObjectResult.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"))
397 fileName.append(".txt");
398 QFile NewFile(fileName);
399 if (NewFile.open( QIODevice::ReadWrite | QIODevice::Append )) {
400 QTextStream t( &NewFile );
401 t << fxOutput->toPlainText() << "\n";
402 NewFile.close();
403 }
404}
405
407{
408 QString cmd = fxCmdHist->currentText();
409 if (cmd.isEmpty()) return;
410
412 if (anal) {
413 anal->ExecuteLine(cmd.toLatin1().constData());
414 go4sett->setCommandsHistoryAnalysis(fxCmdHist->getHistory(50));
415 }
416}
417
418
420{
421 QFileDialog fd(this, "Select ROOT macro for analysis task", "", "CINT Macro(*.C);;Python script(*.py)");
422 fd.setFileMode(QFileDialog::ExistingFile);
423
424 if (fd.exec() != QDialog::Accepted)
425 return;
426 QStringList flst = fd.selectedFiles();
427 if (flst.isEmpty())
428 return;
429 bool iscint = fd.selectedNameFilter().contains(".C");
430 bool ispyth = fd.selectedNameFilter().contains(".py");
431 QString cmd;
432 if (iscint) {
433 cmd = QString(".x ") + flst[0];
434 } else if (ispyth) {
435 cmd = QString("$") + flst[0];
436 } else {
437 // never come here, but anyway...
438 cmd = QString(".x ") + flst[0];
439 if (!cmd.endsWith(".C"))
440 cmd.append(".C");
441 }
442 int index = fxCmdHist->findText(cmd);
443 if (index < 0) {
444 fxCmdHist->insertItem(-1, cmd);
445 index = fxCmdHist->findText(cmd);
446 }
447 fxCmdHist->setCurrentIndex(index);
448}
449
451{
452 const QString cmd = "@PrintHistograms()";
453 int index = fxCmdHist->findText(cmd);
454 if (index < 0) {
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 fxCmdHist->insertItem(-1, cmd);
468 index = fxCmdHist->findText(cmd);
469 }
470 fxCmdHist->setCurrentIndex(index);
471 CommandSlot();
472}
473
475{
476 ServiceCall("StartEventInfo");
477}
478
480{
481 // can be called only once when window is created
482 AddLink(slot, "DebugOutput");
483 fHasLink = true;
484}
485
487{
488 // can be called only once when window is created
489 AddLink(slot, "ObjectUpdateCmd");
490 fHasLink = true;
491}
492
493void TGo4AnalysisWindow::WaitForNewObject(bool isobjectforeditor)
494{
495 fNewObjectForEditor = isobjectforeditor;
496}
497
498void TGo4AnalysisWindow::linkedObjectUpdated(const char *linkname, TObject *obj)
499{
500 if (strcmp(linkname, "ObjectUpdateCmd") == 0) {
501 TGo4AnalysisObjectResult* res = dynamic_cast<TGo4AnalysisObjectResult*>(obj);
502 if (!res) return;
504 const char *itemname = res->GetObjectFullName();
505 TClass *cl = Browser()->ItemClass(itemname);
506 if (cl) InformThatObjectCreated(itemname, cl);
507 if (!fNewObjectForEditor) EditItem(itemname);
508 fNewObjectForEditor = true;
509 }
510
511 if (strcmp(linkname, "DebugOutput") == 0) {
512
513 TList *lst = dynamic_cast<TList *> (obj);
514
515 TListIter iter(lst, kFALSE);
516
517 while (auto obj = iter()) {
518 if (obj == lst->First()) continue;
519 AppendOutputBuffer(obj->GetName());
520 AppendOutputBuffer("\n");
521 }
522 }
523}
524
526{
527 fHasLink = false;
528
529 if (!HasOutput() || (strcmp(linkname, "DebugOutput") == 0))
530 ServiceCall("CloseAnalysisWindow");
531}
532
534{
535 // store size of top widget - JAM only if not within dock window (analysis server)
536 // size of top widget will be restored when new panel is created
537 if (HasOutput())
538 go4sett->storePanelSize(parentWidget(), "AnalysisWindow");
539}
540
542{
543 e->ignore(); // destroying this would mix up the upper level management
544 QWidget *mdi = parentWidget();
545 if (mdi)
546 mdi->hide(); // instead of destroying, we just hide it when X is clicked. JAM
547}
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)
QGo4CommandsHistory * fxCmdHist
void AppendOutputBuffer(const QString &value, int prio=0)
void resizeEvent(QResizeEvent *) override
void CreateCmdLine(QHBoxLayout *)
unsigned int fiMaxOuputSize
void WorkWithUpdateObjectCmd(TGo4Slot *slot)
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)
TGo4QSettings * go4sett