BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
PyImportAssistant.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/mainwindow/PyImportAssistant.cpp
6 //! @brief Implements class PyImportAssistant
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
15 #ifdef BORNAGAIN_PYTHON
16 
18 #include "BABuild.h"
19 #include "Base/Utils/SysUtils.h"
30 #include <QApplication>
31 #include <QDebug>
32 #include <QFileDialog>
33 #include <QTextStream>
34 
35 namespace {
36 
37 //! Returns directory with BornAgain library. If PYTHONPATH is not empty,
38 //! returns an empty string.
39 
40 std::string bornagainDir()
41 {
42  std::string pythonPath = SysUtils::getenv("PYTHONPATH");
43  return pythonPath.empty() ? BABuild::buildLibDir() : std::string();
44 }
45 
46 //! Returns a name from the list which looks like a function name intended for sample
47 //! creation.
48 
49 QString getCandidate(const QStringList& funcNames)
50 {
51  if (funcNames.isEmpty())
52  return "";
53 
54  for (auto str : funcNames) {
55  QString name = str.toLower();
56  if (name.contains("sample") || name.contains("multilayer"))
57  return str;
58  }
59 
60  return funcNames.front();
61 }
62 
63 } // namespace
64 
65 PyImportAssistant::PyImportAssistant(MainWindow* mainwin) : QObject(mainwin), m_mainWindow(mainwin)
66 {
67 }
68 
70 {
71  auto fileName = fileNameToOpen();
72 
73  if (fileName.isEmpty())
74  return;
75 
76  QString snippet = readFile(fileName);
77  if (snippet.isEmpty())
78  return;
79 
80  QString funcName = getPySampleFunctionName(snippet);
81  if (funcName.isEmpty())
82  return;
83 
84  auto multilayer = createMultiLayer(snippet, funcName);
85  if (!multilayer)
86  return;
87 
88  populateModels(*multilayer, GUIHelpers::baseName(fileName));
89 }
90 
91 //! Lets user to select Python file on disk.
92 
94 {
95  QString dirname = AppSvc::projectManager()->userImportDir();
96 
97  QString result = QFileDialog::getOpenFileName(m_mainWindow, "Open python script", dirname,
98  "Python scripts (*.py)");
99 
100  saveImportDir(result);
101 
102  return result;
103 }
104 
105 //! Saves file location as a future import dir.
106 
107 void PyImportAssistant::saveImportDir(const QString& fileName)
108 {
109  if (fileName.isEmpty())
110  return;
111 
113 }
114 
115 //! Read content of text file and returns it as a multi-line string.
116 //! Pop-ups warning dialog in the case of failure.
117 
118 QString PyImportAssistant::readFile(const QString& fileName)
119 {
120  QString result;
121 
122  try {
123  result = ProjectUtils::readTextFile(fileName);
124 
125  } catch (const std::exception& ex) {
126  QString message("Can't read the file. \n\n");
127  message += QString::fromStdString(std::string(ex.what()));
128  GUIHelpers::warning(m_mainWindow, "File read failure.", message);
129  }
130 
131  return result;
132 }
133 
134 //! Returns the name of function which might generate a MultiLayer in Python code snippet.
135 //! Pop-ups dialog and asks user for help in the case of doubts.
136 
137 QString PyImportAssistant::getPySampleFunctionName(const QString& snippet)
138 {
139  QStringList funcList;
140 
141  QApplication::setOverrideCursor(Qt::WaitCursor);
142  try {
143  auto funcs = PyImport::listOfFunctions(snippet.toStdString(), bornagainDir());
144  funcList = GUIHelpers::fromStdStrings(funcs);
145 
146  } catch (const std::exception& ex) {
147  QApplication::restoreOverrideCursor();
148  QString message("Exception thrown while acquiring functions from Python code.\n\n");
149  QString details = QString::fromStdString(std::string(ex.what()));
150  DetailedMessageBox(m_mainWindow, "Python failure", message, details).exec();
151 
152  return "";
153  }
154  QApplication::restoreOverrideCursor();
155 
156  return selectPySampleFunction(funcList);
157 }
158 
159 //! Lets user select a function name which generates a MultiLayer.
160 
161 QString PyImportAssistant::selectPySampleFunction(const QStringList& funcNames)
162 {
163  QString result;
164 
165  if (funcNames.empty()) {
166  QString message("Python code doesn't contain any functions.\n\n");
167  GUIHelpers::warning(m_mainWindow, "Python failure", message);
168 
169  } else if (funcNames.size() == 1) {
170  return funcNames.front();
171 
172  } else {
173  ComboSelectorDialog dialog;
174  dialog.addItems(funcNames, getCandidate(funcNames));
175  dialog.setTextTop("Python code contains a few functions. Do you know by chance, "
176  "which one is intended to produce a valid MultiLayer?");
177  dialog.setTextBottom(
178  "Please select a valid function in combo box and press OK to continue.");
179  if (dialog.exec() == QDialog::Accepted)
180  result = dialog.currentText();
181  }
182 
183  return result;
184 }
185 
186 //! Creates a multi-layer by executing a funcName in embedded Python.
187 //! Function is supposed to be in code provided by 'snippet'.
188 
189 std::unique_ptr<MultiLayer> PyImportAssistant::createMultiLayer(const QString& snippet,
190  const QString& funcName)
191 {
192  std::unique_ptr<MultiLayer> result;
193 
194  QApplication::setOverrideCursor(Qt::WaitCursor);
195  try {
196  result = PyImport::createFromPython(snippet.toStdString(), funcName.toStdString(),
197  bornagainDir());
198 
199  } catch (const std::exception& ex) {
200  QApplication::restoreOverrideCursor();
201  QString message("Exception thrown while executing Python code to create multilayer.\n\n");
202  QString details = QString::fromStdString(std::string(ex.what()));
203  DetailedMessageBox(m_mainWindow, "Python failure", message, details).exec();
204  }
205  QApplication::restoreOverrideCursor();
206 
207  return result;
208 }
209 
210 //! Populates GUI models with domain multilayer.
211 
212 void PyImportAssistant::populateModels(const MultiLayer& multilayer, const QString& sampleName)
213 {
214  try {
215  QString name = sampleName;
216  if (multilayer.getName() != "MultiLayer")
217  name = QString::fromStdString(multilayer.getName());
218 
220  m_mainWindow->materialModel(), multilayer, name);
221 
222  QString message("Seems that import was successfull.\n\n"
223  "Check SampleView for new sample and material editor for new materials.");
224  GUIHelpers::information(m_mainWindow, "PyImport", message);
225 
226  } catch (const std::exception& ex) {
227  QString message("Exception thrown while trying to build GUI models.\n"
228  "GUI models might be in unconsistent state.\n\n");
229  QString details = QString::fromStdString(std::string(ex.what()));
230  DetailedMessageBox(m_mainWindow, "GUIObjectBuilder failure", message, details).exec();
231  }
232 }
233 
234 #endif // BORNAGAIN_PYTHON
Defines class AppSvc.
Defines class ComboSelectorDialog.
Defines class DetailedMessageBox.
Defines class GUIHelpers functions.
Defines GUIObjectBuilder namespace.
Defines class MainWindow.
Defines class ProjectManager.
Defines class MultiLayer.
Defines ProjectUtils namespace.
Implements class PyImportAssistant.
Defines PyImport namespace.
Defines various stuff in namespace Utils.
static ProjectManager * projectManager()
Definition: AppSvc.cpp:18
A dialog similar to standard QMessageBox with combo box selector.
void addItems(const QStringList &selection, const QString &currentItem="")
void setTextTop(const QString &text)
void setTextBottom(const QString &text)
QString currentText() const
A dialog similar to standard QMessageBox intended for detailed warning messages.
const std::string & getName() const
SampleModel * sampleModel()
Definition: mainwindow.cpp:144
MaterialModel * materialModel()
Definition: mainwindow.cpp:134
Our sample model: a stack of layers one below the other.
Definition: MultiLayer.h:41
void setImportDir(const QString &dirname)
Sets user import directory in system settings.
QString userImportDir() const
Returns directory name which was used by the user to import files.
QString fileNameToOpen()
Lets user to select Python file on disk.
QString readFile(const QString &fileName)
Read content of text file and returns it as a multi-line string.
QString getPySampleFunctionName(const QString &snippet)
Returns the name of function which might generate a MultiLayer in Python code snippet.
void populateModels(const MultiLayer &multilayer, const QString &sampleName)
Populates GUI models with domain multilayer.
MainWindow * m_mainWindow
PyImportAssistant(MainWindow *mainwin)
void saveImportDir(const QString &fileName)
Saves file location as a future import dir.
std::unique_ptr< MultiLayer > createMultiLayer(const QString &snippet, const QString &funcName)
Creates a multi-layer by executing a funcName in embedded Python.
QString selectPySampleFunction(const QStringList &funcNames)
Lets user select a function name which generates a MultiLayer.
void warning(QWidget *parent, const QString &title, const QString &text, const QString &detailedText)
Definition: GUIHelpers.cpp:74
QString baseName(const QString &fileName)
Returns base name of file.
Definition: GUIHelpers.cpp:204
void information(QWidget *parent, const QString &title, const QString &text, const QString &detailedText)
Definition: GUIHelpers.cpp:54
QString fileDir(const QString &fileName)
Returns file directory from the full file path.
Definition: GUIHelpers.cpp:193
QStringList fromStdStrings(const std::vector< std::string > &container)
Definition: GUIHelpers.cpp:233
SessionItem * populateSampleModel(SampleModel *sampleModel, MaterialModel *materialModel, const MultiLayer &sample, const QString &sample_name="")
QString readTextFile(const QString &fileName)
Returns multi-lione string representing content of text file.
std::vector< std::string > listOfFunctions(const std::string &script, const std::string &path="")
Returns list of functions defined in the script.
Definition: PyImport.cpp:86
std::unique_ptr< MultiLayer > createFromPython(const std::string &script, const std::string &functionName, const std::string &path="")
Creates a multi layer by running python code in embedded interpreter.
Definition: PyImport.cpp:34
QString const & name(EShape k)
Definition: particles.cpp:21
std::string getenv(const std::string &name)
Returns environment variable.
Definition: SysUtils.cpp:48
Definition: filesystem.h:81