615 lines
22 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "qDetectorMain.h"
#include "qDefs.h"
#include "qDrawPlot.h"
#include "qTabAdvanced.h"
#include "qTabDataOutput.h"
#include "qTabDebugging.h"
#include "qTabDeveloper.h"
#include "qTabMeasurement.h"
#include "qTabMessages.h"
#include "qTabPlot.h"
#include "qTabSettings.h"
#include "sls/ToString.h"
#include "sls/versionAPI.h"
#include <QFileDialog>
#include <QPlastiqueStyle>
#include <QResizeEvent>
#include <QScrollArea>
#include <QSizePolicy>
#include <getopt.h>
#include <string>
#include <sys/stat.h>
int main(int argc, char **argv) {
// options
std::string fname;
bool isDeveloper = false;
int64_t tempval = 0;
int multiId = 0;
// parse command line for config
static struct option long_options[] = {
// These options set a flag.
//{"verbose", no_argument, &verbose_flag, 1},
// These options dont set a flag. We distinguish them by their indices.
{"developer", no_argument, nullptr, 'd'},
{"config", required_argument, nullptr, 'f'},
{"id", required_argument, nullptr, 'i'},
{"version", no_argument, nullptr, 'v'},
{"help", no_argument, nullptr, 'h'},
{nullptr, 0, nullptr, 0}};
// getopt_long stores the option index here
optind = 1;
int option_index = 0;
int c = 0;
while (c != -1) {
c = getopt_long(argc, argv, "hvdf:i:", long_options, &option_index);
// Detect the end of the options
if (c == -1)
break;
switch (c) {
case 'f':
fname = optarg;
LOG(logDEBUG) << long_options[option_index].name << " " << optarg;
break;
case 'd':
isDeveloper = true;
break;
case 'i':
multiId = atoi(optarg);
break;
case 'v':
tempval = APIGUI;
LOG(logINFO) << "SLS Detector GUI " << GITBRANCH << " (0x"
<< std::hex << tempval << ")";
return 0;
case 'h':
default:
std::string help_message =
"\n" + std::string(argv[0]) + "\n" +
"Usage: " + std::string(argv[0]) + " [arguments]\n" +
"Possible arguments are:\n" +
"\t-d, --developer : Enables the developer tab\n" +
"\t-f, --config <fname> : Loads config from file\n" +
"\t-i, --id <i> : Sets the multi detector id to "
"i. Default: 0. Required \n" +
"\t only when more than one multi "
"detector object is needed.\n\n";
LOG(logERROR) << help_message;
return -1;
}
}
QApplication app(argc, argv);
app.setStyle(new QPlastiqueStyle); // style is deleted by QApplication
try {
qDetectorMain det(multiId, fname, isDeveloper);
det.show();
app.exec();
} catch (const std::exception &e) {
qDefs::Message(qDefs::CRITICAL,
std::string(e.what()) + "\nExiting Gui :'( ", "main");
}
return 0;
}
qDetectorMain::qDetectorMain(int multiId, const std::string &fname,
bool isDevel)
: QMainWindow(nullptr), detType(slsDetectorDefs::GENERIC),
isDeveloper(isDevel), heightPlotWindow(0), heightCentralWidget(0) {
setupUi(this);
SetUpDetector(fname, multiId);
SetUpWidgetWindow();
}
qDetectorMain::~qDetectorMain() {
disconnect(tabs, SIGNAL(currentChanged(int)), this, SLOT(Refresh(int)));
for (int i = 0; i < NumberOfTabs; ++i) {
delete scroll[i];
}
}
void qDetectorMain::SetUpWidgetWindow() {
setFont(QFont("Sans Serif", qDefs::Q_FONT_SIZE, QFont::Normal));
// plot setup
plot = new qDrawPlot(dockWidgetPlot, det.get());
LOG(logDEBUG) << "DockPlot ready";
dockWidgetPlot->setWidget(plot);
// tabs setup
tabs = new MyTabWidget(this);
layoutTabs->addWidget(tabs);
// creating all the other tab widgets
tabMeasurement = new qTabMeasurement(this, det.get(), plot);
tabDataOutput = new qTabDataOutput(this, det.get());
tabPlot = new qTabPlot(this, det.get(), plot);
tabSettings = new qTabSettings(this, det.get());
tabAdvanced = new qTabAdvanced(this, det.get(), plot);
tabDebugging = new qTabDebugging(this, det.get());
tabDeveloper = new qTabDeveloper(this, det.get());
// creating the scroll area widgets for the tabs
for (int i = 0; i < NumberOfTabs; ++i) {
scroll[i] = new QScrollArea();
scroll[i]->setFrameShape(QFrame::NoFrame);
}
// setting the tab widgets to the scrollareas
scroll[MEASUREMENT]->setWidget(tabMeasurement);
scroll[DATAOUTPUT]->setWidget(tabDataOutput);
scroll[PLOT]->setWidget(tabPlot);
scroll[SETTINGS]->setWidget(tabSettings);
scroll[ADVANCED]->setWidget(tabAdvanced);
scroll[DEBUGGING]->setWidget(tabDebugging);
scroll[DEVELOPER]->setWidget(tabDeveloper);
// inserting all the tabs
tabs->insertTab(MEASUREMENT, scroll[MEASUREMENT], "Measurement");
tabs->insertTab(DATAOUTPUT, scroll[DATAOUTPUT], "Data Output");
tabs->insertTab(PLOT, scroll[PLOT], "Plot");
tabs->insertTab(SETTINGS, scroll[SETTINGS], "Settings");
tabs->insertTab(ADVANCED, scroll[ADVANCED], "Advanced");
tabs->insertTab(DEBUGGING, scroll[DEBUGGING], "Debugging");
tabs->insertTab(DEVELOPER, scroll[DEVELOPER], "Developer");
// no scroll buttons this way
tabs->insertTab(MESSAGES, tabMessages, "Terminal");
// swap tabs so that messages is last tab
tabs->tabBar()->moveTab(tabs->indexOf(tabMeasurement), MEASUREMENT);
tabs->tabBar()->moveTab(tabs->indexOf(tabSettings), SETTINGS);
tabs->tabBar()->moveTab(tabs->indexOf(tabDataOutput), DATAOUTPUT);
tabs->tabBar()->moveTab(tabs->indexOf(tabPlot), PLOT);
tabs->tabBar()->moveTab(tabs->indexOf(tabAdvanced), ADVANCED);
tabs->tabBar()->moveTab(tabs->indexOf(tabDebugging), DEBUGGING);
tabs->tabBar()->moveTab(tabs->indexOf(tabDeveloper), DEVELOPER);
tabs->tabBar()->moveTab(tabs->indexOf(tabMessages), MESSAGES);
tabs->setCurrentIndex(MEASUREMENT);
// other tab properties
// Default tab color
defaultTabColor = tabs->tabBar()->tabTextColor(DATAOUTPUT);
// Set the current tab(measurement) to blue as it is the current one
tabs->tabBar()->setTabTextColor(0, QColor(0, 0, 200, 255));
tabs->tabBar()->setExpanding(true);
// mode setup - to set up the tabs initially as disabled, not in form so
// done here
LOG(logINFO)
<< "Dockable Mode: 0, Debug Mode: 0, Expert Mode: 0, Developer Mode: "
<< isDeveloper;
tabs->setTabEnabled(DEBUGGING, false);
tabs->setTabEnabled(ADVANCED, false);
tabs->setTabEnabled(DEVELOPER, isDeveloper);
actionLoadTrimbits->setVisible(false);
dockWidgetPlot->setFloating(false);
dockWidgetPlot->setFeatures(QDockWidget::NoDockWidgetFeatures);
// Other setup
// Height of plot and central widget
heightPlotWindow = dockWidgetPlot->size().height();
heightCentralWidget = centralwidget->size().height();
// Default zoom Tool Tip
zoomToolTip = dockWidgetPlot->toolTip();
Initialization();
}
void qDetectorMain::SetUpDetector(const std::string &config_file, int multiID) {
// instantiate detector and set window title
det = sls::make_unique<sls::Detector>(multiID);
// create messages tab to capture config file loading logs
tabMessages = new qTabMessages(this);
// loads the config file at startup
if (!config_file.empty())
LoadConfigFile(config_file);
// validate detector type (for GUI) and update menu
detType = det->getDetectorType().tsquash(
"Different detector type for all modules.");
actionLoadTrimbits->setEnabled(false);
switch (detType) {
case slsDetectorDefs::EIGER:
case slsDetectorDefs::MYTHEN3:
actionLoadTrimbits->setEnabled(true);
break;
case slsDetectorDefs::GOTTHARD:
case slsDetectorDefs::JUNGFRAU:
case slsDetectorDefs::MOENCH:
case slsDetectorDefs::GOTTHARD2:
break;
default:
std::ostringstream os;
os << det->getHostname() << " has "
<< sls::ToString(det->getDetectorType().squash())
<< " detector type (" << std::to_string(detType)
<< "). Exiting GUI.";
std::string errorMess = os.str();
throw sls::RuntimeError(errorMess.c_str());
}
std::ostringstream os;
os << "SLS Detector GUI : "
<< sls::ToString(det->getDetectorType().squash()) << " - "
<< det->getHostname();
std::string title = os.str();
LOG(logINFO) << title;
setWindowTitle(QString(title.c_str()));
}
void qDetectorMain::Initialization() {
// Dockable Plot
connect(dockWidgetPlot, SIGNAL(topLevelChanged(bool)), this,
SLOT(ResizeMainWindow(bool)));
// tabs
connect(tabs, SIGNAL(currentChanged(int)), this,
SLOT(Refresh(int))); //( QWidget*)));
// Measurement tab
connect(tabMeasurement, SIGNAL(EnableTabsSignal(bool)), this,
SLOT(EnableTabs(bool)));
connect(tabMeasurement, SIGNAL(FileNameChangedSignal(QString)), plot,
SLOT(SetSaveFileName(QString)));
// Plot tab
connect(tabPlot, SIGNAL(DisableZoomSignal(bool)), this,
SLOT(SetZoomToolTip(bool)));
// Plotting
connect(plot, SIGNAL(AcquireFinishedSignal()), tabMeasurement,
SLOT(AcquireFinished()));
connect(plot, SIGNAL(AbortSignal(QString)), tabMeasurement,
SLOT(AbortAcquire(QString)));
// menubar
// Modes Menu
connect(menuModes, SIGNAL(triggered(QAction *)), this,
SLOT(EnableModes(QAction *)));
// Utilities Menu
connect(menuUtilities, SIGNAL(triggered(QAction *)), this,
SLOT(ExecuteUtilities(QAction *)));
// Help Menu
connect(menuHelp, SIGNAL(triggered(QAction *)), this,
SLOT(ExecuteHelp(QAction *)));
}
void qDetectorMain::LoadConfigFile(const std::string &config_file) {
LOG(logINFO) << "Loading config file at start up:" << config_file;
struct stat st_buf;
QString file = QString(config_file.c_str());
// path doesnt exist
if (stat(config_file.c_str(), &st_buf)) {
qDefs::Message(
qDefs::WARNING,
std::string("<nobr>Start up configuration failed to load. The "
"following file does not exist:</nobr><br><nobr>") +
config_file,
"qDetectorMain::LoadConfigFile");
LOG(logWARNING) << "Config file does not exist";
}
// not a file
else if (!S_ISREG(st_buf.st_mode)) {
qDefs::Message(
qDefs::WARNING,
std::string(
"<nobr>Start up configuration failed to load. The following "
"file is not a recognized file format:</nobr><br><nobr>") +
config_file,
"qDetectorMain::LoadConfigFile");
LOG(logWARNING) << "File not recognized";
} else {
try {
det->loadConfig(config_file);
}
CATCH_DISPLAY("Could not load config file.",
"qDetectorMain::LoadConfigFile")
}
}
void qDetectorMain::EnableModes(QAction *action) {
bool enable;
// Set DebugMode
if (action == actionDebug) {
enable = actionDebug->isChecked();
tabs->setTabEnabled(DEBUGGING, enable);
LOG(logINFO) << "Debug Mode: " << qDefs::stringEnable(enable);
}
// Set ExpertMode(comes here only if its a digital detector)
else if (action == actionExpert) {
enable = actionExpert->isChecked();
tabs->setTabEnabled(ADVANCED, enable);
actionLoadTrimbits->setVisible(enable &&
(detType == slsDetectorDefs::EIGER ||
detType == slsDetectorDefs::MYTHEN3));
tabSettings->SetExportMode(enable);
LOG(logINFO) << "Expert Mode: " << qDefs::stringEnable(enable);
}
// Set DockableMode
else {
enable = actionDockable->isChecked();
if (enable) {
dockWidgetPlot->setFeatures(QDockWidget::DockWidgetFloatable);
} else {
dockWidgetPlot->setFloating(false);
dockWidgetPlot->setFeatures(QDockWidget::NoDockWidgetFeatures);
}
LOG(logINFO) << "Dockable Mode: " << qDefs::stringEnable(enable);
}
}
void qDetectorMain::ExecuteUtilities(QAction *action) {
bool refreshTabs = false;
try {
if (action == actionLoadConfiguration) {
LOG(logDEBUG) << "Loading Configuration";
QString fName = QString(det->getFilePath().squash("/tmp/").c_str());
fName = QFileDialog::getOpenFileName(
this, tr("Load Detector Configuration"), fName,
tr("Configuration files (*.config);;All Files(*)"));
// Gets called when cancelled as well
if (!fName.isEmpty()) {
refreshTabs = true;
det->loadConfig(std::string(fName.toAscii().constData()));
qDefs::Message(qDefs::INFORMATION,
"The Configuration Parameters have been "
"configured successfully.",
"qDetectorMain::ExecuteUtilities");
LOG(logINFO) << "Configuration Parameters loaded successfully";
}
}
else if (action == actionLoadParameters) {
LOG(logDEBUG) << "Loading Parameters";
QString fName = QString(det->getFilePath().squash("/tmp/").c_str());
fName = QFileDialog::getOpenFileName(
this, tr("Load Measurement Setup"), fName,
tr("Parameter files (*.det);;All Files(*)"));
// Gets called when cancelled as well
if (!fName.isEmpty()) {
refreshTabs = true;
det->loadParameters(std::string(fName.toAscii().constData()));
qDefs::Message(qDefs::INFORMATION,
"The Detector Parameters have been "
"configured successfully.",
"qDetectorMain::ExecuteUtilities");
LOG(logINFO) << "Parameters loaded successfully";
}
}
else if (action == actionLoadTrimbits) {
QString fName =
QString((det->getSettingsPath().squash("/tmp/")).c_str());
LOG(logDEBUG) << "Loading Trimbits";
// so that even nonexisting files can be selected
QFileDialog *fileDialog = new QFileDialog(
this, tr("Load Detector Trimbits"), fName,
tr("Trimbit files (*.trim noise.sn*);;All Files(*)"));
fileDialog->setFileMode(QFileDialog::AnyFile);
if (fileDialog->exec() == QDialog::Accepted) {
fName = fileDialog->selectedFiles()[0];
det->loadTrimbits(std::string(fName.toAscii().constData()));
qDefs::Message(qDefs::INFORMATION,
"The Trimbits have been loaded successfully.",
"qDetectorMain::ExecuteUtilities");
LOG(logINFO) << "Trimbits loaded successfully";
}
}
}
CATCH_DISPLAY("Could not execute utilities.",
"qDetectorMain::ExecuteUtilities")
Refresh(tabs->currentIndex());
if (refreshTabs) {
tabMeasurement->Refresh();
tabSettings->Refresh();
tabDataOutput->Refresh();
if (tabAdvanced->isEnabled())
tabAdvanced->Refresh();
if (tabDebugging->isEnabled())
tabDebugging->Refresh();
if (tabDeveloper->isEnabled())
tabDeveloper->Refresh();
tabPlot->Refresh();
}
}
void qDetectorMain::ExecuteHelp(QAction *action) {
if (action == actionAbout) {
LOG(logINFO) << "About Common GUI for Jungfrau, Eiger, Mythen3, "
"Gotthard, Gotthard2 and Moench detectors";
std::string guiVersion = sls::ToStringHex(APIGUI);
std::string clientVersion = "unknown";
try {
clientVersion = sls::ToStringHex(det->getClientVersion());
}
CATCH_DISPLAY("Could not get client version.",
"qDetectorMain::ExecuteHelp")
qDefs::Message(qDefs::INFORMATION,
"<p style=\"font-family:verdana;\">"
"<b>SLS Detector GUI version:&nbsp;&nbsp;&nbsp;" +
guiVersion +
"<br>SLS Detector Client version: " +
clientVersion +
"</b><br><br>"
"Common GUI to control the SLS Detectors: "
"Jungfrau, Eiger, Mythen3, Gotthard, Gotthard2 and "
"Moench.<br><br>"
"It can be operated in parallel with the command "
"line interface: sls_detector_put, sls_detector_get, "
"sls_detector_acquire and sls_detector_help.<br><br>"
"Support:<br>"
"Dhanya.Thattil@psi.ch <br>"
"Erik.Froejdh@psi.ch.<br><br><br>"
"<br>slsDetectorGui Copyright (C) 2021 Contributors to SLS Detector Package<br><br>"
"This program comes with ABSOLUTELY NO WARRANTY. "
"This is free software, and you are welcome to redistribute it "
"under certain conditions. Refer LICENSE and LICESNSE.lesser "
"in root folder for more details."
"<\\p>",
"qDetectorMain::ExecuteHelp");
}
}
void qDetectorMain::Refresh(int index) {
LOG(logDEBUG) << "Refresh Main Tab";
if (!tabs->isTabEnabled(index))
tabs->setCurrentIndex((index++) < (tabs->count() - 1) ? index
: MEASUREMENT);
else {
switch (tabs->currentIndex()) {
case MEASUREMENT:
tabMeasurement->Refresh();
break;
case SETTINGS:
tabSettings->Refresh();
break;
case DATAOUTPUT:
tabDataOutput->Refresh();
break;
case PLOT:
tabPlot->Refresh();
break;
case ADVANCED:
tabAdvanced->Refresh();
break;
case DEBUGGING:
tabDebugging->Refresh();
break;
case DEVELOPER:
tabDeveloper->Refresh();
break;
case MESSAGES:
tabMessages->Refresh();
break;
}
}
for (int i = 0; i < NumberOfTabs; ++i)
tabs->tabBar()->setTabTextColor(i, defaultTabColor);
tabs->tabBar()->setTabTextColor(index, QColor(0, 0, 200, 255));
}
void qDetectorMain::ResizeMainWindow(bool b) {
LOG(logDEBUG1) << "Resizing Main Window: height:" << height();
// undocked from the main window
if (b) {
// sets the main window height to a smaller maximum to get rid of space
setMaximumHeight(height() - heightPlotWindow - 9);
dockWidgetPlot->setMinimumHeight(0);
LOG(logINFO) << "Undocking from main window";
} else {
setMaximumHeight(QWIDGETSIZE_MAX);
// the minimum for plot will be set when the widget gets resized
// automatically
}
}
void qDetectorMain::resizeEvent(QResizeEvent *event) {
if (!dockWidgetPlot->isFloating()) {
dockWidgetPlot->setMinimumHeight(height() - centralwidget->height() -
50);
centralwidget->setMaximumHeight(heightCentralWidget);
}
// adjusting tab width
if (width() >= 800) {
tabs->tabBar()->setFixedWidth(width() + 61);
} else {
tabs->tabBar()->setMinimumWidth(0);
tabs->tabBar()->setExpanding(true);
tabs->tabBar()->setUsesScrollButtons(true);
}
event->accept();
}
void qDetectorMain::EnableTabs(bool enable) {
LOG(logDEBUG) << "qDetectorMain::EnableTabs";
// normal tabs
tabs->setTabEnabled(DATAOUTPUT, enable);
tabs->setTabEnabled(SETTINGS, enable);
tabs->setTabEnabled(MESSAGES, enable);
// actions check
actionLoadConfiguration->setEnabled(enable);
actionLoadParameters->setEnabled(enable);
actionDebug->setEnabled(enable);
actionExpert->setEnabled(enable);
// special tabs
tabs->setTabEnabled(DEBUGGING, enable && (actionDebug->isChecked()));
tabs->setTabEnabled(DEVELOPER, enable && isDeveloper);
// expert
bool expertTab = enable && (actionExpert->isChecked());
tabs->setTabEnabled(ADVANCED, expertTab);
actionLoadTrimbits->setVisible(expertTab &&
detType == slsDetectorDefs::EIGER);
// moved to here, so that its all in order, instead of signals and different
// threads
if (!enable) {
// tabMeasurement->Refresh(); too slow to refresh
tabSettings->Refresh();
tabDataOutput->Refresh();
if (tabAdvanced->isEnabled())
tabAdvanced->Refresh();
if (tabDebugging->isEnabled())
tabDebugging->Refresh();
if (tabDeveloper->isEnabled())
tabDeveloper->Refresh();
tabPlot->Refresh();
plot->StartAcquisition();
} else { // to enable scan box
tabPlot->Refresh();
// to start adc timer
if (tabDeveloper->isEnabled())
tabDeveloper->Refresh();
}
}
void qDetectorMain::SetZoomToolTip(bool disable) {
if (disable)
dockWidgetPlot->setToolTip(
"<span style=\" color:#00007f;\">To Enable mouse-controlled "
"zooming capabilities,\ndisable min and max for all axes.<span> ");
else
dockWidgetPlot->setToolTip(zoomToolTip);
}