diff --git a/qt/python/CMakeLists.txt b/qt/python/CMakeLists.txt index f285432aea4f493ec4231c1cfc489b272914b11c..08e5cbf0865bb638d2154ff7447b5cc4852c6dca 100644 --- a/qt/python/CMakeLists.txt +++ b/qt/python/CMakeLists.txt @@ -43,6 +43,7 @@ if ( ENABLE_WORKBENCH ) mantidqt/utils/test/test_qt_utils.py mantidqt/widgets/codeeditor/test_codeeditor.py + mantidqt/widgets/codeeditor/test_executablecodeeditor.py mantidqt/widgets/codeeditor/test_execution.py mantidqt/widgets/codeeditor/test_multifileeditor.py diff --git a/qt/python/mantidqt/widgets/codeeditor/editor.py b/qt/python/mantidqt/widgets/codeeditor/editor.py index 2ce2ae4fbd425c2979d57e8a95b69f0c11d0e34a..0aed423995f7bcb0ac2c2940c8e4b8c1adaf8ef6 100644 --- a/qt/python/mantidqt/widgets/codeeditor/editor.py +++ b/qt/python/mantidqt/widgets/codeeditor/editor.py @@ -14,21 +14,70 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from __future__ import (absolute_import) +from __future__ import (absolute_import, unicode_literals) # 3rd party imports -from qtpy.QtWidgets import QTabWidget, QVBoxLayout, QWidget +from qtpy.QtWidgets import QStatusBar, QTabWidget, QVBoxLayout, QWidget # local imports from mantidqt.utils.qt import import_qtlib from .execution import PythonCodeExecution -EDITOR_LANGUAGE = "Python" +DEFAULT_EDITOR_LANGUAGE = "Python" + +EXECUTION_CLS = PythonCodeExecution + +IDLE_STATUS_MSG = "Status: Idle" + +RUNNING_STATUS_MSG = "Status: Running" # Import single-file editor from C++ wrapping CodeEditor = import_qtlib('_widgetscore', 'mantidqt.widgets', 'ScriptEditor') +class ExecutableCodeEditor(QWidget): + + def __init__(self, language=DEFAULT_EDITOR_LANGUAGE, parent=None): + """ + + :param language: Language for syntax highlighting + :param user_globals: Dictionary for global context of execution. + :param user_locals: Dictionary for local context of execution + :param parent: A parent QWidget + """ + super(ExecutableCodeEditor, self).__init__(parent) + + # layout + self.editor = CodeEditor(language, self) + self.status = QStatusBar(self) + layout = QVBoxLayout() + layout.addWidget(self.editor) + layout.addWidget(self.status) + self.setLayout(layout) + layout.setContentsMargins(0, 0, 0, 0) + + self.presenter = ExecutableCodeEditorPresenter(self) + + def execute_all_async(self): + self.presenter.req_execute_all_async() + + +class ExecutableCodeEditorPresenter(object): + """Presenter part of MVP to control actions on the editor""" + + def __init__(self, view, model=None): + self.view = view + self.model = model if model is not None else PythonCodeExecution() + self.view.status.showMessage(IDLE_STATUS_MSG) + + def req_execute_all_async(self): + text = self.view.text() + if not text: + return + self.view.status.showMessage(RUNNING_STATUS_MSG) + self.model.execute_async(text) + + class MultiFileCodeEditor(QWidget): """Provides a tabbed widget for editing multiple files""" @@ -41,9 +90,10 @@ class MultiFileCodeEditor(QWidget): layout = QVBoxLayout() layout.addWidget(self.editors) self.setLayout(layout) + layout.setContentsMargins(0, 0, 0, 0) # add a single editor by default - self.append_new_editor(EDITOR_LANGUAGE) + self.append_new_editor(DEFAULT_EDITOR_LANGUAGE) @property def editor_count(self): @@ -51,4 +101,4 @@ class MultiFileCodeEditor(QWidget): def append_new_editor(self, language): title = "New" - self.editors.addTab(CodeEditor(language, self.editors), title) + self.editors.addTab(ExecutableCodeEditor(language, self.editors), title) diff --git a/qt/python/mantidqt/widgets/codeeditor/test/test_codeeditor.py b/qt/python/mantidqt/widgets/codeeditor/test/test_codeeditor.py index 24e91063f2b1699ab2c79394608f6f91447736dc..ff465ee2e81bc3e85f031a3bdd98d9e234e68434 100644 --- a/qt/python/mantidqt/widgets/codeeditor/test/test_codeeditor.py +++ b/qt/python/mantidqt/widgets/codeeditor/test/test_codeeditor.py @@ -19,8 +19,6 @@ from __future__ import (absolute_import, unicode_literals) # system imports import unittest -# third-party library imports - # local imports from mantidqt.widgets.codeeditor.editor import CodeEditor from mantidqt.utils.qt.testing import requires_qapp @@ -31,8 +29,9 @@ TEST_LANG = "Python" @requires_qapp class CodeEditorTest(unittest.TestCase): + # --------------------------------------------------------------- # Success tests - + # --------------------------------------------------------------- def test_construction_accepts_Python_as_language(self): CodeEditor(TEST_LANG) @@ -61,8 +60,9 @@ class CodeEditorTest(unittest.TestCase): widget.setReadOnly(True) self.assertTrue(widget.isReadOnly()) + # --------------------------------------------------------------- # Failure tests - + # --------------------------------------------------------------- def test_construction_raises_error_for_unknown_language(self): # self.assertRaises causes a segfault here for some reason... try: diff --git a/qt/python/mantidqt/widgets/codeeditor/test/test_executablecodeeditor.py b/qt/python/mantidqt/widgets/codeeditor/test/test_executablecodeeditor.py new file mode 100644 index 0000000000000000000000000000000000000000..ec30f4610ab4e400e06a7e124f06509c1be8a44a --- /dev/null +++ b/qt/python/mantidqt/widgets/codeeditor/test/test_executablecodeeditor.py @@ -0,0 +1,73 @@ +# This file is part of the mantid workbench. +# +# Copyright (C) 2017 mantidproject +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +from __future__ import (absolute_import, unicode_literals) + +# std imports +import unittest + +# 3rd party imports +from qtpy.QtWidgets import QStatusBar +import six + +# local imports +from mantidqt.widgets.codeeditor.editor import ExecutableCodeEditor, ExecutableCodeEditorPresenter +from mantidqt.widgets.codeeditor.execution import PythonCodeExecution + +if six.PY2: + import mock +else: + from unittest import mock + + +class ExecutableCodeEditorPresenterTest(unittest.TestCase): + + def setUp(self): + self.view = mock.create_autospec(ExecutableCodeEditor) + self.view.status = mock.create_autospec(QStatusBar) + self.view.status.showMessage = mock.MagicMock() + self.model = mock.create_autospec(PythonCodeExecution) + + def test_construction_sets_idle_status(self): + ExecutableCodeEditorPresenter(self.view, self.model) + self.view.status.showMessage.assert_called_once_with("Status: Idle") + + def test_execute_all_async_with_empty_code_does_nothing(self): + self.view.text = mock.MagicMock(return_value="") + self.model.execute_async = mock.MagicMock() + + presenter = ExecutableCodeEditorPresenter(self.view, self.model) + self.view.status.showMessage.reset_mock() + presenter.req_execute_all_async() + self.view.text.assert_called_once_with() + self.view.status.showMessage.assert_not_called() + self.model.execute_async.assert_not_called() + + def test_execute_all_async_with_successful_code(self): + code_str = "x = 1 + 2" + self.view.text = mock.MagicMock(return_value=code_str) + self.model.execute_async = mock.MagicMock() + + presenter = ExecutableCodeEditorPresenter(self.view, self.model) + self.view.status.showMessage.reset_mock() + presenter.req_execute_all_async() + self.view.text.assert_called_once_with() + self.view.status.showMessage.assert_called_once_with("Status: Running") + self.model.execute_async.assert_called_once_with(code_str) + + +if __name__ == '__main__': + unittest.main() diff --git a/qt/python/mantidqt/widgets/codeeditor/test/test_multifileeditor.py b/qt/python/mantidqt/widgets/codeeditor/test/test_multifileeditor.py index d14387c10feb80e173b6683be31d6141ba2cb393..b0834dbb536b92bd08cc340585aaa545522c8f79 100644 --- a/qt/python/mantidqt/widgets/codeeditor/test/test_multifileeditor.py +++ b/qt/python/mantidqt/widgets/codeeditor/test/test_multifileeditor.py @@ -35,11 +35,6 @@ class MultiFileCodeEditorTest(unittest.TestCase): widget = MultiFileCodeEditor() self.assertEqual(1, widget.editor_count) - def test_execute_current_file_executes_correct_tab(self): - # set up code in current tab - widget = MultiFileCodeEditor() - editor = self.current_editor() - if __name__ == '__main__': unittest.main()