diff --git a/Modules/Python/QmitkCtkPythonShell.cpp b/Modules/Python/QmitkCtkPythonShell.cpp index 2d14edd4a3..58b62e522e 100644 --- a/Modules/Python/QmitkCtkPythonShell.cpp +++ b/Modules/Python/QmitkCtkPythonShell.cpp @@ -1,72 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkCtkPythonShell.h" #include #include #include #include #include #include "mitkPythonService.h" #include #include #include struct QmitkCtkPythonShellData { mitk::PythonService* m_PythonService; }; QmitkCtkPythonShell::QmitkCtkPythonShell(QWidget* parent) : ctkPythonConsole(parent), d( new QmitkCtkPythonShellData ) { mitk::ModuleContext* context = mitk::GetModuleContext(); mitk::ServiceReference serviceRef = context->GetServiceReference(); d->m_PythonService = dynamic_cast ( context->GetService(serviceRef) ); assert( d->m_PythonService ); this->initialize( d->m_PythonService->GetPythonManager() ); } QmitkCtkPythonShell::~QmitkCtkPythonShell() { delete d; } void QmitkCtkPythonShell::dragEnterEvent(QDragEnterEvent *event) { event->accept(); } void QmitkCtkPythonShell::dropEvent(QDropEvent *event) { QList urls = event->mimeData()->urls(); for(int i = 0; i < urls.size(); i++) { d->m_PythonService->Execute( urls[i].toString(), mitk::IPythonService::SINGLE_LINE_COMMAND ); } } bool QmitkCtkPythonShell::canInsertFromMimeData( const QMimeData *source ) const { return true; } void QmitkCtkPythonShell::executeCommand(const QString& command) { ctkPythonConsole::executeCommand(command); d->m_PythonService->NotifyObserver(command); } + +void QmitkCtkPythonShell::Paste(const QString &command) +{ + if( this->isVisible() ) + { + this->executeCommand( command ); + } +} diff --git a/Modules/Python/QmitkCtkPythonShell.h b/Modules/Python/QmitkCtkPythonShell.h index 1b737cc26d..1e50cf4d2d 100644 --- a/Modules/Python/QmitkCtkPythonShell.h +++ b/Modules/Python/QmitkCtkPythonShell.h @@ -1,58 +1,60 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkCtkPythonShell_h #define QmitkCtkPythonShell_h #include #include #include "mitkPythonExports.h" /// /// forward declarations /// struct QmitkCtkPythonShellData; class ctkAbstractPythonManager; class QDragEnterEvent; class QDropEvent; class QMimeData; /// /// Reimplements the ctkPythonConsole with drag and drop functionality for text /// Furthermore it calls NotifyObserver() on the IPythonService to inform listeners /// class MITK_PYTHON_EXPORT QmitkCtkPythonShell : public ctkPythonConsole { Q_OBJECT public: QmitkCtkPythonShell(QWidget* parent = 0); ~QmitkCtkPythonShell(); +public slots: + void Paste( const QString& command ); protected: void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); bool canInsertFromMimeData( const QMimeData *source ) const; void executeCommand(const QString& command); private: QmitkCtkPythonShellData* d; }; #endif // QmitkCtkPythonShell_h diff --git a/Modules/Python/QmitkPythonSnippets.cpp b/Modules/Python/QmitkPythonSnippets.cpp index 1f4acc637a..5803da3614 100644 --- a/Modules/Python/QmitkPythonSnippets.cpp +++ b/Modules/Python/QmitkPythonSnippets.cpp @@ -1,305 +1,452 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkPythonSnippets.h" #include "QmitkPythonScriptEditorHighlighter.h" #include - -QmitkPythonSnippets::QStringMap QmitkPythonSnippets::CreateDefaultSnippets() -{ - QStringMap defaultSnippets; - - defaultSnippets["itk image processing"] = - "import itk\n" - "import mitkCast\n\n" - "print 'using ITK ' + itk.Version.GetITKVersion()\n" - "itkImage = mitkCast.node2itk( Pic3D, itk.Image.US3 )\n" - "kernel = itk.strel( 3, 3 )\n" - "filter = itk.GrayscaleDilateImageFilter[itkImage,itkImage,kernel].New( itkImage, Kernel=kernel )\n" - "filter.Update()\n" - "Pic3DFiltered = mitkCast.itk2node( filter.GetOutput(), 'Pic3DFiltered' )\n"; - - defaultSnippets["itk batch image processing"] = - "import itk\n" - "import mitkCast\n\n" - "print 'using ITK ' + itk.Version.GetITKVersion()\n" - "itkImage = mitkcast.node2itk( Pic3D, itk.Image.US3 )\n" - "for s in range(5):\n" - "kernel = itk.strel( 3, s+1 )\n" - "filter = itk.GrayscaleDilateImageFilter[itkImage,itkImage,kernel].New( itkImage, Kernel=kernel )\n" - "filter.Update()\n" - "exec( \"newnode_\" + str(s+1) + \" = mitkcast.itk2node( filter.GetOutput(), 'demo_\" + str(s+1) + \"')\" )\n"; - - defaultSnippets["Import itk and vtk"] = - "import itk, vtk"; - - return defaultSnippets; -} +#include struct QmitkPythonSnippetsData { - QString m_FilePath; + QString m_AutoSaveFileName; + QString m_SaveFileName; QAction* m_PasteSnippet; QAction* m_RemoveSnippet; QAction* m_RenameSnippet; QAction* m_AddSnippet; QAction* m_RestoreDefaultSnippets; + QAction* m_LoadSnippets; + QAction* m_SaveSnippets; + QToolBar* m_Toolbar; QComboBox* m_Name; QTextEdit* m_Content; QGridLayout* m_Layout; + + QmitkPythonSnippets::QStringMap m_Snippets; }; -const QmitkPythonSnippets::QStringMap QmitkPythonSnippets::DEFAULT_SNIPPETS - = QmitkPythonSnippets::CreateDefaultSnippets(); +const QString QmitkPythonSnippets::DEFAULT_SNIPPET_FILE( ":/mitkPython/PythonSnippets.xml" ); +const QString QmitkPythonSnippets::SNIPPETS_ROOT_XML_ELEMENT_NAME( "PythonSnippets" ); +const QString QmitkPythonSnippets::SNIPPETS_XML_ELEMENT_NAME( "PythonSnippet" ); -QmitkPythonSnippets::QmitkPythonSnippets(QWidget* parent) +QmitkPythonSnippets::QmitkPythonSnippets( const QString& _AutoSaveFileName, QWidget* parent ) : QWidget(parent), d(new QmitkPythonSnippetsData) { + d->m_SaveFileName = QDir::currentPath(); d->m_PasteSnippet = new QAction(this); d->m_PasteSnippet->setObjectName(QString::fromUtf8("PasteSnippet")); QIcon icon; icon.addFile(QString::fromUtf8(":/mitkPython/edit-paste.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_PasteSnippet->setIcon(icon); + d->m_PasteSnippet->setToolTip("Paste snippet!"); + d->m_PasteSnippet->setEnabled(false); d->m_RemoveSnippet = new QAction(this); d->m_RemoveSnippet->setObjectName(QString::fromUtf8("RemoveSnippet")); QIcon icon1; icon1.addFile(QString::fromUtf8(":/mitkPython/edit-delete.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_RemoveSnippet->setIcon(icon1); + d->m_RemoveSnippet->setToolTip("Remove snippet."); + d->m_RemoveSnippet->setEnabled(false); d->m_RenameSnippet = new QAction(this); d->m_RenameSnippet->setObjectName(QString::fromUtf8("RenameSnippet")); QIcon icon2; icon2.addFile(QString::fromUtf8(":/mitkPython/edit-find-replace.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_RenameSnippet->setIcon(icon2); + d->m_RenameSnippet->setToolTip("Rename snippet."); + d->m_RenameSnippet->setEnabled(false); d->m_AddSnippet = new QAction(this); d->m_AddSnippet->setObjectName(QString::fromUtf8("AddSnippet")); QIcon icon3; icon3.addFile(QString::fromUtf8(":/mitkPython/document-new.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_AddSnippet->setIcon(icon3); + d->m_AddSnippet->setToolTip("Add snippet."); d->m_RestoreDefaultSnippets = new QAction(this); d->m_RestoreDefaultSnippets->setObjectName(QString::fromUtf8("RestoreDefaultSnippets")); QIcon icon4; icon4.addFile(QString::fromUtf8(":/mitkPython/edit-clear.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_RestoreDefaultSnippets->setIcon(icon4); + d->m_RestoreDefaultSnippets->setToolTip("Restore default snippets"); + + d->m_LoadSnippets = new QAction(this); + d->m_LoadSnippets->setToolTip("Load Snippets from disk."); + d->m_LoadSnippets->setObjectName(QString::fromUtf8("LoadSnippets")); + QIcon icon5; + icon5.addFile(QString::fromUtf8(":/mitkPython/document-open.png"), QSize(), QIcon::Normal, QIcon::Off); + d->m_LoadSnippets->setIcon(icon5); + + d->m_SaveSnippets = new QAction(this); + d->m_SaveSnippets->setToolTip("Save Snippets to disk."); + d->m_SaveSnippets->setObjectName(QString::fromUtf8("SaveSnippets")); + QIcon icon6; + icon6.addFile(QString::fromUtf8(":/mitkPython/document-save.png"), QSize(), QIcon::Normal, QIcon::Off); + d->m_SaveSnippets->setIcon(icon6); + d->m_SaveSnippets->setEnabled(false); d->m_Toolbar = new QToolBar; d->m_Toolbar->addAction( d->m_PasteSnippet ); d->m_Toolbar->addAction( d->m_AddSnippet ); d->m_Toolbar->addAction( d->m_RemoveSnippet ); d->m_Toolbar->addAction( d->m_RenameSnippet ); d->m_Toolbar->addAction( d->m_RestoreDefaultSnippets ); + d->m_Toolbar->addAction( d->m_SaveSnippets ); + d->m_Toolbar->addAction( d->m_LoadSnippets ); d->m_Name = new QComboBox; d->m_Name->setObjectName(QString::fromUtf8("Name")); - d->m_Name->setEditable(true); d->m_Content = new QTextEdit(this); d->m_Content->setObjectName(QString::fromUtf8("Content")); + d->m_Content->setEnabled(false); + + QmitkPythonScriptEditorHighlighter* highlighter = + new QmitkPythonScriptEditorHighlighter( d->m_Content->document() ); d->m_Layout = new QGridLayout; d->m_Layout->addWidget( d->m_Toolbar, 0, 0, 1, 1 ); d->m_Layout->addWidget( d->m_Name, 1, 0, 1, 1 ); d->m_Layout->addWidget( d->m_Content, 2, 0, 1, 1 ); d->m_Layout->setContentsMargins(2,2,2,2); this->setLayout(d->m_Layout); QMetaObject::connectSlotsByName(this); + + d->m_AutoSaveFileName = _AutoSaveFileName; + + if( !this->StringMapFromXmlFile( d->m_AutoSaveFileName, d->m_Snippets ) ) + { + this->StringMapFromXmlFile( DEFAULT_SNIPPET_FILE, d->m_Snippets ); + } + + this->Update(); } QmitkPythonSnippets::~QmitkPythonSnippets() { delete d; } -void QmitkPythonSnippets::SetFilePath(const QString &filePath) +void QmitkPythonSnippets::on_PasteSnippet_triggered( bool ) { - d->m_FilePath = filePath; + emit PasteCommandRequested( d->m_Content->toPlainText() ); } -void QmitkPythonSnippets::on_PasteSnippet_triggered( bool ) +void QmitkPythonSnippets::on_RenameSnippet_triggered(bool) { - emit PasteCommandRequested( d->m_Content->toPlainText() ); + QString oldname = d->m_Name->currentText(); + QString name = oldname; + bool ok = false; + while( true ) + { + name = QInputDialog::getText(this, + tr("Add new snippet"), + tr("Name of snippet:"), + QLineEdit::Normal, + name, + &ok); + + if (ok) + { + if ( d->m_Snippets.contains(name) ) + { + QMessageBox::warning(this, + tr("Duplicate name."), + tr("The entered name already exists. Enter another one or cancel the operation."), + QMessageBox::Ok, + QMessageBox::Ok ); + } + else + { + QString tmpSnippet = d->m_Snippets[oldname]; + d->m_Snippets.remove(oldname); + d->m_Snippets[name] = tmpSnippet; + this->Update(name); + break; + } + } + else + { + break; + } + } } -/* -void QmitkPythonSnippets::on_Name_currentIndexChanged(int i) +void QmitkPythonSnippets::on_AddSnippet_triggered(bool) { -// std::cout << "on_Name_currentIndexChanged" << std::endl; - if( m_Controls->Name->count() == 0 ) - return; - QString name = m_Controls->Name->currentText(); - m_Controls->Content->setText( QString::fromStdString(m_Snippets[name.toStdString()]) ); + bool ok; + QString name = QInputDialog::getText(this, + tr("Add new snippet"), + tr("Name of snippet:"), + QLineEdit::Normal, + "newSnippet", + &ok); + if (ok && !name.isEmpty()) + { + MITK_DEBUG("QmitkPythonSnippets") << "creating unique name for " << name.toStdString(); + name = this->CreateUniqueName(name); + + MITK_DEBUG("QmitkPythonSnippets") << "creating snippet " << name.toStdString(); + d->m_Snippets[name] = ""; + this->Update(name); + } } -void QmitkPythonSnippets::on_RestoreDefaultSnippets_clicked() +QString QmitkPythonSnippets::CreateUniqueName( const QString& name ) const { - QString question = QString("Really restore default Snippets?"); - int remove = QMessageBox::question( QApplication::topLevelWidgets().at(0), - QString("Confirm restoring"), + QString newName = name; + size_t i = 2; + while( d->m_Snippets.contains(name) ) + { + newName = name + QString("_%1").arg(i); + ++i; + } + + return newName; +} + +void QmitkPythonSnippets::on_RemoveSnippet_triggered(bool) +{ + QString name = d->m_Name->currentText(); + QString question = QString("Really remove Snippet %1?").arg(name); + int remove = QMessageBox::question( this, + QString("Confirm removal"), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); + if( remove == QMessageBox::Yes || remove == QMessageBox::Ok ) { - QStringMap::const_iterator it - = DEFAULT_SNIPPETS.begin(); - while( it != DEFAULT_SNIPPETS.end() ) - { - m_Snippets[it->first] = it->second; - ++it; - } + d->m_Snippets.remove(name); this->Update(); } - -} - -void QmitkPythonSnippets::on_AddNewSnippet_clicked() -{ - QString name = "newSnippet"; - CreateUniqueName(name); - m_Snippets[name.toStdString()] = ""; - m_NameToSelect = name; - this->Update(); } -void QmitkPythonSnippets::on_RemoveSnippet_clicked() +void QmitkPythonSnippets::on_RestoreDefaultSnippets_triggered(bool) { - if( m_Controls->Name->count() == 0 ) - return; - QString name = m_Controls->Name->currentText(); - QString question = QString("Really remove Snippet %1?").arg(name); - int remove = QMessageBox::question( QApplication::topLevelWidgets().at(0), - QString("Confirm removal"), + QString question = QString("Really restore default Snippets?"); + int remove = QMessageBox::question( this, + QString("Confirm restoring"), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); if( remove == QMessageBox::Yes || remove == QMessageBox::Ok ) { - QStringMap::iterator it - = m_Snippets.find( name.toStdString() ); - if( it!= m_Snippets.end() ) - { -// std::cout << "removing " << it->first << std::endl; - m_Snippets.erase(it); - } + this->StringMapFromXmlFile( DEFAULT_SNIPPET_FILE, d->m_Snippets ); this->Update(); } } -void QmitkPythonSnippets::on_Content_textChanged() +void QmitkPythonSnippets::on_Name_currentIndexChanged(int i) { - if( m_Controls->Name->count() == 0 ) - return; + bool validSelection = i >= 0 ; - std::string name = (m_Controls->Name->currentText()).toStdString(); - std::string snippet = m_Controls->Content->toPlainText().toStdString(); + d->m_PasteSnippet->setEnabled(validSelection); + d->m_RemoveSnippet->setEnabled(validSelection); + d->m_RenameSnippet->setEnabled(validSelection); + d->m_Content->setEnabled(validSelection); + d->m_SaveSnippets->setEnabled(validSelection); - if( m_Snippets.find(name) != m_Snippets.end() ) - m_Snippets[name] = snippet; + if( validSelection ) + { + QString name = d->m_Name->currentText(); + d->m_Content->setText( d->m_Snippets[name] ); + } } -void QmitkPythonSnippets::on_RenameSnippet_clicked() +void QmitkPythonSnippets::StringMapToXmlFile(const QString &filename, const QmitkPythonSnippets::QStringMap &map) const { -// std::cout << "on_Name_editTextChanged" << std::endl; - if( m_Controls->Name->count() == 0 ) - return; + MITK_DEBUG("QmitkPythonSnippets") << "saving to xml file " << filename.toStdString(); - QString newName; - bool repeat = false; - do + QXmlStreamWriter* xmlWriter; + QFile file; + QByteArray data; + if( filename.startsWith(":") ) { - newName = QInputDialog::getText( QApplication::topLevelWidgets().at(0), - "Name the Snippet", "Name:", QLineEdit::Normal, - m_NameToSelect, &repeat ); - - if( newName != m_Controls->Name->currentText() ) - { - repeat = m_Snippets.find(newName.toStdString()) != m_Snippets.end() - || newName.isEmpty(); - if( repeat ) - QMessageBox::information( QApplication::topLevelWidgets().at(0) - , QString("Invalid Name"), - "Invalid name. Please enter another one"); - } - else - repeat = false; + QResource res( filename ); + data = QByteArray( reinterpret_cast< const char* >( res.data() ), res.size() ); + xmlWriter = new QXmlStreamWriter(&data); + } + else + { + QFile file(filename); + file.open(QIODevice::WriteOnly); + xmlWriter = new QXmlStreamWriter(&file); } - while( repeat ); + xmlWriter->setAutoFormatting(true); + xmlWriter->writeStartDocument(); + xmlWriter->writeStartElement(SNIPPETS_ROOT_XML_ELEMENT_NAME); - // name changed - if( newName != m_Controls->Name->currentText() ) + QStringMap::const_iterator it = d->m_Snippets.begin(); + while( it != d->m_Snippets.end() ) { - QStringMap::iterator it - = m_Snippets.find( m_NameToSelect.toStdString() ); - std::string snippet = it->second; - m_Snippets.erase(it); - m_Snippets[newName.toStdString()] = snippet; - m_NameToSelect = newName; - this->Update(); + MITK_DEBUG("QmitkPythonSnippets") << "writing item " << it.key().toStdString(); + xmlWriter->writeStartElement(SNIPPETS_XML_ELEMENT_NAME); + + xmlWriter->writeAttribute( "key", it.key() ); + xmlWriter->writeAttribute( "value", it.value() ); + + ++it; } + + xmlWriter->writeEndElement(); + xmlWriter->writeEndDocument(); + if( file.isOpen() ) + file.close(); + + delete xmlWriter; } -void QmitkPythonSnippets::Update() +bool QmitkPythonSnippets::StringMapFromXmlFile( const QString& filename, QmitkPythonSnippets::QStringMap& oldMap ) const { - if( m_NameToSelect.isEmpty() ) + MITK_DEBUG("QmitkPythonSnippets") << "loading from xml file " << filename.toStdString(); + + bool errorOccured = false; + QStringMap map; + QFile file(filename); + if (!file.open(QFile::ReadOnly | QFile::Text)) { - QString name = m_Controls->Name->currentText(); - m_NameToSelect = name; + MITK_ERROR << "Error: Cannot read file " << qPrintable(filename) + << ": " << qPrintable(file.errorString()); + errorOccured = true; } - m_Controls->Name->clear(); - QStringMap::iterator it - = m_Snippets.begin(); + QXmlStreamReader Rxml; + Rxml.setDevice(&file); + Rxml.readNext(); - while( it != m_Snippets.end() ) + while(!Rxml.atEnd()) { - m_Controls->Name->addItem( QString::fromStdString( it->first ) ); - ++it; + Rxml.readNext(); + + if(Rxml.name() == SNIPPETS_XML_ELEMENT_NAME) + { + QXmlStreamAttributes attributes = Rxml.attributes(); + QString key; + QString value; + if(attributes.hasAttribute("key")) + { + key = attributes.value("key").toString(); + } + + if(attributes.hasAttribute("value")) + { + value = attributes.value("value").toString(); + } + + if( !key.isEmpty() ) + { + MITK_DEBUG("QmitkPythonSnippets") << "loaded snippet " << key.toStdString(); + MITK_DEBUG("QmitkPythonSnippets") << "value " << value.toStdString(); + map[key] = value; + } + } } + file.close(); - // reselect previous or last one if there are elements or nothing if there are no snippets - std::string current = m_NameToSelect.toStdString(); - int index = -1; - if( m_Snippets.find(current) != m_Snippets.end() ) + if (Rxml.hasError()) + { + MITK_ERROR << "Error: Failed to parse file " + << qPrintable(filename) << ": " + << qPrintable(Rxml.errorString()); + errorOccured = true; + } + else if (file.error() != QFile::NoError) { - index = m_Controls->Name->findText( m_NameToSelect ); + MITK_ERROR << "Error: Cannot read file " << qPrintable(filename) + << ": " << qPrintable(file.errorString()); + errorOccured = true; } - else if( m_Snippets.size() > 0 ) + + oldMap = map; + return errorOccured; +} + +void QmitkPythonSnippets::Update(const QString &name) +{ + d->m_Name->clear(); + d->m_Content->clear(); + + MITK_DEBUG("QmitkPythonSnippets") << "size of snippets " << d->m_Snippets.size(); + QStringMap::const_iterator it = d->m_Snippets.begin(); + + while( it != d->m_Snippets.end() ) { - index = m_Controls->Name->count()-1; + MITK_DEBUG("QmitkPythonSnippets") << "adding item " << it.key().toStdString(); + d->m_Name->addItem( it.key() ); + ++it; } + int index = d->m_Name->findText( name ); if( index >= 0 ) { - m_Controls->Name->setCurrentIndex(index); - current = m_Controls->Name->itemText( index ).toStdString(); - m_Controls->Content->setPlainText( QString::fromStdString(m_Snippets[current]) ); + MITK_DEBUG("QmitkPythonSnippets") << "selecting index " << index; + d->m_Name->setCurrentIndex(index); + } + +} + +void QmitkPythonSnippets::on_Content_textChanged() +{ + if( d->m_Content->isEnabled() ) + { + QString name = d->m_Name->currentText(); + QString snippet = d->m_Content->toPlainText(); + d->m_Snippets[name] = snippet; + } +} + +void QmitkPythonSnippets::on_SaveSnippets_triggered(bool) +{ + QString fileName = QFileDialog::getSaveFileName(this, "Save snippets", d->m_SaveFileName, "XML files (*.xml)"); + if( !fileName.isEmpty() ) + { + d->m_SaveFileName = fileName; + this->StringMapToXmlFile( d->m_SaveFileName, d->m_Snippets ); + } +} + +void QmitkPythonSnippets::on_LoadSnippets_triggered(bool) +{ + QString fileName = QFileDialog::getOpenFileName(this, "Load snippets", d->m_SaveFileName, "XML files (*.xml)"); + + if( !fileName.isEmpty() ) + { + d->m_SaveFileName = fileName; + QString question = QString("Your current snippets will be overwritten. Proceed?"); + int overwrite = QMessageBox::warning(this, + QString("Confirm overwrite"), + question, + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No ); + + if( overwrite == QMessageBox::Yes ) + { + this->StringMapFromXmlFile( d->m_SaveFileName, d->m_Snippets ); + this->Update( d->m_Name->currentText() ); + } } - // clear the current name - m_NameToSelect.clear(); } -*/ diff --git a/Modules/Python/QmitkPythonSnippets.h b/Modules/Python/QmitkPythonSnippets.h index 7e805c0ae1..9729099f61 100644 --- a/Modules/Python/QmitkPythonSnippets.h +++ b/Modules/Python/QmitkPythonSnippets.h @@ -1,69 +1,104 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _QmitkPythonSnippets_H #define _QmitkPythonSnippets_H #include #include #include "mitkPythonExports.h" struct QmitkPythonSnippetsData; /// /// a widget that holds snippets and serializes the snippets to a certain places class MITK_PYTHON_EXPORT QmitkPythonSnippets: public QWidget { Q_OBJECT public: + static const QString DEFAULT_SNIPPET_FILE; + static const QString SNIPPETS_ROOT_XML_ELEMENT_NAME; + static const QString SNIPPETS_XML_ELEMENT_NAME; + /// + /// typedef for string map typedef QMap QStringMap; /// - /// creates default snippets - static QStringMap CreateDefaultSnippets(); - static const QStringMap DEFAULT_SNIPPETS; - - QmitkPythonSnippets(QWidget* parent=0); + /// build ui here + /// the snippets will be loaded from _AutoSaveFileName if not empty and readable + /// otherwise the default snippets will be loaded + QmitkPythonSnippets( const QString& _AutoSaveFileName="", QWidget* parent=0 ); + /// + /// delete d pointer virtual ~QmitkPythonSnippets(); - - void SetFilePath( const QString& filePath ); - - signals: +signals: + /// + /// this class whishes to paste sth command void PasteCommandRequested(const QString& command); + protected slots: + /// + /// emits PasteCommandRequested signal void on_PasteSnippet_triggered( bool checked = false ); - /* - protected slots: - void on_Name_currentIndexChanged(int i); - void on_RenameSnippet_clicked(); - void on_AddNewSnippet_clicked(); - void on_RemoveSnippet_clicked(); - void on_PasteNow_clicked(); - void on_RestoreDefaultSnippets_clicked(); + /// + /// ask for name as long as it exists, call update() + void on_RenameSnippet_triggered( bool checked = false ); + /// + /// ask for name, create snippet, call update() + void on_AddSnippet_triggered( bool checked = false ); + /// + /// remove the current snippet, call update() + void on_RemoveSnippet_triggered( bool checked = false ); + /// + /// call StringMapFromXmlFile with d->m_DefaultSnippetsAutoSaveFileName + void on_RestoreDefaultSnippets_triggered( bool checked = false ); + /// + /// update action state (enable/disable), update text box + void on_Name_currentIndexChanged( int i ); + /// + /// save changed snippet void on_Content_textChanged(); + /// + /// ask for file, save snippets + void on_SaveSnippets_triggered( bool checked = false ); + /// + /// ask for file, load snippets (do not replace) + void on_LoadSnippets_triggered( bool checked = false ); protected: - void Update(); - void CreateUniqueName( QString& name ); - */ + /// + /// write string map to xml file + void StringMapToXmlFile( const QString& filename, const QStringMap& map ) const; + /// + /// read string map from xml file + bool StringMapFromXmlFile( const QString& filename, QStringMap& oldMap ) const; + /// + /// creates a name which does not exist in the list + QString CreateUniqueName(const QString &name) const; + /// + /// update combo box + /// if name is passed, the according element will be selected + void Update(const QString &name = ""); private: + /// + /// d pointer declaration (holds members) QmitkPythonSnippetsData* d; }; #endif // _QmitkPythonSnippets_H_INCLUDED diff --git a/Modules/Python/QmitkPythonTextEditor.cpp b/Modules/Python/QmitkPythonTextEditor.cpp index bec88ae86e..1dbf1ae301 100644 --- a/Modules/Python/QmitkPythonTextEditor.cpp +++ b/Modules/Python/QmitkPythonTextEditor.cpp @@ -1,177 +1,180 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkPythonTextEditor.h" #include #include #include #include #include #include #include #include #include #include "QmitkPythonScriptEditorHighlighter.h" struct QmitkPythonTextEditorData { QString m_FilePath; QAction* m_LoadScript; QAction* m_SaveScript; QAction* m_RunScript; QToolBar* m_Toolbar; QTextEdit* m_Content; QGridLayout* m_Layout; mitk::IPythonService* m_PythonService; QString m_FileName; }; QmitkPythonTextEditor::QmitkPythonTextEditor(QWidget *parent) :QWidget(parent), d(new QmitkPythonTextEditorData) { mitk::ModuleContext* context = mitk::GetModuleContext(); mitk::ServiceReference serviceRef = context->GetServiceReference(); d->m_PythonService = context->GetService(serviceRef); d->m_LoadScript = new QAction(this); d->m_LoadScript->setToolTip("Load script from disk."); d->m_LoadScript->setObjectName(QString::fromUtf8("LoadScript")); QIcon icon2; icon2.addFile(QString::fromUtf8(":/mitkPython/document-open.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_LoadScript->setIcon(icon2); d->m_SaveScript = new QAction(this); d->m_SaveScript->setToolTip("Save script to disk."); d->m_SaveScript->setObjectName(QString::fromUtf8("SaveScript")); QIcon icon3; icon3.addFile(QString::fromUtf8(":/mitkPython/document-save.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_SaveScript->setIcon(icon3); d->m_RunScript = new QAction(this); d->m_RunScript->setToolTip("Run the current script."); d->m_RunScript->setObjectName(QString::fromUtf8("RunScript")); QIcon icon4; icon4.addFile(QString::fromUtf8(":/mitkPython/media-playback-start.png"), QSize(), QIcon::Normal, QIcon::Off); d->m_RunScript->setIcon(icon4); d->m_Toolbar = new QToolBar; d->m_Toolbar->addAction( d->m_LoadScript ); d->m_Toolbar->addAction( d->m_SaveScript ); d->m_Toolbar->addAction( d->m_RunScript ); d->m_Content = new QTextEdit(this); d->m_Content->setObjectName(QString::fromUtf8("Content")); QmitkPythonScriptEditorHighlighter* highlighter = new QmitkPythonScriptEditorHighlighter( d->m_Content->document() ); d->m_Layout = new QGridLayout; d->m_Layout->addWidget( d->m_Toolbar, 0, 0, 1, 1 ); d->m_Layout->addWidget( d->m_Content, 1, 0, 1, 1 ); d->m_Layout->setContentsMargins(2,2,2,2); this->setLayout(d->m_Layout); QMetaObject::connectSlotsByName(this); } QmitkPythonTextEditor::~QmitkPythonTextEditor() { delete d; } void QmitkPythonTextEditor::dragEnterEvent(QDragEnterEvent *event) { event->accept(); } void QmitkPythonTextEditor::dropEvent(QDropEvent *event) { QList urls = event->mimeData()->urls(); for(int i = 0; i < urls.size(); i++) { this->Paste( urls[i].toString() ); } } /* bool QmitkPythonTextEditor::canInsertFromMimeData( const QMimeData * ) const { return true; } */ void QmitkPythonTextEditor::Paste(const QString &command) { - d->m_Content->insertPlainText(command + "\n"); + if( this->isVisible() ) + { + d->m_Content->insertPlainText(command + "\n"); + } } QString QmitkPythonTextEditor::ReadFile(const QString& filename) { QFile file(filename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { MITK_ERROR << "Could not open file " << filename; return NULL; } QByteArray total; QByteArray line; while (!file.atEnd()) { line = file.read(1024); total.append(line); } return QString(total); } void QmitkPythonTextEditor::on_SaveScript_triggered( bool ) { d->m_FileName = QFileDialog::getSaveFileName(this,tr("Save File"), d->m_FileName,tr("*.py")); if( d->m_FileName.compare("") != 0) { ofstream myfile; myfile.open(d->m_FileName.toLocal8Bit().data()); myfile << d->m_Content->toPlainText().toLocal8Bit().data(); myfile.close(); } } void QmitkPythonTextEditor::on_LoadScript_triggered( bool ) { d->m_FileName = QFileDialog::getOpenFileName( this, "Load Script", d->m_FileName, tr("*.py")); if( !d->m_FileName.isEmpty() ) { QString contents = this->ReadFile( d->m_FileName ); d->m_Content->setText(contents); } } void QmitkPythonTextEditor::on_RunScript_triggered( bool ) { if( !d->m_PythonService ) { MITK_ERROR << "Python service not available."; return; } d->m_PythonService->Execute( d->m_Content->toPlainText(), mitk::IPythonService::MULTI_LINE_COMMAND ); } diff --git a/Modules/Python/resources/PythonSnippets.xml b/Modules/Python/resources/PythonSnippets.xml new file mode 100644 index 0000000000..7b9ceb16e7 --- /dev/null +++ b/Modules/Python/resources/PythonSnippets.xml @@ -0,0 +1,4 @@ + + + + diff --git a/Modules/Python/resources/mitkPython.qrc b/Modules/Python/resources/mitkPython.qrc index f0752ede10..55d4c94e13 100644 --- a/Modules/Python/resources/mitkPython.qrc +++ b/Modules/Python/resources/mitkPython.qrc @@ -1,12 +1,13 @@ document-new.png document-open.png document-save.png edit-delete.png edit-paste.png edit-find-replace.png edit-clear.png media-playback-start.png + PythonSnippets.xml diff --git a/Plugins/org.mitk.gui.qt.python/src/internal/QmitkPythonView.cpp b/Plugins/org.mitk.gui.qt.python/src/internal/QmitkPythonView.cpp index 6443bd286f..61cf307900 100644 --- a/Plugins/org.mitk.gui.qt.python/src/internal/QmitkPythonView.cpp +++ b/Plugins/org.mitk.gui.qt.python/src/internal/QmitkPythonView.cpp @@ -1,86 +1,90 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkPythonView.h" #include #include #include "mitkPluginActivator.h" #include #include #include const std::string QmitkPythonView::VIEW_ID = "org.mitk.views.python"; struct QmitkPythonViewData { // widget QmitkPythonVariableStackTableView* m_PythonVariableStackTableView; QmitkPythonSnippets* m_PythonSnippets; QmitkCtkPythonShell* m_PythonShell; QmitkPythonTextEditor* m_TextEditor; }; QmitkPythonView::QmitkPythonView() : d( new QmitkPythonViewData ) { d->m_PythonVariableStackTableView = 0; d->m_PythonShell = 0; } QmitkPythonView::~QmitkPythonView() { delete d; } void QmitkPythonView::CreateQtPartControl(QWidget* parent) { d->m_PythonVariableStackTableView = new QmitkPythonVariableStackTableView; d->m_PythonVariableStackTableView->SetDataStorage(this->GetDataStorage()); + d->m_PythonVariableStackTableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch); d->m_PythonSnippets = new QmitkPythonSnippets; QTabWidget* varStackSnippetsTab = new QTabWidget; varStackSnippetsTab->addTab( d->m_PythonVariableStackTableView, "Variable Stack" ); varStackSnippetsTab->addTab( d->m_PythonSnippets, "Snippets" ); varStackSnippetsTab->setTabPosition( QTabWidget::South ); d->m_PythonShell = new QmitkCtkPythonShell; d->m_TextEditor = new QmitkPythonTextEditor; QTabWidget* tabWidgetConsoleEditor = new QTabWidget; tabWidgetConsoleEditor->addTab( d->m_PythonShell, "Console" ); tabWidgetConsoleEditor->addTab( d->m_TextEditor, "Text Editor" ); tabWidgetConsoleEditor->setTabPosition( QTabWidget::South ); QList sizes; sizes << 1 << 3; QSplitter* splitter = new QSplitter; splitter->addWidget(varStackSnippetsTab); splitter->addWidget(tabWidgetConsoleEditor); splitter->setStretchFactor ( 0, 1 ); splitter->setStretchFactor ( 1, 3 ); QGridLayout* layout = new QGridLayout; layout->addWidget( splitter, 0, 0 ); parent->setLayout(layout); + + connect( d->m_PythonSnippets, SIGNAL(PasteCommandRequested(QString)), d->m_PythonShell, SLOT(Paste(QString)) ); + connect( d->m_PythonSnippets, SIGNAL(PasteCommandRequested(QString)), d->m_TextEditor, SLOT(Paste(QString)) ); } void QmitkPythonView::SetFocus() { d->m_PythonShell->setFocus(); }