-
Wuttke, Joachim authoredWuttke, Joachim authored
InstrumentView.cpp 11.78 KiB
// ************************************************************************************************
//
// BornAgain: simulate and fit reflection and scattering
//
//! @file GUI/View/Instrument/InstrumentView.cpp
//! @brief Implements class InstrumentView
//!
//! @homepage http://www.bornagainproject.org
//! @license GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
// ************************************************************************************************
#include "GUI/View/Instrument/InstrumentView.h"
#include "GUI/Application/ApplicationSettings.h"
#include "GUI/Model/Device/InstrumentItems.h"
#include "GUI/View/Instrument/DepthprobeInstrumentEditor.h"
#include "GUI/View/Instrument/GISASInstrumentEditor.h"
#include "GUI/View/Instrument/InstrumentListView.h"
#include "GUI/View/Instrument/OffspecInstrumentEditor.h"
#include "GUI/View/Instrument/SpecularInstrumentEditor.h"
#include "GUI/View/Tool/GroupBoxCollapser.h"
#include "GUI/View/Widget/StyledToolbar.h"
#include <QAction>
#include <QCheckBox>
#include <QFormLayout>
#include <QGroupBox>
#include <QLineEdit>
#include <QMenu>
#include <QMessageBox>
#include <QScrollArea>
#include <QTextEdit>
#include <QWidgetAction>
InstrumentView::InstrumentView(QWidget* parent, ProjectDocument* document)
: QWidget(parent)
, m_document(document)
{
auto* horizontalLayout = new QHBoxLayout;
m_instrumentListView = new InstrumentListView(document, this);
horizontalLayout->addWidget(m_instrumentListView);
m_scrollArea = new QScrollArea(this);
m_scrollArea->setWidgetResizable(true);
m_scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
horizontalLayout->addWidget(m_scrollArea, 1);
auto* toolbar = new StyledToolbar(this);
toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolbar->addActions(m_instrumentListView->toolbarActions());
auto* mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
mainLayout->addWidget(toolbar);
mainLayout->addLayout(horizontalLayout);
createWidgetsForCurrentInstrument();
updateSingleInstrumentMode();
connect(m_instrumentListView, &InstrumentListView::instrumentSelected, this,
&InstrumentView::createWidgetsForCurrentInstrument);
connect(m_document, &ProjectDocument::singleInstrumentModeChanged, this,
&InstrumentView::updateSingleInstrumentMode);
}
void InstrumentView::showEvent(QShowEvent*)
{
// disconnect because when this view is visible, no other instance is modifying instruments. By
// disconnecting, no additional logic is necessary to avoid recursive calls (recursive since
// this view also causes instrumentChanged to be emitted).
disconnect(m_document->multiNotifier(), &MultiInstrumentNotifier::instrumentChanged, this,
&InstrumentView::onInstrumentChangedFromExternal);
}
void InstrumentView::hideEvent(QHideEvent*)
{
// when the instrument view gets hidden (meaning another view is shown), it's necessary to
// listen to changes (e.g. performed by the LinkInstrumentManager).
connect(m_document->multiNotifier(), &MultiInstrumentNotifier::instrumentChanged, this,
&InstrumentView::onInstrumentChangedFromExternal);
}
void InstrumentView::createWidgetsForCurrentInstrument()
{
auto* currentInstrument = m_instrumentListView->currentInstrumentItem();
if (!currentInstrument) {
m_scrollArea->setWidget(new QWidget(m_scrollArea)); // blank widget
return;
}
QWidget* w = new QWidget(m_scrollArea);
auto* layout = new QVBoxLayout(w);
auto* g = new QGroupBox(m_scrollArea);
g->setTitle(QString("Information (%1 instrument)").arg(currentInstrument->instrumentType()));
g->setMaximumHeight(200);
auto* formLayout = new QFormLayout(g);
formLayout->setContentsMargins(17, 17, 17, 17);
formLayout->setSpacing(8);
layout->addWidget(g);
auto* nameEdit = new QLineEdit(g);
formLayout->addRow("Name:", nameEdit);
nameEdit->setText(currentInstrument->instrumentName());
connect(nameEdit, &QLineEdit::textEdited, this, &InstrumentView::onInstrumentNameEdited);
auto* descriptionEdit = new QTextEdit(g);
descriptionEdit->setMinimumWidth(300);
descriptionEdit->setMaximumHeight(100);
descriptionEdit->setAcceptRichText(false);
descriptionEdit->setTabChangesFocus(true);
descriptionEdit->setPlainText(currentInstrument->description());
formLayout->addRow("Description:", descriptionEdit);
connect(descriptionEdit, &QTextEdit::textChanged,
[&]() { onInstrumentdescriptionEdited(descriptionEdit->toPlainText()); });
auto* collapser = GroupBoxCollapser::installIntoGroupBox(g);
collapser->setExpanded(currentInstrument->isExpandInfo());
connect(collapser, &GroupBoxCollapser::toggled, this,
[currentInstrument](bool b) { currentInstrument->setExpandInfo(b); });
auto* ec = m_document->multiNotifier();
if (auto* sp = dynamic_cast<SpecularInstrumentItem*>(currentInstrument)) {
auto* editor = new SpecularInstrumentEditor(m_scrollArea, sp, ec);
connect(editor, &SpecularInstrumentEditor::dataChanged, this,
&InstrumentView::onInstrumentChangedByEditor);
layout->addWidget(editor);
} else if (auto* os = dynamic_cast<OffspecInstrumentItem*>(currentInstrument)) {
auto* editor = new OffspecInstrumentEditor(m_scrollArea, os, ec);
connect(editor, &OffspecInstrumentEditor::dataChanged, this,
&InstrumentView::onInstrumentChangedByEditor);
layout->addWidget(editor);
} else if (auto* gisas = dynamic_cast<GISASInstrumentItem*>(currentInstrument)) {
auto* editor = new GISASInstrumentEditor(m_scrollArea, gisas);
connect(editor, &GISASInstrumentEditor::dataChanged, this,
&InstrumentView::onInstrumentChangedByEditor);
layout->addWidget(editor);
} else if (auto* dp = dynamic_cast<DepthprobeInstrumentItem*>(currentInstrument)) {
auto* editor = new DepthprobeInstrumentEditor(m_scrollArea, dp, ec);
connect(editor, &DepthprobeInstrumentEditor::dataChanged, this,
&InstrumentView::onInstrumentChangedByEditor);
layout->addWidget(editor);
} else
ASSERT(false);
m_scrollArea->setWidget(w);
}
void InstrumentView::onInstrumentNameEdited(const QString& newName)
{
auto* currentInstrument = m_instrumentListView->currentInstrumentItem();
if (currentInstrument && currentInstrument->instrumentName() != newName)
m_document->multiNotifier()->setInstrumentName(currentInstrument, newName);
}
void InstrumentView::onInstrumentdescriptionEdited(const QString& t)
{
auto* currentInstrument = m_instrumentListView->currentInstrumentItem();
if (currentInstrument && currentInstrument->description() != t) {
currentInstrument->setDescription(t);
onInstrumentChangedByEditor();
}
}
void InstrumentView::onInstrumentChangedByEditor()
{
// uses 'MultiInstrumentNotifier::instrumentChanged' signal for two purposes:
// 1) notify 'ProjectDocument' that user has changed data ==> mark project with '*'
// 2) notify 'LinkInstrumentManager' ==> unlink instrument from data if they are incompatible
m_document->multiNotifier()->notifyInstrumentChanged(
m_instrumentListView->currentInstrumentItem());
}
void InstrumentView::onInstrumentChangedFromExternal(const InstrumentItem* instrument)
{
if (instrument == m_instrumentListView->currentInstrumentItem())
createWidgetsForCurrentInstrument();
}
void InstrumentView::updateSingleInstrumentMode()
{
m_instrumentListView->setVisible(!m_document->singleInstrumentMode());
}
void InstrumentView::fillViewMenu(QMenu* menu)
{
auto* action = new QWidgetAction(menu);
auto* checkBox = new QCheckBox("Use single instrument", menu);
action->setDefaultWidget(checkBox);
action->setText("Use single instrument");
action->setCheckable(true);
checkBox->setChecked(m_document->singleInstrumentMode());
connect(checkBox, &QCheckBox::stateChanged, this,
&InstrumentView::onSingleInstrumentModeChanged);
menu->addAction(action);
menu->addSeparator();
const auto f = m_document->functionalities();
gisasCheck = new QCheckBox("GISAS instrument", menu);
gisasCheck->setChecked(f.testFlag(ProjectDocument::Gisas));
connect(gisasCheck, &QCheckBox::stateChanged, this, &InstrumentView::onFunctionalityChanged);
action = new QWidgetAction(menu);
action->setToolTip("GISAS instrument");
action->setDefaultWidget(gisasCheck);
menu->addAction(action);
offspecCheck = new QCheckBox("Off-specular instrument", menu);
offspecCheck->setChecked(f.testFlag(ProjectDocument::Offspec));
connect(offspecCheck, &QCheckBox::stateChanged, this, &InstrumentView::onFunctionalityChanged);
action = new QWidgetAction(menu);
action->setToolTip("Off-specular instrument");
action->setDefaultWidget(offspecCheck);
menu->addAction(action);
specularCheck = new QCheckBox("Specular instrument", menu);
specularCheck->setChecked(f.testFlag(ProjectDocument::Specular));
connect(specularCheck, &QCheckBox::stateChanged, this, &InstrumentView::onFunctionalityChanged);
action = new QWidgetAction(menu);
action->setToolTip("Specular instrument");
action->setDefaultWidget(specularCheck);
menu->addAction(action);
depthProbeCheck = new QCheckBox("Depth probe instrument", menu);
depthProbeCheck->setChecked(f.testFlag(ProjectDocument::Depthprobe));
connect(depthProbeCheck, &QCheckBox::stateChanged, this,
&InstrumentView::onFunctionalityChanged);
action = new QWidgetAction(menu);
action->setToolTip("Depth probe instrument");
action->setDefaultWidget(depthProbeCheck);
menu->addAction(action);
}
void InstrumentView::onSingleInstrumentModeChanged(bool newState)
{
if (newState) {
if (m_document->instrumentModel()->instrumentItems().size() > 1) {
QMessageBox::warning(this, "Select single instrument mode",
"This project already contains more than one instrument. Changing "
"this setting is not possible.");
return;
}
m_document->setSingleInstrumentMode(true);
} else
m_document->setSingleInstrumentMode(false);
if (gProjectDocument.has_value()) {
appSettings->setDefaultIsSingleInstrumentMode(
gProjectDocument.value()->singleInstrumentMode());
}
}
ProjectDocument::Functionalities InstrumentView::functionalities() const
{
ProjectDocument::Functionalities f;
f.setFlag(ProjectDocument::Gisas, gisasCheck->isChecked());
f.setFlag(ProjectDocument::Offspec, offspecCheck->isChecked());
f.setFlag(ProjectDocument::Specular, specularCheck->isChecked());
f.setFlag(ProjectDocument::Depthprobe, depthProbeCheck->isChecked());
return f;
}
void InstrumentView::onFunctionalityChanged()
{
const auto f = functionalities();
if (f == ProjectDocument::None) {
QMessageBox::warning(this, "Select functionality",
"You have to select at least one functionality. Changing "
"this setting is not possible.");
const auto f = m_document->functionalities();
gisasCheck->setChecked(f.testFlag(ProjectDocument::Gisas));
offspecCheck->setChecked(f.testFlag(ProjectDocument::Offspec));
specularCheck->setChecked(f.testFlag(ProjectDocument::Specular));
depthProbeCheck->setChecked(f.testFlag(ProjectDocument::Depthprobe));
return;
}
m_document->setFunctionalities(f);
if (gProjectDocument.has_value()) {
appSettings->setDefaultFunctionalities(toVariant(f));
}
}