Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Expose ctkPythonConsoleCompleter on the public CTK API #1062

Merged
merged 1 commit into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 49 additions & 33 deletions Libs/Scripting/Python/Widgets/ctkPythonConsole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//----------------------------------------------------------------------------
// ctkPythonConsoleCompleter

//----------------------------------------------------------------------------
class ctkPythonConsoleCompleter : public ctkConsoleCompleter
class ctkPythonConsoleCompleterPrivate
{
Q_DECLARE_PUBLIC(ctkPythonConsoleCompleter);
protected:
ctkPythonConsoleCompleter* const q_ptr;

public:
ctkPythonConsoleCompleter(ctkAbstractPythonManager& pythonManager);
ctkPythonConsoleCompleterPrivate(ctkPythonConsoleCompleter& o, ctkAbstractPythonManager& pythonManager)
: q_ptr(&o)
, PythonManager(pythonManager)
{
}

virtual ~ctkPythonConsoleCompleterPrivate()
{
}

virtual int cursorOffset(const QString& completion);
virtual void updateCompletionModel(const QString& completion);
static QString searchUsableCharForCompletion(const QString& completion);

/// Sort Python attributes in the following groups (with case insensitive sorting within each group):
Expand All @@ -95,11 +104,10 @@ class ctkPythonConsoleCompleter : public ctkConsoleCompleter
/// - private attributes (starts with underscore)
static bool PythonAttributeLessThan(const QString& s1, const QString& s2);

protected:
bool isInUserDefinedClass(const QString &pythonFunctionPath);
bool isUserDefinedFunction(const QString &pythonFunctionName);
bool isBuiltInFunction(const QString &pythonFunctionName);
int parameterCountUserDefinedClassFunction(const QString &pythonFunctionName);
bool isInUserDefinedClass(const QString& pythonFunctionPath);
bool isUserDefinedFunction(const QString& pythonFunctionName);
bool isBuiltInFunction(const QString& pythonFunctionName);
int parameterCountUserDefinedClassFunction(const QString& pythonFunctionName);
int parameterCountBuiltInFunction(const QString& pythonFunctionName);
int parameterCountUserDefinedFunction(const QString& pythonFunctionName);
int parameterCountFromDocumentation(const QString& pythonFunctionPath);
Expand All @@ -109,14 +117,21 @@ class ctkPythonConsoleCompleter : public ctkConsoleCompleter

//----------------------------------------------------------------------------
ctkPythonConsoleCompleter::ctkPythonConsoleCompleter(ctkAbstractPythonManager& pythonManager)
: PythonManager(pythonManager)
{
: d_ptr(new ctkPythonConsoleCompleterPrivate(*this, pythonManager))
{
this->setParent(&pythonManager);
}
}

//----------------------------------------------------------------------------
ctkPythonConsoleCompleter::~ctkPythonConsoleCompleter()
{
}


//----------------------------------------------------------------------------
int ctkPythonConsoleCompleter::cursorOffset(const QString& completion)
{
Q_D(ctkPythonConsoleCompleter);
QString allTextFromShell = completion;
int parameterCount = 0;
int cursorOffset = 0;
Expand Down Expand Up @@ -145,24 +160,24 @@ int ctkPythonConsoleCompleter::cursorOffset(const QString& completion)
QString functionName = lineSplit.at(lineSplit.length()-1);
QStringList builtinFunctionPath = QStringList() << "__main__" << "__builtins__";
QStringList userDefinedFunctionPath = QStringList() << "__main__";
if (this->isBuiltInFunction(functionName))
if (d->isBuiltInFunction(functionName))
{
parameterCount = this->parameterCountBuiltInFunction(QStringList(builtinFunctionPath+lineSplit).join("."));
parameterCount = d->parameterCountBuiltInFunction(QStringList(builtinFunctionPath+lineSplit).join("."));
}
else if (this->isUserDefinedFunction(functionName))
else if (d->isUserDefinedFunction(functionName))
{
parameterCount = this->parameterCountUserDefinedFunction(QStringList(userDefinedFunctionPath+lineSplit).join("."));
parameterCount = d->parameterCountUserDefinedFunction(QStringList(userDefinedFunctionPath+lineSplit).join("."));
}
else if (this->isInUserDefinedClass(currentCompletionText))
else if (d->isInUserDefinedClass(currentCompletionText))
{
// "self" parameter can be ignored
parameterCount = this->parameterCountUserDefinedClassFunction(QStringList(userDefinedFunctionPath+lineSplit).join(".")) - 1;
parameterCount = d->parameterCountUserDefinedClassFunction(QStringList(userDefinedFunctionPath+lineSplit).join(".")) - 1;
}
else
{
QStringList variableNameAndFunctionList = userDefinedFunctionPath + lineSplit;
QString variableNameAndFunction = variableNameAndFunctionList.join(".");
parameterCount = this->parameterCountFromDocumentation(variableNameAndFunction);
parameterCount = d->parameterCountFromDocumentation(variableNameAndFunction);
}
}
if (parameterCount > 0)
Expand All @@ -174,25 +189,25 @@ int ctkPythonConsoleCompleter::cursorOffset(const QString& completion)


//---------------------------------------------------------------------------
bool ctkPythonConsoleCompleter::isInUserDefinedClass(const QString &pythonFunctionPath)
bool ctkPythonConsoleCompleterPrivate::isInUserDefinedClass(const QString &pythonFunctionPath)
{
return this->PythonManager.pythonAttributes(pythonFunctionPath).contains("__func__");
}

//---------------------------------------------------------------------------
bool ctkPythonConsoleCompleter::isUserDefinedFunction(const QString &pythonFunctionName)
bool ctkPythonConsoleCompleterPrivate::isUserDefinedFunction(const QString &pythonFunctionName)
{
return this->PythonManager.pythonAttributes(pythonFunctionName).contains("__call__");
}

//---------------------------------------------------------------------------
bool ctkPythonConsoleCompleter::isBuiltInFunction(const QString &pythonFunctionName)
bool ctkPythonConsoleCompleterPrivate::isBuiltInFunction(const QString &pythonFunctionName)
{
return this->PythonManager.pythonAttributes(pythonFunctionName, QLatin1String("__main__.__builtins__")).contains("__call__");
}

//---------------------------------------------------------------------------
int ctkPythonConsoleCompleter::parameterCountBuiltInFunction(const QString& pythonFunctionName)
int ctkPythonConsoleCompleterPrivate::parameterCountBuiltInFunction(const QString& pythonFunctionName)
{
int parameterCount = 0;
PyObject* pFunction = this->PythonManager.pythonModule(pythonFunctionName);
Expand Down Expand Up @@ -220,7 +235,7 @@ int ctkPythonConsoleCompleter::parameterCountBuiltInFunction(const QString& pyth
}

//----------------------------------------------------------------------------
int ctkPythonConsoleCompleter::parameterCountUserDefinedFunction(const QString& pythonFunctionName)
int ctkPythonConsoleCompleterPrivate::parameterCountUserDefinedFunction(const QString& pythonFunctionName)
{
int parameterCount = 0;
PyObject* pFunction = this->PythonManager.pythonModule(pythonFunctionName);
Expand All @@ -246,7 +261,7 @@ int ctkPythonConsoleCompleter::parameterCountUserDefinedFunction(const QString&
}

//----------------------------------------------------------------------------
int ctkPythonConsoleCompleter::parameterCountUserDefinedClassFunction(const QString& pythonFunctionName)
int ctkPythonConsoleCompleterPrivate::parameterCountUserDefinedClassFunction(const QString& pythonFunctionName)
{
int parameterCount = 0;
PyObject* pFunction = this->PythonManager.pythonObject(pythonFunctionName);
Expand All @@ -272,7 +287,7 @@ int ctkPythonConsoleCompleter::parameterCountUserDefinedClassFunction(const QStr
}

//----------------------------------------------------------------------------
int ctkPythonConsoleCompleter::parameterCountFromDocumentation(const QString& pythonFunctionPath)
int ctkPythonConsoleCompleterPrivate::parameterCountFromDocumentation(const QString& pythonFunctionPath)
{
int parameterCount = 0;
PyObject* pFunction = this->PythonManager.pythonObject(pythonFunctionPath);
Expand Down Expand Up @@ -300,7 +315,7 @@ int ctkPythonConsoleCompleter::parameterCountFromDocumentation(const QString& py
}

//----------------------------------------------------------------------------
QString ctkPythonConsoleCompleter::searchUsableCharForCompletion(const QString& completion)
QString ctkPythonConsoleCompleterPrivate::searchUsableCharForCompletion(const QString& completion)
{
bool betweenSingleQuotes = false;
bool betweenDoubleQuotes = false;
Expand Down Expand Up @@ -352,7 +367,7 @@ QString ctkPythonConsoleCompleter::searchUsableCharForCompletion(const QString&
}

//----------------------------------------------------------------------------
bool ctkPythonConsoleCompleter::PythonAttributeLessThan(const QString& s1, const QString& s2)
bool ctkPythonConsoleCompleterPrivate::PythonAttributeLessThan(const QString& s1, const QString& s2)
{
if (!s1.isEmpty() || !s2.isEmpty())
{
Expand Down Expand Up @@ -396,6 +411,7 @@ bool ctkPythonConsoleCompleter::PythonAttributeLessThan(const QString& s1, const
//----------------------------------------------------------------------------
void ctkPythonConsoleCompleter::updateCompletionModel(const QString& completion)
{
Q_D(ctkPythonConsoleCompleter);
// Start by clearing the model
this->setModel(0);

Expand All @@ -407,7 +423,7 @@ void ctkPythonConsoleCompleter::updateCompletionModel(const QString& completion)

bool appendParenthesis = true;
// Search backward through the string for usable characters
QString textToComplete = searchUsableCharForCompletion(completion);
QString textToComplete = ctkPythonConsoleCompleterPrivate::searchUsableCharForCompletion(completion);

// Split the string at the last dot, if one exists
QString lookup;
Expand All @@ -424,12 +440,12 @@ void ctkPythonConsoleCompleter::updateCompletionModel(const QString& completion)
if (!lookup.isEmpty() || !compareText.isEmpty())
{
QString module = "__main__";
attrs = this->PythonManager.pythonAttributes(lookup, module.toLatin1(), appendParenthesis);
attrs = d->PythonManager.pythonAttributes(lookup, module.toLatin1(), appendParenthesis);
module = "__main__.__builtins__";
attrs << this->PythonManager.pythonAttributes(lookup, module.toLatin1(),
attrs << d->PythonManager.pythonAttributes(lookup, module.toLatin1(),
appendParenthesis);
attrs.removeDuplicates();
std::sort(attrs.begin(), attrs.end(), ctkPythonConsoleCompleter::PythonAttributeLessThan);
std::sort(attrs.begin(), attrs.end(), ctkPythonConsoleCompleterPrivate::PythonAttributeLessThan);
}

// Initialize the completion model
Expand Down
42 changes: 37 additions & 5 deletions Libs/Scripting/Python/Widgets/ctkPythonConsole.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
All rights reserved.

ParaView is a free software; you can redistribute it and/or modify it
under the terms of the ParaView license version 1.2.
under the terms of the ParaView license version 1.2.

See http://www.paraview.org/paraview/project/license.html for the full ParaView license.
A copy of this license can be obtained by contacting
Expand Down Expand Up @@ -55,10 +55,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <ctkConsole.h>
#include "ctkScriptingPythonWidgetsExport.h"

// Qt includes
#include <QScopedPointer>

/// \ingroup Scripting_Python_Widgets
///
/// Qt widget that provides an interactive "shell" interface to an embedded Python interpreter.
/// You can put an instance of ctkPythonConsole in a dialog or a window, and the user will be able
/// Qt widget that provides an interactive "shell" interface to an embedded Python interpreter.
/// You can put an instance of ctkPythonConsole in a dialog or a window, and the user will be able
/// to enter Python commands and see their output, while the UI is still responsive.
///
/// \sa ctkConsole
Expand All @@ -69,7 +72,7 @@ class ctkAbstractPythonManager;
class CTK_SCRIPTING_PYTHON_WIDGETS_EXPORT ctkPythonConsole : public ctkConsole
{
Q_OBJECT

public:
typedef ctkConsole Superclass;
ctkPythonConsole(QWidget* parentObject = 0);
Expand Down Expand Up @@ -106,5 +109,34 @@ public Q_SLOTS:
Q_DISABLE_COPY(ctkPythonConsole);
};

#endif
/// \ingroup Scripting_Python_Widgets
///
/// Class that provides list of potential completions for an incomplete input string.
/// ctkPythonConsole sets up a ctkPythonConsoleCompleter by default but applications
/// can subclass and customize this completer and set the custom version in the
/// console.
///
/// \sa ctkPythonConsole

class ctkPythonConsoleCompleterPrivate;

class CTK_SCRIPTING_PYTHON_WIDGETS_EXPORT ctkPythonConsoleCompleter : public ctkConsoleCompleter
{
Q_OBJECT

public:
ctkPythonConsoleCompleter(ctkAbstractPythonManager& pythonManager);
virtual ~ctkPythonConsoleCompleter();

int cursorOffset(const QString& completion) override;
void updateCompletionModel(const QString& completion) override;

protected:
QScopedPointer<ctkPythonConsoleCompleterPrivate> d_ptr;

private:
Q_DECLARE_PRIVATE(ctkPythonConsoleCompleter);
Q_DISABLE_COPY(ctkPythonConsoleCompleter);
};

#endif