From ea4a6948be8c1c7b7018a81722e32e361fb91f90 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 9 Oct 2022 14:26:42 +0200 Subject: [PATCH] Gui: add class TaskDialogPy --- src/Gui/Application.cpp | 1 + src/Gui/TaskView/TaskDialog.h | 20 +++ src/Gui/TaskView/TaskDialogPython.cpp | 212 ++++++++++++++++++++++++++ src/Gui/TaskView/TaskDialogPython.h | 67 ++++++++ src/Gui/TaskView/TaskView.cpp | 1 + 5 files changed, 301 insertions(+) diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index c5cfd2bd0f..8e3525f3a3 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -428,6 +428,7 @@ Application::Application(bool GUIenabled) Gui::TaskView::ControlPy::init_type(); Py::Module(module).setAttr(std::string("Control"), Py::Object(Gui::TaskView::ControlPy::getInstance(), true)); + Gui::TaskView::TaskDialogPy::init_type(); Base::Interpreter().addType(&LinkViewPy::Type,module,"LinkView"); Base::Interpreter().addType(&AxisOriginPy::Type,module,"AxisOrigin"); diff --git a/src/Gui/TaskView/TaskDialog.h b/src/Gui/TaskView/TaskDialog.h index b399793330..f344f633c6 100644 --- a/src/Gui/TaskView/TaskDialog.h +++ b/src/Gui/TaskView/TaskDialog.h @@ -28,6 +28,7 @@ #include #include +#include #include @@ -39,6 +40,9 @@ namespace Gui { namespace TaskView { class TaskContent; +class TaskDialogAttorney; +class TaskDialogPy; +class TaskView; /// Father class of content with header and Icon class GuiExport TaskDialog : public QObject @@ -133,6 +137,7 @@ Q_SIGNALS: void aboutToBeDestroyed(); protected: + QPointer buttonBox; /// List of TaskBoxes of that dialog std::vector Content; ButtonPosition pos; @@ -141,6 +146,21 @@ private: std::string documentName; bool escapeButton; bool autoCloseTransaction; + + friend class TaskDialogAttorney; +}; + +class TaskDialogAttorney { +private: + static void setButtonBox(TaskDialog* dlg, QDialogButtonBox* box) { + dlg->buttonBox = box; + } + static QDialogButtonBox* getButtonBox(TaskDialog* dlg) { + return dlg->buttonBox; + } + + friend class TaskDialogPy; + friend class TaskView; }; } //namespace TaskView diff --git a/src/Gui/TaskView/TaskDialogPython.cpp b/src/Gui/TaskView/TaskDialogPython.cpp index 55012ee715..e79240f008 100644 --- a/src/Gui/TaskView/TaskDialogPython.cpp +++ b/src/Gui/TaskView/TaskDialogPython.cpp @@ -68,6 +68,9 @@ void ControlPy::init_type() add_varargs_method("activeDialog",&ControlPy::activeDialog, "check if a dialog is active in the task panel\n" "activeDialog() --> bool"); + add_varargs_method("activeTaskDialog",&ControlPy::activeTaskDialog, + "return the active task dialog if there is one\n" + "activeTaskDialog() --> TaskDialog or None"); add_varargs_method("closeDialog",&ControlPy::closeDialog, "close the active dialog\n" "closeDialog()"); @@ -131,6 +134,14 @@ Py::Object ControlPy::activeDialog(const Py::Tuple& args) return Py::Boolean(dlg != nullptr); } +Py::Object ControlPy::activeTaskDialog(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); + return (dlg ? Py::asObject(new TaskDialogPy(dlg)) : Py::None()); +} + Py::Object ControlPy::closeDialog(const Py::Tuple& args) { if (!PyArg_ParseTuple(args.ptr(), "")) @@ -306,6 +317,207 @@ bool TaskWatcherPython::shouldShow() // ------------------------------------------------------------------ +void TaskDialogPy::init_type() +{ + behaviors().name("TaskDialog"); + behaviors().doc("Task dialog"); + // you must have overwritten the virtual functions + behaviors().supportRepr(); + behaviors().supportGetattr(); + behaviors().supportSetattr(); + add_varargs_method("getDialogContent",&TaskDialogPy::getDialogContent, + "Returns the widgets of the task dialog -> list"); + add_varargs_method("getStandardButtons",&TaskDialogPy::getStandardButtons, + "Get the standard buttons of the box -> flags"); + add_varargs_method("setEscapeButtonEnabled",&TaskDialogPy::setEscapeButtonEnabled, + "Defines whether the task dialog can be rejected by pressing Esc"); + add_varargs_method("isEscapeButtonEnabled",&TaskDialogPy::isEscapeButtonEnabled, + "Checks if the task dialog can be rejected by pressing Esc -> bool"); + add_varargs_method("setAutoCloseOnTransactionChange",&TaskDialogPy::setAutoCloseOnTransactionChange, + "Defines whether a task dialog must be closed if the document changes the\n" + "active transaction"); + add_varargs_method("isAutoCloseOnTransactionChange",&TaskDialogPy::isAutoCloseOnTransactionChange, + "Checks if the task dialog will be closed when the active transaction has changed -> bool"); + add_varargs_method("getDocumentName",&TaskDialogPy::getDocumentName, + "Get the name of the document the task dialog is attached to -> str"); + add_varargs_method("isAllowedAlterDocument",&TaskDialogPy::isAllowedAlterDocument, + "Indicates whether this task dialog allows other commands to modify\n" + "the document while it is open -> bool"); + add_varargs_method("isAllowedAlterView",&TaskDialogPy::isAllowedAlterView, + "Indicates whether this task dialog allows other commands to modify\n" + "the 3d view while it is open -> bool"); + add_varargs_method("isAllowedAlterSelection",&TaskDialogPy::isAllowedAlterSelection, + "Indicates whether this task dialog allows other commands to modify\n" + "the selection while it is open -> bool"); + add_varargs_method("needsFullSpace",&TaskDialogPy::needsFullSpace, + "Indicates whether the task dialog fully requires the available space -> bool"); + add_varargs_method("accept",&TaskDialogPy::accept, + "Accept the task dialog"); + add_varargs_method("reject",&TaskDialogPy::reject, + "Reject the task dialog"); +} + +TaskDialogPy::TaskDialogPy(TaskDialog* dlg) + : dialog(dlg) +{ +} + +TaskDialogPy::~TaskDialogPy() +{ +} + +Py::Object TaskDialogPy::repr() +{ + std::string s; + std::ostringstream s_out; + s_out << "Task Dialog"; + return Py::String(s_out.str()); +} + +Py::Object TaskDialogPy::getattr(const char * attr) +{ + if (!dialog) { + std::ostringstream s_out; + s_out << "Cannot access attribute '" << attr << "' of deleted object"; + throw Py::RuntimeError(s_out.str()); + } + return BaseType::getattr(attr); +} + +int TaskDialogPy::setattr(const char *attr, const Py::Object &value) +{ + if (!dialog) { + std::ostringstream s_out; + s_out << "Cannot access attribute '" << attr << "' of deleted object"; + throw Py::RuntimeError(s_out.str()); + } + return BaseType::setattr(attr, value); +} + +Py::Object TaskDialogPy::getDialogContent(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + + PythonWrapper wrap; + wrap.loadWidgetsModule(); + + Py::List list; + auto widgets = dialog->getDialogContent(); + for (auto it : widgets) { + list.append(wrap.fromQWidget(it)); + } + + return list; +} + +Py::Object TaskDialogPy::getStandardButtons(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + auto buttons = dialog->getStandardButtons(); + return Py::Long(static_cast(buttons)); +} + +Py::Object TaskDialogPy::setEscapeButtonEnabled(const Py::Tuple& args) +{ + Py::Boolean value(args[0]); + dialog->setEscapeButtonEnabled(static_cast(value)); + return Py::None(); +} + +Py::Object TaskDialogPy::isEscapeButtonEnabled(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + return Py::Boolean(dialog->isEscapeButtonEnabled()); +} + +Py::Object TaskDialogPy::setAutoCloseOnTransactionChange(const Py::Tuple& args) +{ + Py::Boolean value(args[0]); + dialog->setAutoCloseOnTransactionChange(static_cast(value)); + return Py::None(); +} + +Py::Object TaskDialogPy::isAutoCloseOnTransactionChange(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + return Py::Boolean(dialog->isAutoCloseOnTransactionChange()); +} + +Py::Object TaskDialogPy::getDocumentName(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + return Py::String(dialog->getDocumentName()); +} + +Py::Object TaskDialogPy::isAllowedAlterDocument(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + return Py::Boolean(dialog->isAllowedAlterDocument()); +} + +Py::Object TaskDialogPy::isAllowedAlterView(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + return Py::Boolean(dialog->isAllowedAlterView()); +} + +Py::Object TaskDialogPy::isAllowedAlterSelection(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + return Py::Boolean(dialog->isAllowedAlterSelection()); +} + +Py::Object TaskDialogPy::needsFullSpace(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + return Py::Boolean(dialog->needsFullSpace()); +} + +namespace { +auto clickButton = [](QDialogButtonBox* buttonBox, QDialogButtonBox::ButtonRole role) { + if (buttonBox) { + QList list = buttonBox->buttons(); + for (auto pb : list) { + if (buttonBox->buttonRole(pb) == role) { + if (pb->isEnabled()) { + pb->click(); + break; + } + } + } + } +}; +} + +Py::Object TaskDialogPy::accept(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + auto buttonBox = TaskDialogAttorney::getButtonBox(dialog); + clickButton(buttonBox, QDialogButtonBox::AcceptRole); + return Py::None(); +} + +Py::Object TaskDialogPy::reject(const Py::Tuple& args) +{ + if (!PyArg_ParseTuple(args.ptr(), "")) + throw Py::Exception(); + auto buttonBox = TaskDialogAttorney::getButtonBox(dialog); + clickButton(buttonBox, QDialogButtonBox::RejectRole); + return Py::None(); +} + +// ------------------------------------------------------------------ + TaskDialogPython::TaskDialogPython(const Py::Object& o) : dlg(o) { if (dlg.hasAttr(std::string("ui"))) { diff --git a/src/Gui/TaskView/TaskDialogPython.h b/src/Gui/TaskView/TaskDialogPython.h index fb08258fe2..838a2d1e36 100644 --- a/src/Gui/TaskView/TaskDialogPython.h +++ b/src/Gui/TaskView/TaskDialogPython.h @@ -43,6 +43,7 @@ public: Py::Object repr() override; Py::Object showDialog(const Py::Tuple&); Py::Object activeDialog(const Py::Tuple&); + Py::Object activeTaskDialog(const Py::Tuple&); Py::Object closeDialog(const Py::Tuple&); Py::Object addTaskWatcher(const Py::Tuple&); Py::Object clearTaskWatcher(const Py::Tuple&); @@ -67,6 +68,72 @@ private: Py::Object watcher; }; +/** + * @brief The TaskDialogPy class + * This class exposes a TaskDialog written in C++ to Python. + */ +class TaskDialogPy : public Py::PythonExtension +{ +public: + using BaseType = Py::PythonExtension; + static void init_type(); // announce properties and methods + + explicit TaskDialogPy(TaskDialog*); + ~TaskDialogPy() override; + + Py::Object repr() override; + Py::Object getattr(const char *) override; + int setattr(const char *, const Py::Object &) override; + +public: + Py::Object getDialogContent(const Py::Tuple&); + + /// tells the framework which buttons are wished for the dialog + Py::Object getStandardButtons(const Py::Tuple&); + + /// Defines whether a task dialog can be rejected by pressing Esc + Py::Object setEscapeButtonEnabled(const Py::Tuple&); + Py::Object isEscapeButtonEnabled(const Py::Tuple&); + + /// Defines whether a task dialog must be closed if the document changed the + /// active transaction. + Py::Object setAutoCloseOnTransactionChange(const Py::Tuple&); + Py::Object isAutoCloseOnTransactionChange(const Py::Tuple&); + + Py::Object getDocumentName(const Py::Tuple&); + + /*! + Indicates whether this task dialog allows other commands to modify + the document while it is open. + */ + Py::Object isAllowedAlterDocument(const Py::Tuple&); + + /*! + Indicates whether this task dialog allows other commands to modify + the 3d view while it is open. + */ + Py::Object isAllowedAlterView(const Py::Tuple&); + + /*! + Indicates whether this task dialog allows other commands to modify + the selection while it is open. + */ + Py::Object isAllowedAlterSelection(const Py::Tuple&); + Py::Object needsFullSpace(const Py::Tuple&); + + /// is called by the framework if the dialog is accepted (Ok) + Py::Object accept(const Py::Tuple&); + /// is called by the framework if the dialog is rejected (Cancel) + Py::Object reject(const Py::Tuple&); + +private: + QPointer dialog; +}; + +/** + * @brief The TaskDialogPython class + * This wraps a task dialog that is written in Python. + */ class GuiExport TaskDialogPython : public TaskDialog { public: diff --git a/src/Gui/TaskView/TaskView.cpp b/src/Gui/TaskView/TaskView.cpp index 2f72394c0a..da0774240e 100644 --- a/src/Gui/TaskView/TaskView.cpp +++ b/src/Gui/TaskView/TaskView.cpp @@ -479,6 +479,7 @@ void TaskView::showDialog(TaskDialog *dlg) // first create the control element, set it up and wire it: ActiveCtrl = new TaskEditControl(this); ActiveCtrl->buttonBox->setStandardButtons(dlg->getStandardButtons()); + TaskDialogAttorney::setButtonBox(dlg, ActiveCtrl->buttonBox); // make connection to the needed signals connect(ActiveCtrl->buttonBox,SIGNAL(accepted()),