diff --git a/buildconfig/CMake/CommonSetup.cmake b/buildconfig/CMake/CommonSetup.cmake index 7da4bd194f81..9e1d784a9c24 100644 --- a/buildconfig/CMake/CommonSetup.cmake +++ b/buildconfig/CMake/CommonSetup.cmake @@ -19,6 +19,7 @@ endif() option(ENABLE_OPENGL "Enable OpenGLbased rendering" ON) option(ENABLE_OPENCASCADE "Enable OpenCascade-based 3D visualisation" ON) option(USE_PYTHON_DYNAMIC_LIB "Dynamic link python libs" ON) +option(ENABLE_QTASSISTANT "If enabled, add a target to build the qthelp documentation for qtassistant" ON) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) make_directory(${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Testing) diff --git a/qt/applications/workbench/workbench/app/start.py b/qt/applications/workbench/workbench/app/start.py index 667c1d5a5ce1..fde959993d0a 100644 --- a/qt/applications/workbench/workbench/app/start.py +++ b/qt/applications/workbench/workbench/app/start.py @@ -21,6 +21,10 @@ # Find Qt plugins for development builds on some platforms plugins.setup_library_paths() +# This import is needed for new PythonHelpWindow implementation, +# these imports are needed before starting the application +from qtpy.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings # noqa: F401, E402 + from qtpy.QtGui import QIcon, QSurfaceFormat # noqa: E402 from qtpy.QtWidgets import QApplication # noqa: E402 from qtpy.QtCore import QCoreApplication, Qt # noqa: E402 diff --git a/qt/python/mantidqt/mantidqt/widgets/helpwindow/helpwindowbridge.py b/qt/python/mantidqt/mantidqt/widgets/helpwindow/helpwindowbridge.py index 47173bac6d78..cd1b535f0bbb 100644 --- a/qt/python/mantidqt/mantidqt/widgets/helpwindow/helpwindowbridge.py +++ b/qt/python/mantidqt/mantidqt/widgets/helpwindow/helpwindowbridge.py @@ -26,7 +26,7 @@ def show_help_page(relative_url, local_docs=None, online_base_url="https://docs. _presenter.showHelpPage(relative_url) -def main(cmdargs=None): +def main(cmdargs=sys.argv): """ Run this script standalone to test the Python-based Help Window. """ diff --git a/qt/widgets/common/CMakeLists.txt b/qt/widgets/common/CMakeLists.txt index 2c9babe5fb0c..f082c9c268a6 100644 --- a/qt/widgets/common/CMakeLists.txt +++ b/qt/widgets/common/CMakeLists.txt @@ -159,6 +159,11 @@ set(SRC_FILES src/Python/QHashToDict.cpp ) +if(NOT ENABLE_QTASSISTANT) + list(APPEND SRC_FILES src/PythonHelpBridge.cpp) + list(APPEND SRC_FILES src/PythonHelpWindow.cpp) +endif() + set(QT5_MOC_FILES inc/MantidQtWidgets/Common/AddWorkspaceDialog.h inc/MantidQtWidgets/Common/AddWorkspaceMultiDialog.h @@ -262,6 +267,11 @@ set(QT5_MOC_FILES inc/MantidQtWidgets/Common/QtPropertyBrowser/WorkspaceEditorFactory.h ) +if(NOT ENABLE_QTASSISTANT) + list(APPEND QT5_MOC_FILES inc/MantidQtWidgets/Common/PythonHelpBridge.h) + list(APPEND QT5_MOC_FILES inc/MantidQtWidgets/Common/PythonHelpWindow.h) +endif() + # Include files aren't required, but this makes them appear in Visual Studio set(QT5_INC_FILES ${QT5_MOC_FILES} @@ -637,7 +647,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") endif() # Add option to the compiler to register MantidHelpWindow -if(DOCS_QTHELP) +if(ENABLE_QTASSISTANT) add_definitions(-DDOCS_QTHELP) endif() @@ -740,6 +750,10 @@ set(TEST_FILES test/WorkspacePresenter/WorkspacePresenterTest.h ) +if(NOT ENABLE_QTASSISTANT) + list(APPEND TEST_FILES test/PythonHelpWindowTest.h) +endif() + set(CXXTEST_EXTRA_HEADER_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/test/WidgetsCommonTestInitialization.h) set(QT5_TEST_FILES diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/InterfaceManager.h b/qt/widgets/common/inc/MantidQtWidgets/Common/InterfaceManager.h index e8c8fe0d3a45..4b6dce8fc453 100644 --- a/qt/widgets/common/inc/MantidQtWidgets/Common/InterfaceManager.h +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/InterfaceManager.h @@ -138,6 +138,6 @@ class EXPORT_OPT_MANTIDQT_COMMON InterfaceManager { namespace { \ Mantid::Kernel::RegistrationHelper \ register_helpviewer(((MantidQt::API::InterfaceManager::registerHelpWindowFactory( \ - new Mantid::Kernel::Instantiator())), \ + new Mantid::Kernel::Instantiator())), \ 0)); \ } diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/PythonHelpBridge.h b/qt/widgets/common/inc/MantidQtWidgets/Common/PythonHelpBridge.h new file mode 100644 index 000000000000..2740d038fad1 --- /dev/null +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/PythonHelpBridge.h @@ -0,0 +1,22 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2013 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include "MantidQtWidgets/Common/DllOption.h" +#include + +namespace MantidQt { +namespace MantidWidgets { + +class EXPORT_OPT_MANTIDQT_COMMON PythonHelpBridge { +public: + PythonHelpBridge(); + void showHelpPage(const std::string &relative_url); +}; + +} // namespace MantidWidgets +} // namespace MantidQt diff --git a/qt/widgets/common/inc/MantidQtWidgets/Common/PythonHelpWindow.h b/qt/widgets/common/inc/MantidQtWidgets/Common/PythonHelpWindow.h new file mode 100644 index 000000000000..2e1c342b4acb --- /dev/null +++ b/qt/widgets/common/inc/MantidQtWidgets/Common/PythonHelpWindow.h @@ -0,0 +1,38 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2013 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include "MantidQtWidgets/Common/DllOption.h" +#include "MantidQtWidgets/Common/MantidHelpInterface.h" +#include +#include +#include + +namespace MantidQt { +namespace MantidWidgets { + +class EXPORT_OPT_MANTIDQT_COMMON PythonHelpWindow : public MantidQt::API::MantidHelpInterface { +public: + PythonHelpWindow(); + void showPage(const std::string &url = std::string()) override; + void showPage(const QString &url) override; + void showPage(const QUrl &url) override; + void showAlgorithm(const std::string &name = std::string(), const int version = -1) override; + void showAlgorithm(const QString &name, const int version = -1) override; + void showConcept(const std::string &name) override; + void showConcept(const QString &name) override; + void showFitFunction(const std::string &name = std::string()) override; + void showFitFunction(const QString &name) override; + void showCustomInterface(const std::string &name = std::string(), const std::string &area = std::string(), + const std::string §ion = std::string()) override; + void showCustomInterface(const QString &name, const QString &area = QString(), + const QString §ion = QString()) override; + void shutdown() override; +}; + +} // namespace MantidWidgets +} // namespace MantidQt diff --git a/qt/widgets/common/src/PythonHelpBridge.cpp b/qt/widgets/common/src/PythonHelpBridge.cpp new file mode 100644 index 000000000000..43a954b98750 --- /dev/null +++ b/qt/widgets/common/src/PythonHelpBridge.cpp @@ -0,0 +1,39 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#include "MantidQtWidgets/Common/PythonHelpBridge.h" +#include +#include +#include +#include + +namespace { +const std::string MOD_NAME("mantidqt.widgets.helpwindow.helpwindowbridge"); +boost::python::object getHelpWindowModule() { + boost::python::object mod = boost::python::import(boost::python::str(MOD_NAME)); + return mod; +} +} // namespace + +namespace MantidQt { +namespace MantidWidgets { + +PythonHelpBridge::PythonHelpBridge() {} + +void PythonHelpBridge::showHelpPage(const std::string &relative_url) { + PyGILState_STATE gstate = PyGILState_Ensure(); + try { + auto module = getHelpWindowModule(); + module.attr("show_help_page")(relative_url); + } catch (boost::python::error_already_set &) { + PyErr_Print(); + PyGILState_Release(gstate); + throw std::runtime_error("Error calling show_help_page in Python."); + } + PyGILState_Release(gstate); +} +} // namespace MantidWidgets +} // namespace MantidQt diff --git a/qt/widgets/common/src/PythonHelpWindow.cpp b/qt/widgets/common/src/PythonHelpWindow.cpp new file mode 100644 index 000000000000..fdecc2a565b6 --- /dev/null +++ b/qt/widgets/common/src/PythonHelpWindow.cpp @@ -0,0 +1,96 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2013 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#include "MantidQtWidgets/Common/PythonHelpWindow.h" +#include "MantidKernel/RegistrationHelper.h" +#include "MantidQtWidgets/Common/InterfaceManager.h" +#include "MantidQtWidgets/Common/PythonHelpBridge.h" +#include +#include + +namespace MantidQt { +namespace MantidWidgets { + +#ifndef DOCS_QTHELP +REGISTER_HELPWINDOW(PythonHelpWindow) +#endif + +PythonHelpWindow::PythonHelpWindow() : MantidQt::API::MantidHelpInterface() {} + +void PythonHelpWindow::showPage(const std::string &url) { + PythonHelpBridge bridge; + bridge.showHelpPage(url.empty() ? "index.html" : url); +} + +void PythonHelpWindow::showPage(const QString &url) { + PythonHelpBridge bridge; + std::string page = url.isEmpty() ? "index.html" : url.toStdString(); + bridge.showHelpPage(page); +} + +void PythonHelpWindow::showPage(const QUrl &url) { + PythonHelpBridge bridge; + std::string page = url.isEmpty() ? "index.html" : url.toString().toStdString(); + bridge.showHelpPage(page); +} + +void PythonHelpWindow::showAlgorithm(const std::string &name, const int version) { + QString page = "algorithms/" + QString::fromStdString(name); + if (!name.empty() && version > 0) + page += "-v" + QString::number(version) + ".html"; + else if (!name.empty()) + page += ".html"; + else + page = "algorithms/index.html"; + showPage(page); +} + +void PythonHelpWindow::showAlgorithm(const QString &name, const int version) { + std::string n = name.toStdString(); + showAlgorithm(n, version); +} + +void PythonHelpWindow::showConcept(const std::string &name) { + QString page = name.empty() ? "concepts/index.html" : "concepts/" + QString::fromStdString(name) + ".html"; + showPage(page); +} + +void PythonHelpWindow::showConcept(const QString &name) { + std::string n = name.toStdString(); + showConcept(n); +} + +void PythonHelpWindow::showFitFunction(const std::string &name) { + QString page = name.empty() ? "fitting/fitfunctions/index.html" + : "fitting/fitfunctions/" + QString::fromStdString(name) + ".html"; + showPage(page); +} + +void PythonHelpWindow::showFitFunction(const QString &name) { + std::string n = name.toStdString(); + showFitFunction(n); +} + +void PythonHelpWindow::showCustomInterface(const std::string &name, const std::string &area, + const std::string §ion) { + QString areaPath = area.empty() ? "" : QString::fromStdString(area) + "/"; + QString page = "interfaces/" + areaPath + (name.empty() ? "index.html" : QString::fromStdString(name) + ".html"); + if (!section.empty()) + page += "#" + QString::fromStdString(section); + showPage(page); +} + +void PythonHelpWindow::showCustomInterface(const QString &name, const QString &area, const QString §ion) { + std::string n = name.toStdString(); + std::string a = area.toStdString(); + std::string s = section.toStdString(); + showCustomInterface(n, a, s); +} + +void PythonHelpWindow::shutdown() {} + +} // namespace MantidWidgets +} // namespace MantidQt diff --git a/qt/widgets/common/test/PythonHelpWindowTest.h b/qt/widgets/common/test/PythonHelpWindowTest.h new file mode 100644 index 000000000000..a8a281f70495 --- /dev/null +++ b/qt/widgets/common/test/PythonHelpWindowTest.h @@ -0,0 +1,95 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include +#include + +#include +#include + +#include "MantidQtWidgets/Common/PythonHelpBridge.h" +#include "MantidQtWidgets/Common/PythonHelpWindow.h" + +#include "MantidKernel/WarningSuppressions.h" +GNU_DIAG_OFF_SUGGEST_OVERRIDE + +using namespace MantidQt::API; +using namespace MantidQt::MantidWidgets; +using namespace testing; + +GNU_DIAG_ON_SUGGEST_OVERRIDE + +class PythonHelpWindowTest : public CxxTest::TestSuite { +public: + void setUp() override {} + + void tearDown() override {} + + // 1) Test the constructor doesn't throw and creates a valid object + void testConstructorDoesNotThrow() { TS_ASSERT_THROWS_NOTHING(PythonHelpWindow helpWin); } + + // 2) Test showPage(std::string) with an empty string + void testShowPageWithEmptyString() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.showPage(std::string{})); + } + + // 3) Test showPage(std::string) with a non-empty string + void testShowPageWithNonEmptyString() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.showPage("custom_page.html")); + } + + // 4) Test showPage(QString) with an empty string + void testShowPage_QString_Empty() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.showPage(QString())); + } + + // 5) Test showPage(QUrl) with an empty URL + void testShowPage_QUrl_Empty() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.showPage(QUrl())); + } + + // 6) Test showAlgorithm(std::string) with empty name and version + void testShowAlgorithm_NoName() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.showAlgorithm("", -1)); + } + + // 7) Test showAlgorithm(std::string) with valid name and version + void testShowAlgorithm_NameAndVersion() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.showAlgorithm("Load", 2)); + } + + // 8) Test showConcept(std::string) with empty name + void testShowConceptEmpty() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.showConcept("")); + } + + // 9) Test showFitFunction(std::string) with a name + void testShowFitFunctionName() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.showFitFunction("GaussPeak")); + } + + // 10) Test showCustomInterface(std::string) with name, area, section + void testShowCustomInterface() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.showCustomInterface("Reflectometry", "ISISReflectometry", "Usage")); + } + + // 11) Test the shutdown method (currently a no-op) + void testShutdownDoesNotThrow() { + PythonHelpWindow helpWin; + TS_ASSERT_THROWS_NOTHING(helpWin.shutdown()); + } +};