Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
QmitkPythonSnippets.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #include "QmitkPythonSnippets.h"
15 #include <mitkCommon.h>
16 #include <memory>
17 
18 #include <QToolBar>
19 #include <QComboBox>
20 #include <QTextEdit>
21 #include <QGridLayout>
22 #include <QDir>
23 #include <QAction>
24 #include <QInputDialog>
25 #include <QLineEdit>
26 #include <QMessageBox>
27 #include <QXmlStreamWriter>
28 #include <QXmlStreamReader>
29 #include <QResource>
30 #include <QFileDialog>
31 
32 struct QmitkPythonSnippetsData
33 {
34  QString m_AutoSaveFileName;
35  QString m_SaveFileName;
36 
37  QAction* m_PasteSnippet;
38  QAction* m_RemoveSnippet;
39  QAction* m_RenameSnippet;
40  QAction* m_AddSnippet;
41  QAction* m_RestoreDefaultSnippets;
42  QAction* m_LoadSnippets;
43  QAction* m_SaveSnippets;
44 
45  QToolBar* m_Toolbar;
46 
47  QComboBox* m_Name;
48 
49  QTextEdit* m_Content;
50 
51  QGridLayout* m_Layout;
52 
54 };
55 
56 const QString QmitkPythonSnippets::DEFAULT_SNIPPET_FILE( ":/mitkPython/PythonSnippets.xml" );
57 const QString QmitkPythonSnippets::SNIPPETS_ROOT_XML_ELEMENT_NAME( "PythonSnippets" );
58 const QString QmitkPythonSnippets::SNIPPETS_XML_ELEMENT_NAME( "PythonSnippet" );
59 
60 QmitkPythonSnippets::QmitkPythonSnippets( const QString& _AutoSaveFileName, QWidget* parent )
61 : QWidget(parent), d(new QmitkPythonSnippetsData)
62 {
63  d->m_SaveFileName = QDir::currentPath();
64  d->m_AutoSaveFileName = _AutoSaveFileName;
65 
66  if( !QmitkPythonSnippets::LoadStringMap( d->m_AutoSaveFileName, d->m_Snippets ) )
67  {
69  }
70 
71  d->m_PasteSnippet = new QAction(this);
72  d->m_PasteSnippet->setObjectName(QString::fromUtf8("PasteSnippet"));
73  QIcon icon;
74  icon.addFile(QString::fromUtf8(":/mitkPython/edit-paste.png"), QSize(), QIcon::Normal, QIcon::Off);
75  d->m_PasteSnippet->setIcon(icon);
76  d->m_PasteSnippet->setToolTip("Paste snippet!");
77  d->m_PasteSnippet->setEnabled(false);
78 
79  d->m_RemoveSnippet = new QAction(this);
80  d->m_RemoveSnippet->setObjectName(QString::fromUtf8("RemoveSnippet"));
81  QIcon icon1;
82  icon1.addFile(QString::fromUtf8(":/mitkPython/edit-delete.png"), QSize(), QIcon::Normal, QIcon::Off);
83  d->m_RemoveSnippet->setIcon(icon1);
84  d->m_RemoveSnippet->setToolTip("Remove snippet.");
85  d->m_RemoveSnippet->setEnabled(false);
86 
87  d->m_RenameSnippet = new QAction(this);
88  d->m_RenameSnippet->setObjectName(QString::fromUtf8("RenameSnippet"));
89  QIcon icon2;
90  icon2.addFile(QString::fromUtf8(":/mitkPython/edit-find-replace.png"), QSize(), QIcon::Normal, QIcon::Off);
91  d->m_RenameSnippet->setIcon(icon2);
92  d->m_RenameSnippet->setToolTip("Rename snippet.");
93  d->m_RenameSnippet->setEnabled(false);
94 
95  d->m_AddSnippet = new QAction(this);
96  d->m_AddSnippet->setObjectName(QString::fromUtf8("AddSnippet"));
97  QIcon icon3;
98  icon3.addFile(QString::fromUtf8(":/mitkPython/document-new.png"), QSize(), QIcon::Normal, QIcon::Off);
99  d->m_AddSnippet->setIcon(icon3);
100  d->m_AddSnippet->setToolTip("Add snippet.");
101 
102  d->m_RestoreDefaultSnippets = new QAction(this);
103  d->m_RestoreDefaultSnippets->setObjectName(QString::fromUtf8("RestoreDefaultSnippets"));
104  QIcon icon4;
105  icon4.addFile(QString::fromUtf8(":/mitkPython/edit-clear.png"), QSize(), QIcon::Normal, QIcon::Off);
106  d->m_RestoreDefaultSnippets->setIcon(icon4);
107  d->m_RestoreDefaultSnippets->setToolTip("Restore default snippets");
108 
109  d->m_LoadSnippets = new QAction(this);
110  d->m_LoadSnippets->setToolTip("Load Snippets from disk.");
111  d->m_LoadSnippets->setObjectName(QString::fromUtf8("LoadSnippets"));
112  QIcon icon5;
113  icon5.addFile(QString::fromUtf8(":/mitkPython/document-open.png"), QSize(), QIcon::Normal, QIcon::Off);
114  d->m_LoadSnippets->setIcon(icon5);
115 
116  d->m_SaveSnippets = new QAction(this);
117  d->m_SaveSnippets->setToolTip("Save Snippets to disk.");
118  d->m_SaveSnippets->setObjectName(QString::fromUtf8("SaveSnippets"));
119  QIcon icon6;
120  icon6.addFile(QString::fromUtf8(":/mitkPython/document-save.png"), QSize(), QIcon::Normal, QIcon::Off);
121  d->m_SaveSnippets->setIcon(icon6);
122  d->m_SaveSnippets->setEnabled(false);
123 
124  d->m_Toolbar = new QToolBar;
125  d->m_Toolbar->addAction( d->m_PasteSnippet );
126  d->m_Toolbar->addAction( d->m_AddSnippet );
127  d->m_Toolbar->addAction( d->m_RemoveSnippet );
128  d->m_Toolbar->addAction( d->m_RenameSnippet );
129  d->m_Toolbar->addAction( d->m_RestoreDefaultSnippets );
130  d->m_Toolbar->addAction( d->m_SaveSnippets );
131  d->m_Toolbar->addAction( d->m_LoadSnippets );
132 
133  d->m_Name = new QComboBox;
134  d->m_Name->setObjectName(QString::fromUtf8("Name"));
135 
136  d->m_Content = new QTextEdit(this);
137  d->m_Content->setObjectName(QString::fromUtf8("Content"));
138  d->m_Content->setEnabled(false);
139 
140  new QmitkPythonScriptEditorHighlighter( d->m_Content->document() );
141 
142  d->m_Layout = new QGridLayout;
143  d->m_Layout->addWidget( d->m_Toolbar, 0, 0, 1, 1 );
144  d->m_Layout->addWidget( d->m_Name, 1, 0, 1, 1 );
145  d->m_Layout->addWidget( d->m_Content, 2, 0, 1, 1 );
146  d->m_Layout->setContentsMargins(2,2,2,2);
147 
148  this->setLayout(d->m_Layout);
149  QMetaObject::connectSlotsByName(this);
150 
151  this->Update();
152 }
153 
155 {
156  delete d;
157 }
158 
160 {
161  emit PasteCommandRequested( d->m_Content->toPlainText() );
162 }
163 
165 {
166  QString oldname = d->m_Name->currentText();
167  QString name = oldname;
168  bool ok = false;
169  while( true )
170  {
171  name = QInputDialog::getText(this,
172  tr("Add new snippet"),
173  tr("Name of snippet:"),
174  QLineEdit::Normal,
175  name,
176  &ok);
177 
178  if (ok)
179  {
180  if ( d->m_Snippets.contains(name) )
181  {
182  QMessageBox::warning(this,
183  tr("Duplicate name."),
184  tr("The entered name already exists. Enter another one or cancel the operation."),
185  QMessageBox::Ok,
186  QMessageBox::Ok );
187  }
188  else
189  {
190  QString tmpSnippet = d->m_Snippets[oldname];
191  d->m_Snippets.remove(oldname);
192  d->m_Snippets[name] = tmpSnippet;
193  this->Update(name);
194  this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets );
195  break;
196  }
197  }
198  else
199  {
200  break;
201  }
202  }
203 }
204 
206 {
207  bool ok;
208  QString name = QInputDialog::getText(this,
209  tr("Add new snippet"),
210  tr("Name of snippet:"),
211  QLineEdit::Normal,
212  "newSnippet",
213  &ok);
214  if (ok && !name.isEmpty())
215  {
216  MITK_DEBUG("QmitkPythonSnippets") << "creating unique name for " << name.toStdString();
217  name = this->CreateUniqueName(name);
218 
219  MITK_DEBUG("QmitkPythonSnippets") << "creating snippet " << name.toStdString();
220  d->m_Snippets[name] = "";
221  this->Update(name);
222  this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets );
223  }
224 }
225 
226 QString QmitkPythonSnippets::CreateUniqueName( const QString& name ) const
227 {
228  QString newName = name;
229  size_t i = 2;
230  while( d->m_Snippets.contains(name) )
231  {
232  newName = name + QString("_%1").arg(i);
233  ++i;
234  }
235 
236  return newName;
237 }
238 
240 {
241  QString name = d->m_Name->currentText();
242  QString question = QString("Really remove Snippet %1?").arg(name);
243  int remove = QMessageBox::question( this,
244  QString("Confirm removal"),
245  question,
246  QMessageBox::Yes | QMessageBox::No,
247  QMessageBox::No );
248 
249  if( remove == QMessageBox::Yes || remove == QMessageBox::Ok )
250  {
251  d->m_Snippets.remove(name);
252  this->Update();
253  this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets );
254  }
255 }
256 
258 {
259  QString question = QString("Really restore default Snippets?");
260  int remove = QMessageBox::question( this,
261  QString("Confirm restoring"),
262  question,
263  QMessageBox::Yes | QMessageBox::No,
264  QMessageBox::No );
265  if( remove == QMessageBox::Yes || remove == QMessageBox::Ok )
266  {
268  this->Update();
269  this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets );
270  }
271 }
272 
274 {
275  bool validSelection = i >= 0 ;
276 
277  d->m_PasteSnippet->setEnabled(validSelection);
278  d->m_RemoveSnippet->setEnabled(validSelection);
279  d->m_RenameSnippet->setEnabled(validSelection);
280  d->m_Content->setEnabled(validSelection);
281  d->m_SaveSnippets->setEnabled(validSelection);
282 
283  if( validSelection )
284  {
285  QString name = d->m_Name->currentText();
286  MITK_DEBUG("QmitkPythonSnippets") << "selected snippet " << name.toStdString();
287  d->m_Content->setText( d->m_Snippets[name] );
288  MITK_DEBUG("QmitkPythonSnippets") << "selected snippet content " << d->m_Snippets[name].toStdString();
289  }
290 }
291 
292 void QmitkPythonSnippets::SaveStringMap(const QString &filename, const QmitkPythonSnippets::QStringMap &) const
293 {
294  MITK_DEBUG("QmitkPythonSnippets") << "saving to xml file " << filename.toStdString();
295 
296  if( filename.isEmpty() )
297  {
298  MITK_WARN("QmitkPythonSnippets") << "empty auto save file path given. quit.";
299  return;
300  }
301 
302  QFile file(filename);
303  file.open(QIODevice::WriteOnly);
304  if( !file.isOpen() )
305  {
306  MITK_WARN("QmitkPythonSnippets") << "could not open file " << filename.toStdString() << " for writing";
307  return;
308  }
309  QXmlStreamWriter xmlWriter(&file);
310 
311  xmlWriter.setAutoFormatting(true);
312  xmlWriter.writeStartDocument();
313  xmlWriter.writeStartElement(SNIPPETS_ROOT_XML_ELEMENT_NAME);
314 
315  QStringMap::const_iterator it = d->m_Snippets.begin();
316  while( it != d->m_Snippets.end() )
317  {
318 
319  {
320  MITK_DEBUG("QmitkPythonSnippets") << "SNIPPETS_XML_ELEMENT_NAME " << SNIPPETS_XML_ELEMENT_NAME.toStdString();
321  MITK_DEBUG("QmitkPythonSnippets") << "writing item " << it.key().toStdString();
322  }
323 
324  xmlWriter.writeStartElement(SNIPPETS_XML_ELEMENT_NAME);
325 
326  xmlWriter.writeAttribute( "key", it.key() );
327  xmlWriter.writeAttribute( "value", it.value() );
328 
329  xmlWriter.writeEndElement();
330 
331  ++it;
332  }
333 
334  xmlWriter.writeEndDocument();
335  if( file.isOpen() )
336  file.close();
337 
338  {
339  MITK_DEBUG("QmitkPythonSnippets") << "SaveStringMap successful ";
340  }
341 
342 }
343 
345 {
346  MITK_DEBUG("QmitkPythonSnippets") << "loading from xml file " << filename.toStdString();
347  QStringMap map;
348 
349  QXmlStreamReader xmlReader;
350  QFile file;
351  QByteArray data;
352  // resource file
353  if( filename.startsWith(":") )
354  {
355  QResource res( filename );
356  data = QByteArray( reinterpret_cast< const char* >( res.data() ), res.size() );
357  xmlReader.addData( data );
358  }
359  else
360  {
361  file.setFileName( filename );
362  if (!file.open(QFile::ReadOnly | QFile::Text))
363  {
364  MITK_ERROR << "Error: Cannot read file " << qPrintable(filename)
365  << ": " << qPrintable(file.errorString());
366  return false;
367  }
368  xmlReader.setDevice(&file);
369  }
370 
371  xmlReader.readNext();
372 
373  while(!xmlReader.atEnd())
374  {
375  xmlReader.readNext();
376 
377  if(xmlReader.name() == SNIPPETS_XML_ELEMENT_NAME)
378  {
379  QXmlStreamAttributes attributes = xmlReader.attributes();
380  QString key;
381  QString value;
382  if(attributes.hasAttribute("key"))
383  {
384  key = attributes.value("key").toString();
385  }
386 
387  if(attributes.hasAttribute("value"))
388  {
389  value = attributes.value("value").toString();
390  }
391 
392  if( !key.isEmpty() )
393  {
394  MITK_DEBUG("QmitkPythonSnippets") << "loaded snippet " << key.toStdString();
395  MITK_DEBUG("QmitkPythonSnippets") << "value " << value.toStdString();
396  map[key] = value;
397  }
398  }
399  }
400 
401  if (xmlReader.hasError())
402  {
403  MITK_ERROR << "Error: Failed to parse file "
404  << qPrintable(filename) << ": "
405  << qPrintable(xmlReader.errorString());
406  return false;
407  }
408  else if (file.error() != QFile::NoError)
409  {
410  MITK_ERROR << "Error: Cannot read file " << qPrintable(filename)
411  << ": " << qPrintable(file.errorString());
412  return false;
413  }
414 
415  if( file.isOpen() )
416  file.close();
417 
418  oldMap = map;
419  return true;
420 }
421 
422 void QmitkPythonSnippets::Update(const QString &name)
423 {
424  d->m_Name->clear();
425  d->m_Content->clear();
426 
427  MITK_DEBUG("QmitkPythonSnippets") << "size of snippets " << d->m_Snippets.size();
428  QStringMap::const_iterator it = d->m_Snippets.begin();
429 
430  while( it != d->m_Snippets.end() )
431  {
432  MITK_DEBUG("QmitkPythonSnippets") << "adding item " << it.key().toStdString();
433  d->m_Name->addItem( it.key() );
434  ++it;
435  }
436 
437  int index = d->m_Name->findText( name );
438  if( index >= 0 )
439  {
440  MITK_DEBUG("QmitkPythonSnippets") << "selecting index " << index;
441  d->m_Name->setCurrentIndex(index);
442  }
443 
444 }
445 
447 {
448  if( d->m_Content->isEnabled() )
449  {
450  QString name = d->m_Name->currentText();
451  QString snippet = d->m_Content->toPlainText();
452  d->m_Snippets[name] = snippet;
453 
454  this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets );
455  MITK_DEBUG("QmitkPythonSnippets") << "SaveStringMap successful";
456  }
457 }
458 
460 {
461  QString fileName = QFileDialog::getSaveFileName(this, "Save snippets", d->m_SaveFileName, "XML files (*.xml)");
462  if( !fileName.isEmpty() )
463  {
464  d->m_SaveFileName = fileName;
465  this->SaveStringMap( d->m_SaveFileName, d->m_Snippets );
466  }
467 }
468 
470 {
471  QString fileName = QFileDialog::getOpenFileName(this, "Load snippets", d->m_SaveFileName, "XML files (*.xml)");
472 
473  if( !fileName.isEmpty() )
474  {
475  d->m_SaveFileName = fileName;
476  QString question = QString("Your current snippets will be overwritten. Proceed?");
477  int overwrite = QMessageBox::warning(this,
478  QString("Confirm overwrite"),
479  question,
480  QMessageBox::Yes | QMessageBox::No,
481  QMessageBox::No );
482 
483  if( overwrite == QMessageBox::Yes )
484  {
485  this->LoadStringMap( d->m_SaveFileName, d->m_Snippets );
486  this->Update( d->m_Name->currentText() );
487  this->SaveStringMap( d->m_AutoSaveFileName, d->m_Snippets );
488  }
489  }
490 
491 }
static const QString DEFAULT_SNIPPET_FILE
QMap< QString, QString > QStringMap
typedef for string map
#define MITK_ERROR
Definition: mitkLogMacros.h:20
void on_LoadSnippets_triggered(bool checked=false)
ask for file, load snippets (do not replace)
void PasteCommandRequested(const QString &command)
this class whishes to paste sth command
#define MITK_DEBUG
Definition: mitkLogMacros.h:22
void Update(const QString &name="")
void on_AddSnippet_triggered(bool checked=false)
ask for name, create snippet, call update()
static const QString SNIPPETS_ROOT_XML_ELEMENT_NAME
#define MITK_WARN
Definition: mitkLogMacros.h:19
void on_Content_textChanged()
save changed snippet
void on_SaveSnippets_triggered(bool checked=false)
ask for file, save snippets
void on_PasteSnippet_triggered(bool checked=false)
emits PasteCommandRequested signal
void on_RenameSnippet_triggered(bool checked=false)
ask for name as long as it exists, call update()
void on_RestoreDefaultSnippets_triggered(bool checked=false)
call LoadStringMap with d->m_DefaultSnippetsAutoSaveFileName
static bool LoadStringMap(const QString &filename, QStringMap &oldMap)
read string map from xml file
void on_Name_currentIndexChanged(int i)
update action state (enable/disable), update text box
MITKMATCHPOINTREGISTRATION_EXPORT ResultImageType::Pointer map(const InputImageType *input, const RegistrationType *registration, bool throwOnOutOfInputAreaError=false, const double &paddingValue=0, const ResultImageGeometryType *resultGeometry=nullptr, bool throwOnMappingError=true, const double &errorValue=0, mitk::ImageMappingInterpolator::Type interpolatorType=mitk::ImageMappingInterpolator::Linear)
A script highlighter for Python Scripts.
static const QString SNIPPETS_XML_ELEMENT_NAME
~QmitkPythonSnippets() override
delete d pointer
QmitkPythonSnippets(const QString &_AutoSaveFileName="", QWidget *parent=nullptr)
void on_RemoveSnippet_triggered(bool checked=false)
remove the current snippet, call update()
void SaveStringMap(const QString &filename, const QStringMap &map) const
write string map to xml file
QString CreateUniqueName(const QString &name) const
creates a name which does not exist in the list