Skip to content
Snippets Groups Projects
InstrumentView.cpp 11.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • //  ************************************************************************************************
    
    Pospelov, Gennady's avatar
    Pospelov, Gennady committed
    //
    
    //  BornAgain: simulate and fit reflection and scattering
    
    Pospelov, Gennady's avatar
    Pospelov, Gennady committed
    //
    
    //! @file      GUI/View/Instrument/InstrumentView.cpp
    
    Pospelov, Gennady's avatar
    Pospelov, Gennady committed
    //! @brief     Implements class InstrumentView
    //!
    
    //! @homepage  http://www.bornagainproject.org
    
    Pospelov, Gennady's avatar
    Pospelov, Gennady committed
    //! @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)
    
    Pospelov, Gennady's avatar
    Pospelov, Gennady committed
    //
    
    //  ************************************************************************************************
    
    Pospelov, Gennady's avatar
    Pospelov, Gennady committed
    
    
    #include "GUI/View/Instrument/InstrumentView.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"
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include "GUI/View/Widget/ApplicationSettings.h"
    #include "GUI/View/Widget/GroupBoxes.h"
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
    #include "GUI/View/Widget/StyledToolbar.h"
    
    #include <QCheckBox>
    
    #include <QFormLayout>
    #include <QLineEdit>
    
    #include <QMenu>
    #include <QMessageBox>
    
    #include <QScrollArea>
    
    #include <QWidgetAction>
    
    Pospelov, Gennady's avatar
    Pospelov, Gennady committed
    
    
    InstrumentView::InstrumentView(QWidget* parent, ProjectDocument* document)
    
        : QWidget(parent)
        , m_document(document)
    
        // Left margin: instrument list
    
        m_instrumentListView = new InstrumentListView(document, this);
    
        m_instrumentListView->setVisible(!document->singleInstrumentMode());
        connect(m_document, &ProjectDocument::singleInstrumentModeChanged,
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
                [this]() { m_instrumentListView->setVisible(!m_document->singleInstrumentMode()); });
    
        connect(m_instrumentListView, &InstrumentListView::instrumentSelected, this,
                &InstrumentView::createWidgetsForCurrentInstrument);
    
        // Large widget: current instrument
    
        m_scrollArea = new QScrollArea(this);
        m_scrollArea->setWidgetResizable(true);
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
        // m_scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    
        // Top toolbar with action buttons "new ...instrument" etc
    
        auto* toolbar = new StyledToolbar(this);
    
        toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    
        toolbar->addActions(m_instrumentListView->toolbarActions());
    
        // Overall layout
        auto* horizontalLayout = new QHBoxLayout;
        horizontalLayout->addWidget(m_instrumentListView);
        horizontalLayout->addWidget(m_scrollArea, 1);
    
        mainLayout->setContentsMargins(0, 0, 0, 0);
    
        mainLayout->addWidget(toolbar);
    
    Pospelov, Gennady's avatar
    Pospelov, Gennady committed
        mainLayout->addLayout(horizontalLayout);
    
        // Initial state
    
        createWidgetsForCurrentInstrument();
    }
    
    
    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();
    
            m_scrollArea->setWidget(new QWidget(m_scrollArea)); // blank widget
    
    
        QWidget* w = new QWidget(m_scrollArea);
        auto* layout = new QVBoxLayout(w);
    
    
    Wuttke, Joachim's avatar
    ..  
    Wuttke, Joachim committed
        //... Groupbox with instrument name and description (same layout for all instrument types)
    
    
        auto title = QString("Summary (%1 instrument)").arg(currentInstrument->instrumentType());
    
        auto* g = new CollapsibleGroupBox(title, m_scrollArea, currentInstrument->expandInfo);
    
        g->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    
        auto* formLayout = new QFormLayout;
        g->body()->setLayout(formLayout);
    
        layout->addWidget(g);
    
        auto* nameEdit = new QLineEdit(g);
        nameEdit->setText(currentInstrument->instrumentName());
        connect(nameEdit, &QLineEdit::textEdited, this, &InstrumentView::onInstrumentNameEdited);
    
        formLayout->addRow("Name:", nameEdit);
    
        auto* descriptionEdit = new QTextEdit(g);
        descriptionEdit->setMinimumWidth(300);
    
        descriptionEdit->setFixedHeight(60); // TODO replace by 2*line_height
    
        descriptionEdit->setAcceptRichText(false);
        descriptionEdit->setTabChangesFocus(true);
        descriptionEdit->setPlainText(currentInstrument->description());
    
        connect(descriptionEdit, &QTextEdit::textChanged, [this, descriptionEdit]() {
            onInstrumentdescriptionEdited(descriptionEdit->toPlainText());
        });
    
        formLayout->addRow("Description:", descriptionEdit);
    
    Wuttke, Joachim's avatar
    ..  
    Wuttke, Joachim committed
        //... All remaining content depends on instrument type
    
        auto* ec = m_document->multiNotifier();
    
    Wuttke, Joachim's avatar
    ..  
    Wuttke, Joachim committed
        if (auto* ii = dynamic_cast<SpecularInstrumentItem*>(currentInstrument)) {
            auto* editor = new SpecularInstrumentEditor(m_scrollArea, ii, ec);
    
            connect(editor, &SpecularInstrumentEditor::dataChanged, this,
                    &InstrumentView::onInstrumentChangedByEditor);
            layout->addWidget(editor);
    
    Wuttke, Joachim's avatar
    ..  
    Wuttke, Joachim committed
    
        } else if (auto* ii = dynamic_cast<OffspecInstrumentItem*>(currentInstrument)) {
            auto* editor = new OffspecInstrumentEditor(m_scrollArea, ii, ec);
    
    Wuttke, Joachim's avatar
    Wuttke, Joachim committed
            connect(editor, &OffspecInstrumentEditor::dataChanged, this,
    
                    &InstrumentView::onInstrumentChangedByEditor);
            layout->addWidget(editor);
    
    Wuttke, Joachim's avatar
    ..  
    Wuttke, Joachim committed
    
        } else if (auto* ii = dynamic_cast<GISASInstrumentItem*>(currentInstrument)) {
            auto* editor = new GISASInstrumentEditor(m_scrollArea, ii);
    
            connect(editor, &GISASInstrumentEditor::dataChanged, this,
                    &InstrumentView::onInstrumentChangedByEditor);
            layout->addWidget(editor);
    
    Wuttke, Joachim's avatar
    ..  
    Wuttke, Joachim committed
    
        } else if (auto* ii = dynamic_cast<DepthprobeInstrumentItem*>(currentInstrument)) {
            auto* editor = new DepthprobeInstrumentEditor(m_scrollArea, ii, ec);
    
            connect(editor, &DepthprobeInstrumentEditor::dataChanged, this,
    
                    &InstrumentView::onInstrumentChangedByEditor);
            layout->addWidget(editor);
    
    Wuttke, Joachim's avatar
    ..  
    Wuttke, Joachim committed
    
    
            ASSERT_NEVER;
    
    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::fillViewMenu(QMenu* menu)
    {
    
    Ludwig Jaeck's avatar
    Ludwig Jaeck committed
        auto* action = new QWidgetAction(menu);
        auto* checkBox = new QCheckBox("Use single instrument", menu);
        action->setDefaultWidget(checkBox);
    
        action->setText("Use single instrument");
        action->setCheckable(true);
    
    Ludwig Jaeck's avatar
    Ludwig Jaeck committed
        checkBox->setChecked(m_document->singleInstrumentMode());
        connect(checkBox, &QCheckBox::stateChanged, this,
                &InstrumentView::onSingleInstrumentModeChanged);
    
        menu->addAction(action);
        menu->addSeparator();
    
    
    Ludwig Jaeck's avatar
    Ludwig Jaeck committed
        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);
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        if (gProjectDocument.has_value()) {
    
            appSettings->setDefaultIsSingleInstrumentMode(
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
                gProjectDocument.value()->singleInstrumentMode());
    
        }
    }
    
    ProjectDocument::Functionalities InstrumentView::functionalities() const
    {
        ProjectDocument::Functionalities f;
    
    Ludwig Jaeck's avatar
    Ludwig Jaeck committed
        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();
    
    Ludwig Jaeck's avatar
    Ludwig Jaeck committed
            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);
    
    Mikhail Svechnikov's avatar
    Mikhail Svechnikov committed
        if (gProjectDocument.has_value()) {
    
            appSettings->setDefaultFunctionalities(toVariant(f));
        }
    }