diff --git a/Modules/Python/CMakeLists.txt b/Modules/Python/CMakeLists.txt index 582129e58e..6d77ab72a1 100644 --- a/Modules/Python/CMakeLists.txt +++ b/Modules/Python/CMakeLists.txt @@ -1,8 +1,10 @@ MITK_CREATE_MODULE(mitkPython DEPENDS Mitk EXPORT_DEFINE MITK_PYTHON_EXPORT PACKAGE_DEPENDS QT CTK QT_MODULE ) configure_file(PythonPath.h.in "${CMAKE_CURRENT_BINARY_DIR}/PythonPath.h" @ONLY) + +add_subdirectory(Testing) diff --git a/Modules/Python/PythonPath.h.in b/Modules/Python/PythonPath.h.in index 945904a8c3..2f06a8943c 100644 --- a/Modules/Python/PythonPath.h.in +++ b/Modules/Python/PythonPath.h.in @@ -1,2 +1,7 @@ #define PYTHONPATH_ITK_LIBRARY_DIRS "@ITK_LIBRARY_DIRS@" #define PYTHONPATH_WRAP_ITK_DIR "@ITK_DIR@/Wrapping/WrapITK/Python" + +#define PYTHONPATH_VTK_LIBRARY_DIRS "@VTK_LIBRARY_DIRS@" +#define PYTHONPATH_VTK_PYTHON_WRAPPING_DIR "@VTK_DIR@/Wrapping/Python" + +#define PYTHONPATH_OPEN_CV_LIBRARY_DIRS "@OpenCV_DIR@/lib" diff --git a/Modules/Python/QmitkPythonScriptEditorHighlighter.cpp b/Modules/Python/QmitkPythonScriptEditorHighlighter.cpp new file mode 100644 index 0000000000..55fb80e313 --- /dev/null +++ b/Modules/Python/QmitkPythonScriptEditorHighlighter.cpp @@ -0,0 +1,110 @@ +/*=================================================================== + +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 "QmitkPythonScriptEditorHighlighter.h" + +QmitkPythonScriptEditorHighlighter::QmitkPythonScriptEditorHighlighter(QTextDocument *parent) +: QSyntaxHighlighter(parent) +{ +} + +QmitkPythonScriptEditorHighlighter::~QmitkPythonScriptEditorHighlighter() +{ +} + +void QmitkPythonScriptEditorHighlighter::highlightBlock(const QString &text) +{ + QTextCharFormat commentFormat; + commentFormat.setFontWeight(QFont::Courier); + commentFormat.setForeground(Qt::darkGreen); + + QTextCharFormat keywordFormat; + keywordFormat.setFontWeight(QFont::Bold); + keywordFormat.setForeground(Qt::blue); + QString pattern = "\\b(and|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|yield|None|True|False)\\b"; + + QTextCharFormat qouteFormat; + qouteFormat.setForeground(Qt::gray); + + QTextCharFormat numberFormat; + numberFormat.setForeground(Qt::red); + + QRegExp numberExpression("\\d+"); + int indexNumbers = text.indexOf(numberExpression); + + while (indexNumbers >= 0) { + int length = numberExpression.matchedLength(); + setFormat(indexNumbers, length, numberFormat); + indexNumbers = text.indexOf(numberExpression, indexNumbers + length); + } + + QRegExp qouteExpression("\""); + int startQouteIndex = text.indexOf(qouteExpression); + int endQouteIndex = text.indexOf(qouteExpression, startQouteIndex); + + QRegExp keyword(pattern); + int indexKeyword = text.indexOf(keyword); + + QRegExp startCommentExpression("^#"); + QRegExp endCommentExpression("\\n"); + int startCommentIndex = text.indexOf(startCommentExpression); + int endCommentIndex = text.indexOf(endCommentExpression, startCommentIndex); + + + while (startQouteIndex >= 0) { + endQouteIndex = text.indexOf(qouteExpression, startQouteIndex+1); + int commentLength; + if (endQouteIndex == -1) { + setCurrentBlockState(1); + commentLength = text.length() - startQouteIndex; + } else { + commentLength = endQouteIndex - startQouteIndex + + qouteExpression.matchedLength(); + } + setFormat(startQouteIndex, commentLength, qouteFormat); + startQouteIndex = text.indexOf(qouteExpression, + startQouteIndex + commentLength); + } + + if(startCommentIndex < 0) + { + while (indexKeyword >= 0) { + int length = keyword.matchedLength(); + setFormat(indexKeyword, length, keywordFormat); + indexKeyword = text.indexOf(keyword, indexKeyword + length); + } + } + + while (startCommentIndex >= 0) { + endCommentIndex = text.indexOf(endCommentExpression, startCommentIndex); + int commentLength; + if (endCommentIndex == -1) { + setCurrentBlockState(1); + commentLength = text.length() - startCommentIndex; + } else { + commentLength = endCommentIndex - startCommentIndex + + endCommentExpression.matchedLength(); + } + setFormat(startCommentIndex, commentLength, commentFormat); + startCommentIndex = text.indexOf(startCommentExpression, + startCommentIndex + commentLength); + } +} + +void QmitkPythonScriptEditorHighlighter::highlightComments(const QString &text) +{ +} + diff --git a/Modules/Python/QmitkPythonScriptEditorHighlighter.h b/Modules/Python/QmitkPythonScriptEditorHighlighter.h new file mode 100644 index 0000000000..cf97e58248 --- /dev/null +++ b/Modules/Python/QmitkPythonScriptEditorHighlighter.h @@ -0,0 +1,40 @@ +/*=================================================================== + +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 QmitkPythonScriptEditorHighlighter_h +#define QmitkPythonScriptEditorHighlighter_h + +#include +#include "mitkPythonExports.h" + +/// +/// A script highlighter for Python Scripts +class MITK_PYTHON_EXPORT QmitkPythonScriptEditorHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + QmitkPythonScriptEditorHighlighter(QTextDocument *parent); + virtual ~QmitkPythonScriptEditorHighlighter(); + +protected: + void highlightBlock(const QString &text); + void highlightComments(const QString &text); + +private: +}; + +#endif // QmitkPythonScriptEditorHighlighter_h diff --git a/Modules/Python/QmitkPythonSnippets.cpp b/Modules/Python/QmitkPythonSnippets.cpp new file mode 100644 index 0000000000..30660c025a --- /dev/null +++ b/Modules/Python/QmitkPythonSnippets.cpp @@ -0,0 +1,312 @@ +/*=================================================================== + +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 +#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; +} + +struct QmitkPythonSnippetsData +{ + QString m_FilePath; + + QAction* m_PasteSnippet; + QAction* m_RemoveSnippet; + QAction* m_RenameSnippet; + QAction* m_AddSnippet; + QAction* m_RestoreDefaultSnippets; + QToolBar* m_Toolbar; + + QComboBox* m_Name; + + QTextEdit* m_Content; + + QGridLayout* m_Layout; +}; + +const QmitkPythonSnippets::QStringMap QmitkPythonSnippets::DEFAULT_SNIPPETS + = QmitkPythonSnippets::CreateDefaultSnippets(); + +QmitkPythonSnippets::QmitkPythonSnippets(QWidget* parent) +: QWidget(parent), d(new QmitkPythonSnippetsData) +{ + + 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_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_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_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_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_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_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_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); +} + +QmitkPythonSnippets::~QmitkPythonSnippets() +{ + delete d; +} + +void QmitkPythonSnippets::SetFilePath(const QString &filePath) +{ + d->m_FilePath = filePath; +} + +/* +void QmitkPythonSnippets::on_Name_currentIndexChanged(int i) +{ +// 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()]) ); +} + +void QmitkPythonSnippets::on_RestoreDefaultSnippets_clicked() +{ + QString question = QString("Really restore default Snippets?"); + int remove = QMessageBox::question( QApplication::topLevelWidgets().at(0), + QString("Confirm restoring"), + 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; + } + 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() +{ + 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"), + 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->Update(); + } +} + +void QmitkPythonSnippets::on_PasteNow_clicked() +{ + if( m_Controls->Name->count() == 0 ) + return; + + QString name = m_Controls->Name->currentText(); + QString snippet = QString::fromStdString(m_Snippets[name.toStdString()]); + QmitkPythonMediator::getInstance()->paste( snippet ); + QmitkPythonMediator::getInstance()->update(); +} + +void QmitkPythonSnippets::on_Content_textChanged() +{ + if( m_Controls->Name->count() == 0 ) + return; + + std::string name = (m_Controls->Name->currentText()).toStdString(); + std::string snippet = m_Controls->Content->toPlainText().toStdString(); + + if( m_Snippets.find(name) != m_Snippets.end() ) + m_Snippets[name] = snippet; +} + +void QmitkPythonSnippets::on_RenameSnippet_clicked() +{ +// std::cout << "on_Name_editTextChanged" << std::endl; + if( m_Controls->Name->count() == 0 ) + return; + + QString newName; + bool repeat = false; + do + { + 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; + } + while( repeat ); + + + // name changed + if( newName != m_Controls->Name->currentText() ) + { + 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(); + } +} + +void QmitkPythonSnippets::Update() +{ + if( m_NameToSelect.isEmpty() ) + { + QString name = m_Controls->Name->currentText(); + m_NameToSelect = name; + } + + m_Controls->Name->clear(); + QStringMap::iterator it + = m_Snippets.begin(); + + while( it != m_Snippets.end() ) + { + m_Controls->Name->addItem( QString::fromStdString( it->first ) ); + ++it; + } + + // 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() ) + { + index = m_Controls->Name->findText( m_NameToSelect ); + } + else if( m_Snippets.size() > 0 ) + { + index = m_Controls->Name->count()-1; + } + + if( index >= 0 ) + { + m_Controls->Name->setCurrentIndex(index); + current = m_Controls->Name->itemText( index ).toStdString(); + m_Controls->Content->setPlainText( QString::fromStdString(m_Snippets[current]) ); + } + + // clear the current name + m_NameToSelect.clear(); +} +*/ diff --git a/Modules/Python/QmitkPythonSnippets.h b/Modules/Python/QmitkPythonSnippets.h new file mode 100644 index 0000000000..b48bf0402f --- /dev/null +++ b/Modules/Python/QmitkPythonSnippets.h @@ -0,0 +1,65 @@ +/*=================================================================== + +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: + typedef QMap QStringMap; + /// + /// creates default snippets + static QStringMap CreateDefaultSnippets(); + static const QStringMap DEFAULT_SNIPPETS; + + QmitkPythonSnippets(QWidget* parent=0); + virtual ~QmitkPythonSnippets(); + + void SetFilePath( const QString& filePath ); + + /* + 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(); + void on_Content_textChanged(); + + protected: + void Update(); + void CreateUniqueName( QString& name ); + */ + private: + QmitkPythonSnippetsData* d; +}; + + + + +#endif // _QmitkPythonSnippets_H_INCLUDED + diff --git a/Modules/Python/QmitkPythonSnippets.ui b/Modules/Python/QmitkPythonSnippets.ui new file mode 100644 index 0000000000..f7bc574d07 --- /dev/null +++ b/Modules/Python/QmitkPythonSnippets.ui @@ -0,0 +1,116 @@ + + + QmitkPythonSnippets + + + + 0 + 0 + 613 + 436 + + + + + 0 + 0 + + + + true + + + QmitkTemplate + + + + + + Name: + + + + + + + true + + + + + + + Content: + + + + + + + + + + + :/mitkPython/edit-paste.png:/mitkPython/edit-paste.png + + + Paste snippet! + + + Push button to paste the snippet into the console or the editor. Or double click on the snippet text window. + + + + + + :/mitkPython/edit-delete.png:/mitkPython/edit-delete.png + + + Remove snippet + + + Remove the selected snippet. + + + + + + :/mitkPython/edit-find-replace.png:/mitkPython/edit-find-replace.png + + + Rename snippet + + + Rename the selected snippet. + + + + + + :/mitkPython/document-new.png:/mitkPython/document-new.png + + + Add snippet + + + Add a new snippet to the list. + + + + + + :/mitkPython/edit-clear.png:/mitkPython/edit-clear.png + + + Restore default snippets + + + Clear and restore the default snippets. + + + + + + + + diff --git a/Modules/Python/QmitkPythonTextEditor.cpp b/Modules/Python/QmitkPythonTextEditor.cpp new file mode 100644 index 0000000000..bec88ae86e --- /dev/null +++ b/Modules/Python/QmitkPythonTextEditor.cpp @@ -0,0 +1,177 @@ +/*=================================================================== + +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"); +} + + +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/QmitkPythonTextEditor.h b/Modules/Python/QmitkPythonTextEditor.h new file mode 100644 index 0000000000..0e407ee866 --- /dev/null +++ b/Modules/Python/QmitkPythonTextEditor.h @@ -0,0 +1,53 @@ +/*=================================================================== + +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 QMITKPYTHONTEXTEDITOR_H_ +#define QMITKPYTHONTEXTEDITOR_H_ + +#include +#include +#include +#include "mitkPythonExports.h" +struct QmitkPythonTextEditorData; + +/// +/// this is a python text editor with syntax highlightning +class MITK_PYTHON_EXPORT QmitkPythonTextEditor : public QWidget +{ + Q_OBJECT + +public: + QmitkPythonTextEditor(QWidget *parent = 0); + virtual ~QmitkPythonTextEditor(); +public slots: + void Paste(const QString& command); + +protected slots: + void on_SaveScript_triggered(bool checked=false); + void on_LoadScript_triggered(bool checked=false); + void on_RunScript_triggered(bool checked=false); + +protected: + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); + //bool canInsertFromMimeData( const QMimeData *source ) const; + QString ReadFile(const QString &filename); + +private: + QmitkPythonTextEditorData* d; +}; + +#endif diff --git a/Modules/Python/QmitkPythonVariableStackTableModel.cpp b/Modules/Python/QmitkPythonVariableStackTableModel.cpp index 6438f61a38..f4506ec4ef 100755 --- a/Modules/Python/QmitkPythonVariableStackTableModel.cpp +++ b/Modules/Python/QmitkPythonVariableStackTableModel.cpp @@ -1,174 +1,163 @@ /*=================================================================== 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 "QmitkPythonVariableStackTableModel.h" #include #include #include #include #include #include const QString QmitkPythonVariableStackTableModel::MITK_IMAGE_VAR_NAME = "mitkImage"; QmitkPythonVariableStackTableModel::QmitkPythonVariableStackTableModel(QObject *parent) :QAbstractTableModel(parent) { mitk::ModuleContext* context = mitk::GetModuleContext(); mitk::ServiceReference serviceRef = context->GetServiceReference(); m_PythonService = context->GetService(serviceRef); + m_PythonService->AddPythonCommandObserver( this ); } QmitkPythonVariableStackTableModel::~QmitkPythonVariableStackTableModel() { + m_PythonService->RemovePythonCommandObserver( this ); } bool QmitkPythonVariableStackTableModel::dropMimeData ( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent ) { // Early exit, returning true, but not actually doing anything (ignoring data). if (action == Qt::IgnoreAction) return true; // Note, we are returning true if we handled it, and false otherwise bool returnValue = false; if(data->hasFormat("application/x-mitk-datanodes")) { MITK_DEBUG("QmitkPythonVariableStackTableModel") << "dropped MITK DataNode"; returnValue = true; QString arg = QString(data->data("application/x-mitk-datanodes").data()); QStringList listOfDataNodeAddressPointers = arg.split(","); QStringList::iterator slIter; int i = 0; for (slIter = listOfDataNodeAddressPointers.begin(); slIter != listOfDataNodeAddressPointers.end(); slIter++) { long val = (*slIter).toLong(); mitk::DataNode* node = static_cast((void*)val); mitk::Image* mitkImage = dynamic_cast(node->GetData()); //QString::fromStdString((node->GetName()); QString varName = MITK_IMAGE_VAR_NAME; if( i > 0 ) - varName = QString("%1%2).arg(MITK_IMAGE_VAR_NAME).arg(i); + varName = QString("%1%2").arg(MITK_IMAGE_VAR_NAME).arg(i); MITK_DEBUG("varName") << "varName" << varName; m_PythonService->CopyToPythonAsItkImage( mitkImage, MITK_IMAGE_VAR_NAME ); ++i; } } return returnValue; } QVariant QmitkPythonVariableStackTableModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant headerData; // show only horizontal header if ( role == Qt::DisplayRole ) { if( orientation == Qt::Horizontal ) { // first column: "Attribute" if(section == 0) headerData = "Attribute"; else if(section == 1) headerData = "Value"; else if(section == 2) headerData = "Type"; } } return headerData; } Qt::ItemFlags QmitkPythonVariableStackTableModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if(index.isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | flags; else return Qt::ItemIsDropEnabled | flags; } int QmitkPythonVariableStackTableModel::rowCount(const QModelIndex &) const { return m_VariableStack.size(); } int QmitkPythonVariableStackTableModel::columnCount(const QModelIndex &) const { return 3; } QVariant QmitkPythonVariableStackTableModel::data(const QModelIndex &index, int role) const { if (index.isValid() && !m_VariableStack.empty()) { if(role == Qt::DisplayRole) { mitk::PythonVariable item = m_VariableStack.at(index.row()); if(index.column() == 0) return item.m_Name; if(index.column() == 1) return item.m_Value; if(index.column() == 2) return item.m_Type; } } return QVariant(); } QStringList QmitkPythonVariableStackTableModel::mimeTypes() const { return QAbstractTableModel::mimeTypes(); QStringList types; types << "application/x-mitk-datanodes"; types << "application/x-qabstractitemmodeldatalist"; return types; } Qt::DropActions QmitkPythonVariableStackTableModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } void QmitkPythonVariableStackTableModel::CommandExecuted(const QString &pythonCommand) { + MITK_DEBUG("QmitkPythonVariableStackTableModel") << "command was executed " << pythonCommand.toStdString(); m_VariableStack = m_PythonService->GetVariableStack(); QAbstractTableModel::reset(); } QList QmitkPythonVariableStackTableModel::GetVariableStack() const { return m_VariableStack; } - - -QString QmitkPythonVariableStackTableModel::CreateDictionaryCommandIfNecessary() -{ - MITK_DEBUG("QmitkPythonVariableStackTableModel") << "CreateDictionaryCommandIfNecessary()"; - MITK_DEBUG("QmitkPythonVariableStackTableModel") << "m_PythonImagesDictName = " << m_PythonImagesDictName.toStdString(); - - QString command; - command.append( QString("try:\n") ); - command.append( QString(" %1" ) .arg( m_PythonImagesDictName ) ); - command.append( QString("except NameError:\n") ); - command.append( QString(" %1 = {}\n") .arg( m_PythonImagesDictName ) ); - return command; -} diff --git a/Modules/Python/QmitkPythonVariableStackTableView.cpp b/Modules/Python/QmitkPythonVariableStackTableView.cpp index 0da147f6e9..53273e136a 100755 --- a/Modules/Python/QmitkPythonVariableStackTableView.cpp +++ b/Modules/Python/QmitkPythonVariableStackTableView.cpp @@ -1,75 +1,75 @@ /*=================================================================== 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 "QmitkPythonVariableStackTableView.h" #include #include #include #include QmitkPythonVariableStackTableView::QmitkPythonVariableStackTableView(QWidget *parent) :QTableView(parent) { m_TableModel = new QmitkPythonVariableStackTableModel(parent); m_TableModel->CommandExecuted(""); this->setSelectionBehavior( QAbstractItemView::SelectRows ); this->setAlternatingRowColors(true); this->setDropIndicatorShown(true); this->setAcceptDrops(true); this->setModel( m_TableModel ); mitk::ModuleContext* context = mitk::GetModuleContext(); mitk::ServiceReference serviceRef = context->GetServiceReference(); m_PythonService = context->GetService(serviceRef); connect( this, SIGNAL(doubleClicked ( const QModelIndex& )), this, SLOT( OnVariableStackDoubleClicked(const QModelIndex&) ) ); } QmitkPythonVariableStackTableView::~QmitkPythonVariableStackTableView() { } void QmitkPythonVariableStackTableView::SetDataStorage(mitk::DataStorage *_DataStorage) { m_DataStorage = _DataStorage; } void QmitkPythonVariableStackTableView::OnVariableStackDoubleClicked(const QModelIndex &index) { if( m_DataStorage.IsNull() || m_PythonService == 0 ) { MITK_ERROR << "QmitkPythonVariableStackTableView not configured correctly. Quit"; return; } QList variableStack = m_TableModel->GetVariableStack(); QString varName = variableStack.at(index.row()).m_Name; MITK_DEBUG("QmitkPythonVariableStackTableView") << varName; mitk::Image::Pointer mitkImage = m_PythonService->CopyItkImageFromPython(varName); if( mitkImage.IsNotNull() ) { std::string nodeName = varName.toStdString(); - node = mitk::DataNode::New(); + mitk::DataNode::Pointer node = mitk::DataNode::New(); m_DataStorage->Add(node); node->SetData( mitkImage ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } diff --git a/Modules/Python/Testing/CMakeLists.txt b/Modules/Python/Testing/CMakeLists.txt new file mode 100644 index 0000000000..2d5e45bb88 --- /dev/null +++ b/Modules/Python/Testing/CMakeLists.txt @@ -0,0 +1,2 @@ +MITK_CREATE_MODULE_TESTS() + diff --git a/Modules/Python/Testing/files.cmake b/Modules/Python/Testing/files.cmake new file mode 100644 index 0000000000..d4301946da --- /dev/null +++ b/Modules/Python/Testing/files.cmake @@ -0,0 +1,3 @@ +set(MODULE_TESTS + mitkPythonTest.cpp +) diff --git a/Modules/Python/Testing/mitkPythonTest.cpp b/Modules/Python/Testing/mitkPythonTest.cpp new file mode 100644 index 0000000000..383741a65f --- /dev/null +++ b/Modules/Python/Testing/mitkPythonTest.cpp @@ -0,0 +1,23 @@ +/*=================================================================== + +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 + +int mitkPythonTest(int /*argc*/, char* /*argv*/[]) +{ + return EXIT_SUCCESS; +} diff --git a/Modules/Python/files.cmake b/Modules/Python/files.cmake index cde5a394de..581c111cf3 100644 --- a/Modules/Python/files.cmake +++ b/Modules/Python/files.cmake @@ -1,23 +1,28 @@ SET(CPP_FILES mitkPythonActivator.cpp mitkIPythonService.cpp mitkPythonService.cpp QmitkCtkPythonShell.cpp QmitkPythonVariableStackTableModel.cpp QmitkPythonVariableStackTableView.cpp + QmitkPythonScriptEditorHighlighter.cpp + QmitkPythonTextEditor.cpp + QmitkPythonSnippets.cpp ) #SET(UI_FILES - #Qmitk/QmitkKinectParameterWidgetControls.ui +# QmitkPythonSnippets.ui #) SET(MOC_H_FILES QmitkCtkPythonShell.h QmitkPythonVariableStackTableModel.h QmitkPythonVariableStackTableView.h + QmitkPythonScriptEditorHighlighter.h + QmitkPythonTextEditor.h + QmitkPythonSnippets.h ) -# uncomment the following line if you want to use Qt resources -#set(QRC_FILES - #resources/QmitkToFUtilWidget.qrc -#) +set(QRC_FILES + resources/mitkPython.qrc +) diff --git a/Modules/Python/mitkIPythonService.h b/Modules/Python/mitkIPythonService.h index d534851125..4de02db8f5 100644 --- a/Modules/Python/mitkIPythonService.h +++ b/Modules/Python/mitkIPythonService.h @@ -1,82 +1,118 @@ /*=================================================================== 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 mitkIPythonService_h #define mitkIPythonService_h // mitk #include "mitkPythonExports.h" #include "mitkImage.h" //for microservices #include // Qt #include #include #include namespace mitk { /// /// describes a python variable (data container) /// \see IPythonService::GetVariableStack() /// struct PythonVariable { QString m_Name; QString m_Type; QString m_Value; }; /// /// a PythonCommandObserver gets informed as soon as a python command was issued /// \see IPythonService::AddPythonCommandObserver() /// class PythonCommandObserver { public: virtual void CommandExecuted(const QString& pythonCommand) = 0; }; /// /// The central service for issuing Python Code /// The class also enables to transfer mitk images to python as itk::Image and vice versa /// \see IPythonService::GetVariableStack() /// class MITK_PYTHON_EXPORT IPythonService { public: + /// + /// Constant representing a single line command + /// \see IPythonService::Execute() static const int SINGLE_LINE_COMMAND = 0; + /// + /// Constant representing a command in which the commands are seperated by new lines, i.e. "\\n" + /// \see IPythonService::Execute() static const int MULTI_LINE_COMMAND = 1; + /// + /// Constant representing a single line command x which is run as "eval(x)" + /// \see IPythonService::Execute() static const int EVAL_COMMAND = 2; - virtual ~IPythonService(); // leer in mitkIPythonService.cpp implementieren + /// + /// Executes a python command. + /// \return A variant containing the return value of the python code (if any) virtual QVariant Execute( const QString& pythonCommand, int commandType = SINGLE_LINE_COMMAND ) = 0; + /// + /// \return The list of variables in the __main__ namespace virtual QList GetVariableStack() const = 0; - + /// + /// adds a command observer which is informed after a command was issued with "Execute" virtual void AddPythonCommandObserver( PythonCommandObserver* observer ) = 0; + /// + /// removes a specific command observer virtual void RemovePythonCommandObserver( PythonCommandObserver* observer ) = 0; + /// + /// notify all observer. this should only be used if it can be garantueed that the + /// current python interpreter instance got another command from anywhere else + /// the the Execute() method of this service, e.g. the shell widget uses this function + /// since it does not use Execute() virtual void NotifyObserver( const QString& command ) = 0; + /// + /// copies an mitk image as itk image into the python interpreter process + /// the image will be available as "varName" in python if everythin worked + /// \return true if image was copied, else false virtual bool CopyToPythonAsItkImage( mitk::Image* image, const QString& varName ) = 0; + /// + /// copies an itk image from the python process that is named "varName" + /// \return the image or 0 if copying was not possible virtual mitk::Image::Pointer CopyItkImageFromPython( const QString& varName ) = 0; + /// + /// \see CopyToPythonAsItkImage() virtual bool CopyToPythonAsCvImage( mitk::Image* image, const QString& varName ) = 0; + /// + /// \see CopyCvImageFromPython() virtual mitk::Image::Pointer CopyCvImageFromPython( const QString& varName ) = 0; + + /// + /// nothing to do here + virtual ~IPythonService(); // leer in mitkIPythonService.cpp implementieren }; } US_DECLARE_SERVICE_INTERFACE(mitk::IPythonService, "org.mitk.services.IPythonService") #endif diff --git a/Modules/Python/mitkPythonActivator.cpp b/Modules/Python/mitkPythonActivator.cpp index 032b26a316..7de905e7c4 100644 --- a/Modules/Python/mitkPythonActivator.cpp +++ b/Modules/Python/mitkPythonActivator.cpp @@ -1,79 +1,101 @@ /*=================================================================== 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 mitkPythonActivator_h #define mitkPythonActivator_h // Microservices #include #include "mitkModuleContext.h" #include "mitkPythonService.h" #include #include "PythonPath.h" namespace mitk { /// /// installs the PythonService /// runs all initial commands (setting env paths etc) /// class PythonActivator : public mitk::ModuleActivator { public: void Load(mitk::ModuleContext* context) { // Registering PythonService as MicroService m_PythonService = itk::SmartPointer(new PythonService()); ServiceProperties _PythonServiceProps; _PythonServiceProps["Name"] = std::string("PythonService"); context->RegisterService(m_PythonService, _PythonServiceProps); - MITK_DEBUG << "registering python paths"; + MITK_DEBUG("PythonActivator") << "registering python paths"; - QString itkLibDirs(PYTHONPATH_ITK_LIBRARY_DIRS); - MITK_DEBUG << "itkLibDirs" << itkLibDirs.toStdString(); + QString _PYTHONPATH_ITK_LIBRARY_DIRS(PYTHONPATH_ITK_LIBRARY_DIRS); + MITK_DEBUG("PythonActivator") << "_PYTHONPATH_ITK_LIBRARY_DIRS" << _PYTHONPATH_ITK_LIBRARY_DIRS.toStdString(); - QString itkWrapItkDir(PYTHONPATH_WRAP_ITK_DIR); - MITK_DEBUG << "itkWrapItkDir" << itkWrapItkDir.toStdString(); + QString _PYTHONPATH_WRAP_ITK_DIR(PYTHONPATH_WRAP_ITK_DIR); + MITK_DEBUG("PythonActivator") << "_PYTHONPATH_WRAP_ITK_DIR" << _PYTHONPATH_WRAP_ITK_DIR.toStdString(); + + QString _PYTHONPATH_VTK_LIBRARY_DIRS(PYTHONPATH_VTK_LIBRARY_DIRS); + MITK_DEBUG("PythonActivator") << "_PYTHONPATH_VTK_LIBRARY_DIRS" << _PYTHONPATH_VTK_LIBRARY_DIRS.toStdString(); + + QString _PYTHONPATH_VTK_PYTHON_WRAPPING_DIR(PYTHONPATH_VTK_PYTHON_WRAPPING_DIR); + MITK_DEBUG("PythonActivator") << "_PYTHONPATH_VTK_PYTHON_WRAPPING_DIR" << _PYTHONPATH_VTK_PYTHON_WRAPPING_DIR.toStdString(); + + QString _PYTHONPATH_OPEN_CV_LIBRARY_DIRS(PYTHONPATH_OPEN_CV_LIBRARY_DIRS); + MITK_DEBUG("PythonActivator") << "_PYTHONPATH_OPEN_CV_LIBRARY_DIRS" << _PYTHONPATH_OPEN_CV_LIBRARY_DIRS.toStdString(); QString basecommand = "sys.path.append('%1');"; QString pythonCommand; - pythonCommand = basecommand.arg(itkLibDirs); - MITK_DEBUG << "issuing command " << pythonCommand.toStdString(); + + pythonCommand = basecommand.arg(_PYTHONPATH_ITK_LIBRARY_DIRS); + MITK_DEBUG("PythonActivator") << "issuing command " << pythonCommand.toStdString(); + m_PythonService->Execute(pythonCommand, mitk::IPythonService::SINGLE_LINE_COMMAND ); + + pythonCommand = basecommand.arg(_PYTHONPATH_WRAP_ITK_DIR); + MITK_DEBUG("PythonActivator") << "issuing command " << pythonCommand.toStdString(); + m_PythonService->Execute(pythonCommand, mitk::IPythonService::SINGLE_LINE_COMMAND ); + + pythonCommand = basecommand.arg(_PYTHONPATH_VTK_LIBRARY_DIRS); + MITK_DEBUG("PythonActivator") << "issuing command " << pythonCommand.toStdString(); + m_PythonService->Execute(pythonCommand, mitk::IPythonService::SINGLE_LINE_COMMAND ); + + pythonCommand = basecommand.arg(_PYTHONPATH_VTK_PYTHON_WRAPPING_DIR); + MITK_DEBUG("PythonActivator") << "issuing command " << pythonCommand.toStdString(); m_PythonService->Execute(pythonCommand, mitk::IPythonService::SINGLE_LINE_COMMAND ); - pythonCommand = basecommand.arg(itkWrapItkDir); - MITK_DEBUG << "issuing command " << pythonCommand.toStdString(); + pythonCommand = basecommand.arg(_PYTHONPATH_OPEN_CV_LIBRARY_DIRS); + MITK_DEBUG("PythonActivator") << "issuing command " << pythonCommand.toStdString(); m_PythonService->Execute(pythonCommand, mitk::IPythonService::SINGLE_LINE_COMMAND ); } void Unload(mitk::ModuleContext* ) { } ~PythonActivator() { } private: itk::SmartPointer m_PythonService; }; } US_EXPORT_MODULE_ACTIVATOR(mitkPython, mitk::PythonActivator) #endif diff --git a/Modules/Python/mitkPythonService.cpp b/Modules/Python/mitkPythonService.cpp index d2a40c8e7a..5f8d8d1c48 100644 --- a/Modules/Python/mitkPythonService.cpp +++ b/Modules/Python/mitkPythonService.cpp @@ -1,211 +1,297 @@ /*=================================================================== 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 "mitkPythonService.h" #include #include #include #include const QString mitk::PythonService::m_TmpImageName("temp_mitk_image"); mitk::PythonService::PythonService() { m_PythonManager.initialize(); } mitk::PythonService::~PythonService() { } QVariant mitk::PythonService::Execute(const QString &pythonCommand, int commandType) { { MITK_DEBUG("mitk::PythonService") << "pythonCommand = " << pythonCommand.toStdString(); MITK_DEBUG("mitk::PythonService") << "commandType = " << commandType; } QVariant result; bool commandIssued = true; if(commandType == IPythonService::SINGLE_LINE_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::SingleInput ); else if(commandType == IPythonService::MULTI_LINE_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::FileInput ); else if(commandType == IPythonService::EVAL_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::EvalInput ); else commandIssued = false; if(commandIssued) this->NotifyObserver(pythonCommand); return result; } QList mitk::PythonService::GetVariableStack() const { QList list; PyObject* dict = PyImport_GetModuleDict(); PyObject* object = PyDict_GetItemString(dict, "__main__"); PyObject* dirMain = PyObject_Dir(object); PyObject* tempObject; if(dirMain) { QString attr, attrValue, attrType; for(int i = 0; iob_type->tp_name; if(PyUnicode_Check(tempObject) || PyString_Check(tempObject)) attrValue = PyString_AsString(tempObject); else attrValue = ""; mitk::PythonVariable var; var.m_Name = attr; var.m_Value = attrValue; var.m_Type = attrType; list.append(var); } } return list; } void mitk::PythonService::AddPythonCommandObserver(mitk::PythonCommandObserver *observer) { - if(m_Observer.contains(observer)) + if(!m_Observer.contains(observer)) m_Observer.append(observer); } void mitk::PythonService::RemovePythonCommandObserver(mitk::PythonCommandObserver *observer) { m_Observer.removeOne(observer); } void mitk::PythonService::NotifyObserver(const QString &command) { + MITK_DEBUG("mitk::PythonService") << "number of observer " << m_Observer.size(); foreach(mitk::PythonCommandObserver* observer, m_Observer) { observer->CommandExecuted(command); } } QString mitk::PythonService::GetTempImageName(const QString& ext) const { QString tmpFolder = QDir::tempPath(); QString fileName = tmpFolder + QDir::separator() + m_TmpImageName + ext; return fileName; } bool mitk::PythonService::CopyToPythonAsItkImage(mitk::Image *image, const QString &varName) { // save image QString fileName = this->GetTempImageName( QString::fromStdString(mitk::IOUtil::DEFAULTIMAGEEXTENSION) ); MITK_DEBUG("PythonService") << "Saving temporary file " << fileName.toStdString(); if( !mitk::IOUtil::SaveImage(image, fileName.toStdString()) ) { MITK_ERROR << "Temporary file could not be created."; } else { // TODO CORRECT TYPE SETUP, MAKE MITK_DEBUG("PythonService") MITK_DEBUG("PythonService") - int dim = 3; + int dim = image->GetDimension(); + mitk::PixelType pixelType = image->GetPixelType(); + itk::ImageIOBase::IOPixelType ioPixelType = image->GetPixelType().GetPixelTypeId(); + + // default pixeltype: unsigned short QString type = "US"; + if( ioPixelType == itk::ImageIOBase::SCALAR ) + { + if( pixelType.GetTypeId() == typeid(double) ) + type = "D"; + else if( pixelType.GetTypeId() == typeid(float) ) + type = "F"; + if( pixelType.GetTypeId() == typeid(long double) ) + type = "LD"; + else if( pixelType.GetTypeId() == typeid(short) ) + type = "SS"; + else if( pixelType.GetTypeId() == typeid(signed char) ) + type = "SC"; + else if( pixelType.GetTypeId() == typeid(signed int) ) + type = "SI"; + else if( pixelType.GetTypeId() == typeid(signed long) ) + type = "SL"; + else if( pixelType.GetTypeId() == typeid(signed short) ) + type = "SS"; + else if( pixelType.GetTypeId() == typeid(unsigned char) ) + type = "UC"; + else if( pixelType.GetTypeId() == typeid(unsigned int) ) + type = "UI"; + else if( pixelType.GetTypeId() == typeid(unsigned long) ) + type = "UL"; + else if( pixelType.GetTypeId() == typeid(unsigned short) ) + type = "US"; + } + + MITK_DEBUG("PythonService") << "Got mitk image with type " << type.toStdString() << " and dim " << dim; + QString command; command.append( QString("import itk\n") ); command.append( QString("imageType = itk.Image[%1, %2]\n") .arg( type ).arg( dim ) ); command.append( QString("readerType = itk.ImageFileReader[imageType]\n") ); command.append( QString("reader = readerType.New()\n") ); command.append( QString("reader.SetFileName( \"%1\" )\n") .arg(fileName) ); command.append( QString("reader.Update()\n") ); command.append( QString("%1 = reader.GetOutput()\n").arg( varName ) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command, IPythonService::MULTI_LINE_COMMAND ); QFile file(fileName); MITK_DEBUG("PythonService") << "Removing file " << fileName.toStdString(); file.remove(); return true; } return false; } mitk::Image::Pointer mitk::PythonService::CopyItkImageFromPython(const QString &varName) { mitk::Image::Pointer mitkImage; QString command; QString fileName = GetTempImageName( QString::fromStdString( mitk::IOUtil::DEFAULTIMAGEEXTENSION ) ); MITK_DEBUG("PythonService") << "Saving temporary file with python itk code " << fileName.toStdString(); command.append( QString("import itk\n") ); command.append( QString( "writer = itk.ImageFileWriter[ %1 ].New()\n").arg( varName ) ); command.append( QString( "writer.SetFileName( \"%1\" )\n").arg(fileName) ); command.append( QString( "writer.SetInput( %1 )\n").arg(varName) ); command.append( QString( "writer.Update()\n" ) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command, IPythonService::MULTI_LINE_COMMAND ); try { MITK_DEBUG("PythonService") << "Loading temporary file " << fileName.toStdString() << " as MITK image"; mitkImage = mitk::IOUtil::LoadImage( fileName.toStdString() ); } catch(std::exception& e) { MITK_ERROR << e.what(); } QFile file(fileName); if( file.exists() ) { MITK_DEBUG("PythonService") << "Removing temporary file " << fileName.toStdString(); file.remove(); } return mitkImage; } bool mitk::PythonService::CopyToPythonAsCvImage( mitk::Image* image, const QString& varName ) { bool convert = false; if(image->GetDimension() != 2) { MITK_ERROR << "Only 2D images allowed for OpenCV images"; return convert; } + + // try to save mitk image + QString fileName = this->GetTempImageName( QString::fromStdString(".bmp") ); + MITK_DEBUG("PythonService") << "Saving temporary file " << fileName.toStdString(); + if( !mitk::IOUtil::SaveImage(image, fileName.toStdString()) ) + { + MITK_ERROR << "Temporary file " << fileName.toStdString() << " could not be created."; + return convert; + } + + QString command; + command.append( QString("import cv\n") ); + command.append( QString("%1 = cv.LoadImage(\"%2\")\n") .arg( varName ).arg( fileName ) ); + MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); + this->Execute(command, IPythonService::MULTI_LINE_COMMAND ); + + MITK_DEBUG("PythonService") << "Removing file " << fileName.toStdString(); + QFile file(fileName); + file.remove(); + return true; + } mitk::Image::Pointer mitk::PythonService::CopyCvImageFromPython( const QString& varName ) { -return 0; + + mitk::Image::Pointer mitkImage; + QString command; + QString fileName = GetTempImageName( QString::fromStdString( ".bmp" ) ); + + MITK_DEBUG("PythonService") << "run python command to save image with opencv to " << fileName.toStdString(); + command.append( QString("import cv\n") ); + command.append( QString( "cv.SaveImage(\"%1\", %2)\n").arg( fileName ).arg( varName ) ); + + MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); + this->Execute(command, IPythonService::MULTI_LINE_COMMAND ); + + try + { + MITK_DEBUG("PythonService") << "Loading temporary file " << fileName.toStdString() << " as MITK image"; + mitkImage = mitk::IOUtil::LoadImage( fileName.toStdString() ); + } + catch(std::exception& e) + { + MITK_ERROR << e.what(); + } + + QFile file(fileName); + if( file.exists() ) + { + MITK_DEBUG("PythonService") << "Removing temporary file " << fileName.toStdString(); + file.remove(); + } + + return mitkImage; } ctkAbstractPythonManager *mitk::PythonService::GetPythonManager() { - return &m_PythonManager; + return &m_PythonManager; } + diff --git a/Modules/Python/resources/document-new.png b/Modules/Python/resources/document-new.png new file mode 100644 index 0000000000..e6d64bb90b Binary files /dev/null and b/Modules/Python/resources/document-new.png differ diff --git a/Modules/Python/resources/document-open.png b/Modules/Python/resources/document-open.png new file mode 100644 index 0000000000..f35f258354 Binary files /dev/null and b/Modules/Python/resources/document-open.png differ diff --git a/Modules/Python/resources/document-save.png b/Modules/Python/resources/document-save.png new file mode 100644 index 0000000000..db5c52b769 Binary files /dev/null and b/Modules/Python/resources/document-save.png differ diff --git a/Modules/Python/resources/edit-clear.png b/Modules/Python/resources/edit-clear.png new file mode 100644 index 0000000000..5542948bca Binary files /dev/null and b/Modules/Python/resources/edit-clear.png differ diff --git a/Modules/Python/resources/edit-delete.png b/Modules/Python/resources/edit-delete.png new file mode 100644 index 0000000000..9becb3e2f3 Binary files /dev/null and b/Modules/Python/resources/edit-delete.png differ diff --git a/Modules/Python/resources/edit-find-replace.png b/Modules/Python/resources/edit-find-replace.png new file mode 100644 index 0000000000..0f1b117ff7 Binary files /dev/null and b/Modules/Python/resources/edit-find-replace.png differ diff --git a/Modules/Python/resources/edit-paste.png b/Modules/Python/resources/edit-paste.png new file mode 100644 index 0000000000..dd429ced62 Binary files /dev/null and b/Modules/Python/resources/edit-paste.png differ diff --git a/Modules/Python/resources/media-playback-start.png b/Modules/Python/resources/media-playback-start.png new file mode 100644 index 0000000000..66f32d89b5 Binary files /dev/null and b/Modules/Python/resources/media-playback-start.png differ diff --git a/Modules/Python/resources/mitkPython.qrc b/Modules/Python/resources/mitkPython.qrc new file mode 100644 index 0000000000..f0752ede10 --- /dev/null +++ b/Modules/Python/resources/mitkPython.qrc @@ -0,0 +1,12 @@ + + + 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 + + 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 310f56ea69..6443bd286f 100644 --- a/Plugins/org.mitk.gui.qt.python/src/internal/QmitkPythonView.cpp +++ b/Plugins/org.mitk.gui.qt.python/src/internal/QmitkPythonView.cpp @@ -1,67 +1,86 @@ /*=================================================================== 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 "QmitkCtkPythonShell.h" +#include #include "mitkPluginActivator.h" -#include "QmitkPythonVariableStackTableView.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_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(d->m_PythonVariableStackTableView); - splitter->addWidget(d->m_PythonShell); + 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); } void QmitkPythonView::SetFocus() { d->m_PythonShell->setFocus(); }