65
Developing BornAgain graphical user interface: lessons learned Gennady Pospelov Scientific Computing Group at MLZ Workshop on Neutron Scattering Data Analysis Software 6-8 June, 2018, Sorangna

Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

  • Upload
    others

  • View
    13

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Developing BornAgain graphical user interface:lessons learned

Gennady PospelovScientific Computing Group at MLZ

Workshop on Neutron Scattering Data Analysis Software6-8 June, 2018, Sorangna

Page 2: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Developing graphical user interface

○ Design of the visual composition of an application○ Design of internal application structure

We are going to focus on aspects of internal design of large GUI applications, leaving questions of usability and user experience aside.

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 02

Page 3: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Outline

○ Context○ GUI design patterns○ Model/View in Qt○ BornAgain application model○ Useful recipes

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 03

Page 4: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Context

GUI application in most cases deals with three copies of the data

Record state

Session state

Screen state

The data stored on disk or in the database

Temporary local version of data that the user works on until they save

Copy of data laying in GUI components which users see on the screen

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 04

Page 5: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Context

Record state

Session state

Screen state

The data stored on disk or in the database

Temporary local version of data that the user works on until they save

Copy of data laying in GUI components which users see on the screen

Core state

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 04

GUI application in most cases deals with three copies of the data

Page 6: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Context

Session state

Screen state

Temporary local version of data that the user works on until they save

Copy of data laying in GUI components which users see on the screen

How to organize presentation and presentation-related code?

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 05

Page 7: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Context

Session state

Screen state

How to organize presentation and presentation-related code?

Temporary local version of data that the user works on until they save

Copy of data laying in GUI components which users see on the screen

● It is important to keep session state and screen state separated● Session objects should be self contained and work without reference to the presentation

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 05

Page 8: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Toy dialog

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 06

Page 9: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

class Widget : public QDialog { Q_OBJECTpublic: Widget(QWidget* parent = nullptr); ~Widget();

int getNumber() { return m_number;}

private slots: void onTextChanged();

private: int m_number; QLineEdit* m_lineEdit; QLineEdit* m_label;};

Toy dialog

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 07

Page 10: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

class Widget : public QDialog { Q_OBJECTpublic: Widget(QWidget* parent = nullptr); ~Widget();

int getNumber() { return m_number;}

private slots: void onTextChanged();

private: int m_number; QLineEdit* m_lineEdit; QLineEdit* m_label;};

Widget::Widget(QWidget* parent) : QDialog(parent) , m_number(0) , m_lineEdit(new QLineEdit) , m_label(new QLineEdit){ auto layout = new QVBoxLayout; layout->addWidget(m_lineEdit); layout->addWidget(m_label);

setLayout(layout);

m_lineEdit->setText(QString::number(m_number));

connect(m_lineEdit, &QLineEdit::textChanged, this, &Widget::onTextChanged);}

void Widget::onTextChanged(){ if (m_lineEdit->text().toInt() == 42) { m_label->setText("Good!"); m_number = m_lineEdit->text().toInt(); }}

int main(){ Widget widget; widget.exec(); std::cout << "Number was " << widget.getNumber() << std::endl;}

Toy dialog

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 07

Page 11: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

class Widget : public QDialog { Q_OBJECTpublic: Widget(QWidget* parent = nullptr); ~Widget();

int getNumber() { return m_number;}

private slots: void onTextChanged();

private: int m_number; QLineEdit* m_lineEdit; QLineEdit* m_label;};

Widget::Widget(QWidget* parent) : QDialog(parent) , m_number(0) , m_lineEdit(new QLineEdit) , m_label(new QLineEdit){ auto layout = new QVBoxLayout; layout->addWidget(m_lineEdit); layout->addWidget(m_label);

setLayout(layout);

m_lineEdit->setText(QString::number(m_number));

connect(m_lineEdit, &QLineEdit::textChanged, this, &Widget::onTextChanged);}

void Widget::onTextChanged(){ if (m_lineEdit->text().toInt() == 42) { m_label->setText("Good!"); m_number = m_lineEdit->text().toInt(); }}

int main(){ Widget widget; widget.exec(); std::cout << "Number was " << widget.getNumber() << std::endl;}

Data (Model)

Toy dialog

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 08

Page 12: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

class Widget : public QDialog { Q_OBJECTpublic: Widget(QWidget* parent = nullptr); ~Widget();

int getNumber() { return m_number;}

private slots: void onTextChanged();

private: int m_number; QLineEdit* m_lineEdit; QLineEdit* m_label;};

Widget::Widget(QWidget* parent) : QDialog(parent) , m_number(0) , m_lineEdit(new QLineEdit) , m_label(new QLineEdit){ auto layout = new QVBoxLayout; layout->addWidget(m_lineEdit); layout->addWidget(m_label);

setLayout(layout);

m_lineEdit->setText(QString::number(m_number));

connect(m_lineEdit, &QLineEdit::textChanged, this, &Widget::onTextChanged);}

void Widget::onTextChanged(){ if (m_lineEdit->text().toInt() == 42) { m_label->setText("Good!"); m_number = m_lineEdit->text().toInt(); }}

int main(){ Widget widget; widget.exec(); std::cout << "Number was " << widget.getNumber() << std::endl;}

Data (Model)

Toy dialog

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 08

View

Page 13: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

class Widget : public QDialog { Q_OBJECTpublic: Widget(QWidget* parent = nullptr); ~Widget();

int getNumber() { return m_number;}

private slots: void onTextChanged();

private: int m_number; QLineEdit* m_lineEdit; QLineEdit* m_label;};

Widget::Widget(QWidget* parent) : QDialog(parent) , m_number(0) , m_lineEdit(new QLineEdit) , m_label(new QLineEdit){ auto layout = new QVBoxLayout; layout->addWidget(m_lineEdit); layout->addWidget(m_label);

setLayout(layout);

m_lineEdit->setText(QString::number(m_number));

connect(m_lineEdit, &QLineEdit::textChanged, this, &Widget::onTextChanged);}

void Widget::onTextChanged(){ if (m_lineEdit->text().toInt() == 42) { m_label->setText("Good!"); m_number = m_lineEdit->text().toInt(); }}

int main(){ Widget widget; widget.exec(); std::cout << "Number was " << widget.getNumber() << std::endl;}

Data (Model)

Toy dialog

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 08

View

Business logic

Page 14: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

class Widget : public QDialog { Q_OBJECTpublic: Widget(QWidget* parent = nullptr); ~Widget();

int getNumber() { return m_number;}

private slots: void onTextChanged();

private: int m_number; QLineEdit* m_lineEdit; QLineEdit* m_label;};

Widget::Widget(QWidget* parent) : QDialog(parent) , m_number(0) , m_lineEdit(new QLineEdit) , m_label(new QLineEdit){ auto layout = new QVBoxLayout; layout->addWidget(m_lineEdit); layout->addWidget(m_label);

setLayout(layout);

m_lineEdit->setText(QString::number(m_number));

connect(m_lineEdit, &QLineEdit::textChanged, this, &Widget::onTextChanged);}

void Widget::onTextChanged(){ if (m_lineEdit->text().toInt() == 42) { m_label->setText("Good!"); m_number = m_lineEdit->text().toInt(); }}

int main(){ Widget widget; widget.exec(); std::cout << "Number was " << widget.getNumber() << std::endl;}

Data (Model)

Toy dialog

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 08

View

Business logic

Presentation logic

Page 15: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

View1 View2

Two toy views

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 09

Page 16: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

○ Context○ GUI design patterns○ Model/View in Qt○ BornAgain application model○ Useful recipes

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 10

Page 17: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Model View Controller

The data (model), user interface (view) and interactions (controller) are separated

Disadvantages○ View is tightly coupled to the model and pollutes business logic○ Hard to test a view

Advantages○ Increased flexibility and reuse○ Same data can be displayed in many views

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 11

Page 18: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Model View Controller

Different descriptions contradict each other

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 12

Page 19: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Model

Model View Presenter

Presenter

View

User

Advantages○ Separates view from the model.○ Solves the problem of unit testing of the view.

○ Business logic layer

○ Reads model○ Drives View through interface○ Contains Presentation logic○ Have two concerns: updating

model and presenting model

○ Obeys IView interface○ Very thin○ It is acceptable not to Unit test it

3) command to update 5) query4) change

notification

6) manipulates2) notifies

1) interacts 7) displays

View interface

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 13

Page 20: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Model

Model View ViewModel

ViewModel

View

User

3) command to update 5) query4) change

notification

6) bindings2) notifies

1) interacts 7) displays

Advantages○ Separates view from the model.○ Solves the problem of unit testing of the view.

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 14

○ Business logic layer

○ Reads model○ Drives View through interface○ Contains Presentation logic○ Have two concerns: updating

model and presenting model

○ Obeys IView interface○ Very thin○ It is acceptable not to Unit test it

Page 21: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

○ Context○ GUI Design patterns○ Model/View in Qt○ BornAgain application model○ Useful recipes

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 15

Page 22: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Model View in Qt

○ Data contains raw, GUI unaware data

○ Model communicates with data source and provides interface with other components

○ View displays data obtained from the model and handles user input with the help of delegate

○ Delegate renders the data in a view and communicates with the model

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 16

Page 23: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Model View in Qt

○ Data → Model

○ Model → ViewModel or Presenter

○ View

○ Delegate → Controller

Some users insist that the naming is wrong and should be…

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 17

Page 24: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Model View in Qt

It’s easier to think of Qt’s Model View Framework as a way to adapt user data for Qt’s standard widgets.

○ Data → Model

○ Model → ViewModel or Presenter

○ View

○ Delegate → Controller

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 17

Some users insist that the naming is wrong and should be…

Page 25: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

○ Context○ GUI Design patterns○ Model/View in Qt○ BornAgain application model○ Useful recipes

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 18

Page 26: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the
Page 27: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the
Page 28: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the
Page 29: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the
Page 30: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

C++ coreC++ core

External dependencies:Eigen, fftw3, GSL

External dependencies:Eigen, fftw3, GSL

Python bindings

StandaloneGUI

StandaloneGUI

External dependencies:Qt5

External dependencies:Qt5

UserUser

script.pyscript.py

BornAgain GUI

Core is Qt independent and fully unaware of GUI existenceGUI objects do not inherit from Core objects, do not contain any Core members

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 19

80k loc60k loc

Page 31: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

GUI and Core relations

Time

Sample constructionfully isolated from Core

GUI

CORE

Generates Core simulation,runs it in non-GUI thread

#include “core/simulation.h”s = new Simulation()

s->runSimulation()

Knows how to retrieve results

s→result()delete s

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 20

Core is Qt independent and fully unaware of GUI existenceGUI objects do not inherit from Core objects, do not contain any Core members

Page 32: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain GUI application model

We use Model-View-ViewModel

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 21

Data Model

View ModelQAbstractItemModel

ViewQAbstractItemView

Data Model○ Key component of BornAgain GUI○ Contains all the data of GUI session○ Contain all the business logic of GUI session○ Can read/write itself on disk

ViewModel○ Adapts data model to Qt’s QAbstractItemModel interface

Page 33: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain GUI application model

QTreeView QListView QTreeView QTreeView Custom widgetQGraphicsSceneCustom widget

Delegate Delegate Delegate

Proxy ModelQAbstractProxyModel

Proxy ModelQFilterProxyModel

Widget MapperQDataWidgetMapper

View ModelQAbstractItemModel

Data Model

queryNotifies

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 22

We use Model-View-ViewModel ... more or less

Page 34: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain GUI application model

QTreeView QListView QTreeView QTreeView Custom widgetQGraphicsSceneCustom widget

Delegate Delegate Delegate

Proxy ModelQAbstractProxyModel

Proxy ModelQFilterProxyModel

Widget MapperQDataWidgetMapper

View ModelQAbstractItemModel

Data Model

queryNotifies

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 22

We use Model-View-ViewModel ... more or less

Page 35: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain GUI application model

QTreeView QListView QTreeView QTreeView Custom widgetQGraphicsSceneCustom widget

Delegate Delegate Delegate

Proxy ModelQAbstractProxyModel

Proxy ModelQFilterProxyModel

Widget MapperQDataWidgetMapper

View ModelQAbstractItemModel

Data Model

queryNotifies

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 22

We use Model-View-ViewModel ... more or less

Page 36: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain GUI application model

QTreeView QListView QTreeView QTreeView Custom widgetQGraphicsSceneCustom widget

Delegate Delegate Delegate

Proxy ModelQAbstractProxyModel

Proxy ModelQFilterProxyModel

Widget MapperQDataWidgetMapper

View ModelQAbstractItemModel

Data Model

queryNotifies

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 22

We use Model-View-ViewModel ... more or less

Page 37: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain GUI application model

QTreeView QListView QTreeView QTreeView Custom widgetQGraphicsSceneCustom widget

Delegate Delegate Delegate

Proxy ModelQAbstractProxyModel

Proxy ModelQFilterProxyModel

Widget MapperQDataWidgetMapper

View ModelQAbstractItemModel

Data Model

queryNotifies

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 22

We use Model-View-ViewModel ... more or less

Page 38: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain GUI application model

QTreeView QListView QTreeView QTreeView Custom widgetQGraphicsSceneCustom widget

Delegate Delegate Delegate

Proxy ModelQAbstractProxyModel

Proxy ModelQFilterProxyModel

Widget MapperQDataWidgetMapper

View ModelQAbstractItemModel

Data Model

queryNotifies

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 22

We use Model-View-ViewModel ... more or less

Page 39: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

root

class SessionItem {

public: SessionItem(QString modelType);

private: QMap<Role, QVariant> m_data; QVector<SessionItem*> m_children;};

BornAgain data model

Data model is a tree structure made upof nodes - SessionItem objects

○ Node might contain arbitrary amount of data○ Node might contain row of children○ Node owns its children

Data Model

View Model

View

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 23

Page 40: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

SessionItem and its roles

Role QVariant(...)

DataRole Anything

DisplayRole QString

ModelType QString

ToolTipRole QString

RealLimitsRole RealLimits

EditorRole QString

. . . . . .

class SessionItem {

public: SessionItem(QString modelType);

QVariant data(Role role = DataRole) { return m_data[role]; }

private: QMap<Role, QVariant> m_data; QVector<SessionItem*> m_children;};

SessionItem’s m_data is a map where integer Role corresponds to QVariantSet of available roles may differ from node to node

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 24

Page 41: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

SessionItem as a base class

All objects of GUI session are derived from SessionItem

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 25

class CylinderItem : public SessionItem {public: CylinderItem() : SessionItem("Cylinder") { addProperty("Radius", 8.0)->setLimits(RealLimits::positive()); addProperty("Height", 16.0)->setToolTip("Height of the cylinder in nanometer") .setEditorType(Constants::ScientificEditorType); } };

Page 42: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

SessionItem as a base class

SessionItem

SessionItem

SessionItem

Construction of concrete CylinderItem object leads to creation of the branch in data treeCylinderItem cylinder;

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 25

class CylinderItem : public SessionItem {public: CylinderItem() : SessionItem("Cylinder") { addProperty("Radius", 8.0)->setLimits(RealLimits::positive()); addProperty("Height", 16.0)->setToolTip("Height of the cylinder in nanometer") .setEditorType(Constants::ScientificEditorType); } };

All objects of GUI session are derived from SessionItem

Page 43: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

SessionItem as a base class

Role QVariant(...)

DisplayRole Cylinder

Role QVariant(...)

DataRole 8.0

DisplayRole Radius

RealLimitsRole Positive

Role QVariant(...)

DataRole 16.0

DisplayRole Height

ToolTipRole Height of cylinder

EditorRole Scientific Double

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 25

Construction of concrete CylinderItem object leads to creation of the branch in data treeCylinderItem cylinder;

class CylinderItem : public SessionItem {public: CylinderItem() : SessionItem("Cylinder") { addProperty("Radius", 8.0)->setLimits(RealLimits::positive()); addProperty("Height", 16.0)->setToolTip("Height of the cylinder in nanometer") .setEditorType(Constants::ScientificEditorType); } };

SessionItem

SessionItem

SessionItem

All objects of GUI session are derived from SessionItem

Page 44: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

○ Adapts data model to QAbstractItemModel interface○ Contains set of methods to manipulate underlying data

BornAgain ViewModel

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 26

Data Model

View Model

View

class ViewModel : public QAbstractItemModel {public: ViewModel(QObject* parent);

// Overloaded methods QVariant data(const QModelIndex& index, int role) const; int rowCount(const QModelIndex& parent) const; int columnCount(const QModelIndex& parent) const;

// Methods to manipulate underlying data model SessionItem* itemFromIndex(const QModelIndex& index); QModelIndex indexFromItem(SessionItem* item); SessionItem* insertItem(const QModelIndex& parent, QString modelType);

private: SessionItem* m_rootItem;};

Page 45: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

class ViewModel : public QAbstractItemModel {public: ViewModel(QObject* parent);

// Overloaded methods QVariant data(const QModelIndex& index, int role) const; int rowCount(const QModelIndex& parent) const; int columnCount(const QModelIndex& parent) const;

// Methods to manipulate underlying data model SessionItem* itemFromIndex(const QModelIndex& index); QModelIndex indexFromItem(SessionItem* item); SessionItem* insertItem(const QModelIndex& parent, QString modelType);

private: SessionItem* m_rootItem;};

○ Adapts data model to QAbstractItemModel interface○ Contains set of methods to manipulate underlying data

BornAgain ViewModel

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 26

Data Model

View Model

View

Entry point to underlying data model

Page 46: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain ViewModel and its Views

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 27

Page 47: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain ViewModel and its Views

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 27

Page 48: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

○ Context○ GUI Design patterns○ Model/View in Qt○ BornAgain application model○ Useful recipes

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 28

Page 49: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

QAbstractItemDelegate

○ Provides display and editing facilities for data item from the model○ Should be used if item contains custom QVariant○ Or if native display/editing is not fancy enough

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 29

Data Model

View Model

View

Delegate

Page 50: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

QAbstractItemDelegate

○ Provides display and editing facilities for data item from the model○ Should be used if item contains custom QVariant○ Or if native display/editing is not fancy enough

SampleModel model;SessionModelDelegate delegate;

auto tree = new QTreeView;tree->setModel(&model);tree->setItemDelegate(&delegate);

class SessionModelDelegate : public QStyledItemDelegate{public: SessionModelDelegate(QObject* parent);

void paint(QPainter* painter, const QModelIndex& index); QWidget* createEditor(QWidget* parent, const QModelIndex& index);

};

Usage:

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 29

Data Model

View Model

View

Delegate

Page 51: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Using QDataWidgetMapper

Provides automatic data-binding between custom view and model parts

QTreeView displaying sample model Custom layout displaying parts of the model

SampleModel model;

// index pointing to some model partQModelIndex index = model.index(/*row*/0, /*col*/0, QModelIndex());

auto editor = new QDoubleSpinBox;auto mapper = new QDataWidgetMapper;

mapper->setCurrentModelIndex(index);mapper->addMapping(editor);

Any change in the model will be automatically propagated to the widget and back

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 30

Page 52: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Business logic

○ Most of business logic is handled by data model and can be easily tested

Total particle density should be disabled, when there is 2D interference function attached to ParticleLayout

ParticleLayoutItem::ParticleLayoutItem() : SessionGraphicsItem(Constants::ParticleLayoutType){

mapper()->setOnChildrenChange([this](SessionItem*) { updateDensityAppearance(); updateDensityValue(); });}

In BornAgain data model this is handled by setting proper callbackin ParticleLayoutItem constructor

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 31

Page 53: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Unit testing

TEST_F(TestParticleLayoutItem, densityAppearance){ SampleModel model; auto layout = dynamic_cast<ParticleLayoutItem*>(model.insertNewItem(Constants::ParticleLayoutType));

// empty layout should have TotalDensity enabled EXPECT_TRUE(layout->getItem(ParticleLayoutItem::P_TOTAL_DENSITY)->isEnabled());

// adding radial paracrystal shouldn't change it auto interference = model.insertNewItem(Constants::InterferenceFunctionRadialParaCrystalType, model.indexOfItem(layout), -1, ParticleLayoutItem::T_INTERFERENCE); EXPECT_TRUE(layout->getItem(ParticleLayoutItem::T_INTERFERENCE) == interference); EXPECT_TRUE(layout->getItem(ParticleLayoutItem::P_TOTAL_DENSITY)->isEnabled());

// removing paracrystal, TotalDensity still enabled layout->takeRow(ParentRow(*interference)); EXPECT_TRUE(layout->getItem(ParticleLayoutItem::T_INTERFERENCE) == nullptr); EXPECT_TRUE(layout->getItem(ParticleLayoutItem::P_TOTAL_DENSITY)->isEnabled()); delete interference;

// adding 2d interference, TotalDensity should be disabled interference = model.insertNewItem(Constants::InterferenceFunction2DLatticeType, model.indexOfItem(layout), -1, ParticleLayoutItem::T_INTERFERENCE); EXPECT_FALSE(layout->getItem(ParticleLayoutItem::P_TOTAL_DENSITY)->isEnabled());

// removing 2D interference, TotalIntensity should be reenabled layout->takeRow(ParentRow(*interference)); delete interference; EXPECT_TRUE(layout->getItem(ParticleLayoutItem::T_INTERFERENCE) == nullptr); EXPECT_TRUE(layout->getItem(ParticleLayoutItem::P_TOTAL_DENSITY)->isEnabled());}

○ There are 189 tests for the moment

Demonstrates how to test SurfaceDensity enabled/disabledBornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 32

Page 54: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

○ Various sources use terms Model, View, Controller, Presenter for different things

○ The common idea behind – separation of concerns○ Qt Model/View has not much to do with structuring of the whole app○ It just provides the way to show user data in Qt’s tables and trees

Summary

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 33

Page 55: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

○ Data Model is the core of large GUI application○ Contains all the GUI data○ Provides business logic, covered by unit tests○ Should serialize itself on disk○ Should provide project file back compatibility○ By design have to have undo/redo capabilities○ Be Qt independent, if possible

Summary

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 34

Page 56: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Backup

Page 57: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Links

Stack Overflow: why Qt is misusing model/view terminologyhttps://stackoverflow.com/questions/5543198/why-qt-is-misusing-model-view-terminology

Martin FowlerEssay on GUI Architectureshttps://martinfowler.com/eaaDev/uiArchs.html

Stack Overflow: what are MVP and MVChttps://stackoverflow.com/questions/2056/what-are-mvp-and-mvc-and-what-is-the-difference

Combining MVC, MVP and MVVMhttp://gexiaoguo.github.io/MVC,-MVP-and-MVVM/ Yet another discussion MVC, MVP and MVVMhttps://stackoverflow.com/questions/19444431/what-is-difference-between-mvc-mvp-mvvm-design-pattern-in-terms-of-coding-c-s

Page 58: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

QIdentityProxyModel

○ QIdentityProxyModel proxies its source model unmodified○ Allows to substitute data with something else

class SessionDecorationModel : public QIdentityProxyModel{public: SessionDecorationModel(SessionModel* model); ~SessionDecorationModel();

QVariant data(const QModelIndex& index, int role) const { if (role == Qt::DecorationRole) { QVariant result = createIcon(index); if (result.isValid()) return result; }

return QVariant(); }

private: SessionModel* m_model;};

SampleModel model;SessionDecorationModel decoration(&model);

auto list = new QListView;list->setModel(&decoration);

Usage:

Page 59: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

QFilterProxyModel

○ Provides support for filtering data passed between model and view○ Can be used to restructure the data for a view without transformation of underlying data

Original model Filtered model

Page 60: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

QAbstractProxyModel○ Allows to restructure layout of model in a view without actual change of underlying

layout○ Can be used, for example, to substitute parentship, or change number of columns

Original model Model transformation

Parent was hidden

Radius and Height got another parent

Page 61: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

QAbstractProxyModel○ Allows to restructure layout of model in a view without actual change of underlying

layout○ Can be used, for example, to substitute parentship, or change number of columns

Original model Model transformation

Layout of original model was changedncol=2 → ncol=5

BornAgain / GUI SINE2020 Workshop, 6-8 June, 2018, Soragna 31

Page 62: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Unit testing

○ We rely on googletest instead of native QtTest framework○ Signal/slots are tested via QSignalSpy

ProjectDocument document;

QSignalSpy spyDocument(&document, SIGNAL(modified())); EXPECT_EQ(spyDocument.count(), 0);

// Changing document and checking its status modify_models(&models); EXPECT_TRUE(document.isModified()); EXPECT_EQ(spyDocument.count(), 1);

// Saving document document.save(projectFileName); EXPECT_FALSE(document.isModified()); EXPECT_EQ(spyDocument.count(), 2); // save triggers 'modified' signal

Page 63: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

Third party Qt code in BornAgain

QCustomPlot, 36k loc

DetailsWidget (Qt creator), 800 loc

Widgetbox (Qt creator), 8k loc

Manhattan style (Qt creator), 7k loc

Gradients, MainWindow layout, 1 pixel splitters

Page 64: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

4K displays

○ 15,6” laptops nowadays come mostly in two resolutions

FHD, also known as 1080p, 1920 x 1080, PPI=141UHD-1, also known as 2160p, 3840 x 2160, PPI=282

○ Users then change scale factor in OS settings from 100% to 200%

OS icons, interface fonts etc remains same size as in FHD caseThey start to look visibly crisper

Time on battery decreases by factor of 2 (6 hours → 3 hours)Many application not supporting scaling are unusable

Page 65: Developing BornAgain graphical user interface: lessons learnedapps.jcns.fz-juelich.de/doku/sc/_media/pospelov_soranga_gui.pdf · Developing graphical user interface Design of the

BornAgain in 4K

Will be ready soon...