Merge branch 'master' into master

This commit is contained in:
younghang
2022-05-17 09:03:56 +08:00
committed by GitHub
13 changed files with 126 additions and 90 deletions

View File

@@ -219,7 +219,12 @@ bool Origin::OriginExtension::extensionGetSubObject(DocumentObject *&ret, const
ret = obj->getOriginFeature(name.c_str());
if (!ret)
return false;
ret = ret->getSubObject(subname + name.size() + 1, pyobj, mat, true, depth+1);
const char *dot = strchr(subname, '.');
if (dot)
subname = dot+1;
else
subname = "";
ret = ret->getSubObject(subname, pyobj, mat, true, depth+1);
return true;
}
catch (const Base::Exception& e) {

View File

@@ -369,7 +369,7 @@ void Command::invoke(int i, TriggerSource trigger)
{
CommandTrigger cmdTrigger(_trigger,trigger);
if (displayText.empty()) {
displayText = getMenuText();
displayText = getMenuText() ? getMenuText() : "";
boost::replace_all(displayText,"&","");
if (displayText.empty())
displayText = getName();

View File

@@ -15,116 +15,107 @@
</Documentation>
<Methode Name="get" Static='true'>
<Documentation>
<UserDocu>Get a given command by name or None if it doesn't exist.
get(string) -> Command
</UserDocu>
<UserDocu>get(name) -> Gui.Command or None\n
Get a given command by name or None if it doesn't exist.\n
name : str\n Command name.</UserDocu>
</Documentation>
</Methode>
<Methode Name="update" Static='true'>
<Documentation>
<UserDocu>Update active status of all commands.
update() -> None
</UserDocu>
<UserDocu>update() -> None\n
Update active status of all commands.</UserDocu>
</Documentation>
</Methode>
<Methode Name="listAll" Static='true'>
<Documentation>
<UserDocu>Returns the name of all commands.
listAll() -> list of strings
</UserDocu>
<UserDocu>listAll() -> list of str\n
Returns the name of all commands.</UserDocu>
</Documentation>
</Methode>
<Methode Name="listByShortcut" Static='true'>
<Documentation>
<UserDocu>Returns a list of all commands, filtered by shortcut.
listByShortcut(string, bool bUseRegExp=False) -> list of strings
--
Shortcuts are converted to uppercase and spaces removed prior to comparison.
</UserDocu>
<UserDocu>listByShortcut(string, useRegExp=False) -> list of str\n
Returns a list of all commands, filtered by shortcut.
Shortcuts are converted to uppercase and spaces removed
prior to comparison.\n
string : str\n Shortcut to be searched.
useRegExp : bool\n Filter using regular expression.</UserDocu>
</Documentation>
</Methode>
<Methode Name="run">
<Documentation>
<UserDocu>Runs the given command.
run() -> None
</UserDocu>
<UserDocu>run(item=0) -> None\n
Runs the given command.\n
item : int\n Item to be run.</UserDocu>
</Documentation>
</Methode>
<Methode Name="isActive" Const="true">
<Documentation>
<UserDocu>Returns True if the command is active, False otherwise.
isActive() -> bool
</UserDocu>
<UserDocu>isActive() -> bool\n
Returns True if the command is active, False otherwise.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getShortcut">
<Documentation>
<UserDocu>Returns string representing shortcut key accelerator for command.
getShortcut() -> string
</UserDocu>
<UserDocu>getShortcut() -> str\n
Returns string representing shortcut key accelerator for command.</UserDocu>
</Documentation>
</Methode>
<Methode Name="setShortcut">
<Documentation>
<UserDocu>Sets shortcut for given command, returns bool True for success.
setShortcut(string) -> bool
</UserDocu>
<UserDocu>setShortcut(string) -> bool\n
Sets shortcut for given command, returns True for success.\n
string : str\n Shortcut to be set.</UserDocu>
</Documentation>
</Methode>
<Methode Name="resetShortcut">
<Documentation>
<UserDocu>Resets shortcut for given command back to the default, returns bool True for success.
resetShortcut() -> bool
</UserDocu>
<UserDocu>resetShortcut() -> bool\n
Resets shortcut for given command back to the default, returns True for success.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getInfo">
<Documentation>
<UserDocu>Return information about this command.
getInfo() -> list of strings
--
Usage: menuText, tooltipText, whatsThisText, statustipText, pixmapText, shortcutText.
</UserDocu>
<UserDocu>getInfo() -> dict\n
Return information about this command.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getAction">
<Documentation>
<UserDocu>Return the associated QAction object.
getAction() -> list of QAction</UserDocu>
<UserDocu>getAction() -> list of QAction\n
Return the associated QAction object.</UserDocu>
</Documentation>
</Methode>
<Methode Name="createCustomCommand" Static='true'>
<Methode Name="createCustomCommand" Static='true' Keyword='true'>
<Documentation>
<UserDocu>Create a custom command for a macro
createCustomCommand(macrofile, menuText, tooltipText, whatsThisText, statustipText, pixmapText, shortcutText) -> str
--
Only the macrofile argument is required, and should be the name of the macro file. All other arguments are
passed on to the command creation routines if they are provided. All arguments except the first accept None.
Returns the name of the created custom command.
</UserDocu>
<UserDocu>createCustomCommand(macroFile, menuText, toolTip, whatsThis, statusTip, pixmap, shortcut) -> str\n
Create a custom command for a macro. Returns name of the created command.\n
macroFile : str\n Macro file.
menuText : str\n Menu text. Optional.
toolTip : str\n Tool tip text. Optional.
whatsThis : str\n `What's this?` text. Optional.
statusTip : str\n Status tip text. Optional.
pixmap : str\n Pixmap name. Optional.
shortcut : str\n Shortcut key sequence. Optional.</UserDocu>
</Documentation>
</Methode>
<Methode Name="removeCustomCommand" Static='true'>
<Documentation>
<UserDocu>Remove the custom command if it exists
removeCustomCommand(name) -> bool
--
Given the name of a custom command, this removes that command. It is not an error
to remove a non-existent command, the function simply does nothing in that case.
Returns True if something was removed, or False if not.
</UserDocu>
<UserDocu>removeCustomCommand(name) -> bool\n
Remove the custom command if it exists.
Given the name of a custom command, this removes that command.
It is not an error to remove a non-existent command, the function
simply does nothing in that case.
Returns True if something was removed, or False if not.\n
name : str\n Command name.</UserDocu>
</Documentation>
</Methode>
<Methode Name="findCustomCommand" Static='true'>
<Documentation>
<UserDocu>Find the name of a custom command, given a macro name
findCustomCommand(name) -> Optional[str]
--
Given the name of a macro, return the name of the custom command for that macro, or
None if there is no command matching that macro script name.
</UserDocu>
<UserDocu>findCustomCommand(name) -> str or None\n
Given the name of a macro, return the name of the custom command for that macro
or None if there is no command matching that macro script name.\n
name : str\n Macro name.</UserDocu>
</Documentation>
</Methode>
</PythonExport>

View File

@@ -33,7 +33,7 @@
#include "Window.h"
#include "PythonWrapper.h"
// inclusion of the generated files (generated out of AreaPy.xml)
// inclusion of the generated files (generated out of CommandPy.xml)
#include "CommandPy.h"
#include "CommandPy.cpp"
@@ -86,8 +86,8 @@ PyObject* CommandPy::listAll(PyObject *args)
PyObject* CommandPy::listByShortcut(PyObject *args)
{
char* shortcut_to_find;
bool bIsRegularExp = false;
if (!PyArg_ParseTuple(args, "s|b", &shortcut_to_find, &bIsRegularExp))
PyObject* bIsRegularExp = Py_False;
if (!PyArg_ParseTuple(args, "s|O!", &shortcut_to_find, &PyBool_Type, &bIsRegularExp))
return nullptr;
std::vector <Command*> cmds = Application::Instance->commandManager().getAllCommands();
@@ -96,7 +96,7 @@ PyObject* CommandPy::listByShortcut(PyObject *args)
Action* action = c->getAction();
if (action){
QString spc = QString::fromLatin1(" ");
if(bIsRegularExp){
if(PyObject_IsTrue(bIsRegularExp)){
QRegExp re = QRegExp(QString::fromLatin1(shortcut_to_find));
re.setCaseSensitivity(Qt::CaseInsensitive);
if (!re.isValid()){
@@ -105,7 +105,7 @@ PyObject* CommandPy::listByShortcut(PyObject *args)
throw Py::RuntimeError(str.str());
}
if (re.indexIn(action->shortcut().toString().remove(spc).toUpper()) != -1){
if (re.indexIn(action->shortcut().toString().remove(spc).toUpper()) != -1) {
matches.push_back(c->getName());
}
}
@@ -261,7 +261,8 @@ PyObject* CommandPy::getInfo(PyObject *args)
Command* cmd = this->getCommandPtr();
if (cmd) {
Action* action = cmd->getAction();
PyObject* pyList = PyList_New(6);
PyObject* pyDict = PyDict_New();
const char* cmdName = cmd->getName();
const char* menuTxt = cmd->getMenuText();
const char* tooltipTxt = cmd->getToolTipText();
const char* whatsThisTxt = cmd->getWhatsThis();
@@ -271,19 +272,21 @@ PyObject* CommandPy::getInfo(PyObject *args)
if (action)
shortcutTxt = action->shortcut().toString().toStdString();
PyObject* strCmdName = PyUnicode_FromString(cmdName);
PyObject* strMenuTxt = PyUnicode_FromString(menuTxt ? menuTxt : "");
PyObject* strTooltipTxt = PyUnicode_FromString(tooltipTxt ? tooltipTxt : "");
PyObject* strWhatsThisTxt = PyUnicode_FromString(whatsThisTxt ? whatsThisTxt : "");
PyObject* strStatustipTxt = PyUnicode_FromString(statustipTxt ? statustipTxt : "");
PyObject* strPixMapTxt = PyUnicode_FromString(pixMapTxt ? pixMapTxt : "");
PyObject* strShortcutTxt = PyUnicode_FromString(!shortcutTxt.empty() ? shortcutTxt.c_str() : "");
PyList_SetItem(pyList, 0, strMenuTxt);
PyList_SetItem(pyList, 1, strTooltipTxt);
PyList_SetItem(pyList, 2, strWhatsThisTxt);
PyList_SetItem(pyList, 3, strStatustipTxt);
PyList_SetItem(pyList, 4, strPixMapTxt);
PyList_SetItem(pyList, 5, strShortcutTxt);
return pyList;
PyDict_SetItemString(pyDict, "name", strCmdName);
PyDict_SetItemString(pyDict, "menuText", strMenuTxt);
PyDict_SetItemString(pyDict, "toolTip", strTooltipTxt);
PyDict_SetItemString(pyDict, "whatsThis", strWhatsThisTxt);
PyDict_SetItemString(pyDict, "statusTip", strStatustipTxt);
PyDict_SetItemString(pyDict, "pixmap", strPixMapTxt);
PyDict_SetItemString(pyDict, "shortcut", strShortcutTxt);
return pyDict;
}
else {
PyErr_Format(Base::PyExc_FC_GeneralError, "No such command");
@@ -322,16 +325,18 @@ PyObject* CommandPy::getAction(PyObject *args)
}
PyObject* CommandPy::createCustomCommand(PyObject* args)
PyObject* CommandPy::createCustomCommand(PyObject* args, PyObject* kw)
{
const char* macroFile = nullptr;
const char* macroFile;
const char* menuTxt = nullptr;
const char* tooltipTxt = nullptr;
const char* whatsthisTxt = nullptr;
const char* statustipTxt = nullptr;
const char* pixmapTxt = nullptr;
const char* shortcutTxt = nullptr;
if (!PyArg_ParseTuple(args, "s|zzzzzz", &macroFile, &menuTxt, &tooltipTxt, &whatsthisTxt, &statustipTxt, &pixmapTxt, &shortcutTxt))
static char* kwlist[] = {"macroFile", "menuText", "toolTip", "whatsThis","statusTip", "pixmap", "shortcut", nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|zzzzzz", kwlist, &macroFile, &menuTxt,
&tooltipTxt, &whatsthisTxt, &statustipTxt, &pixmapTxt, &shortcutTxt))
return nullptr;
auto name = Application::Instance->commandManager().newMacroName();
@@ -400,13 +405,10 @@ PyObject* CommandPy::findCustomCommand(PyObject* args)
return false;
});
if (action != macros.end()) {
if (action != macros.end())
return PyUnicode_FromString((*action)->getName());
}
else {
Py_INCREF(Py_None);
return Py_None;
}
else
Py_Return;
}
PyObject *CommandPy::getCustomAttributes(const char* /*attr*/) const

View File

@@ -719,7 +719,7 @@ Py::Object AbstractSplitViewPy::getViewer(const Py::Tuple& args)
}
}
Py::Object AbstractSplitViewPy::sequence_item(ssize_t viewIndex)
Py::Object AbstractSplitViewPy::sequence_item(Py_ssize_t viewIndex)
{
AbstractSplitView* view = getSplitViewPtr();
if (viewIndex >= view->getSize() || viewIndex < 0)

View File

@@ -98,7 +98,7 @@ public:
Py::Object viewTop(const Py::Tuple&);
Py::Object viewIsometric(const Py::Tuple&);
Py::Object getViewer(const Py::Tuple&);
Py::Object sequence_item(ssize_t);
Py::Object sequence_item(Py_ssize_t);
Py::Object close(const Py::Tuple&);
int sequence_length();

View File

@@ -96,7 +96,11 @@ def makeWindow(baseobj=None,width=None,height=None,parts=None,name=None):
else:
if baseobj:
if baseobj.getLinkedObject().isDerivedFrom("Part::Part2DObject"):
# create default component
if baseobj.Shape.Wires:
tp = "Frame"
if len(baseobj.Shape.Wires) == 1:
tp = "Solid panel"
i = 0
ws = ''
for w in baseobj.Shape.Wires:
@@ -104,7 +108,7 @@ def makeWindow(baseobj=None,width=None,height=None,parts=None,name=None):
if ws: ws += ","
ws += "Wire" + str(i)
i += 1
obj.WindowParts = ["Default","Frame",ws,"1","0"]
obj.WindowParts = ["Default",tp,ws,"1","0"]
else:
# bind properties from base obj if existing
for prop in ["Height","Width","Subvolume","Tag","Description","Material"]:

View File

@@ -1591,14 +1591,15 @@ class svgHandler(xml.sax.ContentHandler):
cy = 0
angle = argsplit[0]
if len(argsplit) >= 3:
# Rotate around a non-origin centerpoint (note: SVG y axis is opposite FreeCAD y axis)
cx = argsplit[1]
cy = argsplit[2]
m.move(Vector(cx, -cy, 0))
m.move(Vector(-cx, cy, 0)) # Reposition for rotation
# Mirroring one axis is equal to changing the direction
# of rotation
m.rotateZ(math.radians(-angle))
if len(argsplit) >= 3:
m.move(Vector(-cx, cy, 0))
m.move(Vector(cx, -cy, 0)) # Reverse repositioning
elif transformation == 'skewX':
_m = FreeCAD.Matrix(1,
-math.tan(math.radians(argsplit[0])))

View File

@@ -31,6 +31,14 @@ $CMLSTYLE
2
STANDARD
9
$LUNITS
70
2
9
$INSUNITS
70
4
9
$PEXTMAX
10
50

View File

@@ -134,7 +134,7 @@ void ViewProviderSketch::ParameterObserver::updateGridSize(const std::string & s
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
Client.GridSize.setValue(Base::Quantity::parse(QString::fromLatin1(hGrp->GetGroup("GridSize")->GetASCII("Hist0", "10.0").c_str())).getValue());
Client.GridSize.setValue(Base::Quantity::parse(QString::fromLatin1(hGrp->GetGroup("GridSize")->GetASCII("GridSize", "10.0").c_str())).getValue());
}
void ViewProviderSketch::ParameterObserver::updateEscapeKeyBehaviour(const std::string & string, App::Property * property)

View File

@@ -306,6 +306,17 @@ class DocumentBasicCases(unittest.TestCase):
self.assertEqual(obj.getSubObject(("XY_Plane", "YZ_Plane"), retType=4)[0], obj.getSubObject("XY_Plane", retType=4))
self.assertEqual(obj.getSubObject(("XY_Plane", "YZ_Plane"), retType=4)[1], obj.getSubObject("YZ_Plane", retType=4))
# Create a second origin object
obj2 = self.Doc.addObject("App::Origin", "Origin2")
self.Doc.recompute()
# Use the names of the origin's out-list
for i in obj2.OutList:
self.assertEqual(obj2.getSubObject(i.Name, retType=1).Name, i.Name)
# Add a '.' to the names
for i in obj2.OutList:
self.assertEqual(obj2.getSubObject(i.Name + '.', retType=1).Name, i.Name)
def testExtensions(self):
#we try to create a normal python object and add an extension to it
obj = self.Doc.addObject("App::DocumentObject", "Extension_1")

View File

@@ -306,7 +306,7 @@ void UnitTestDialog::setProgressFraction(float fraction, const QString& color)
}
/**
* Emtpies the error listview.
* Empties the error listview.
*/
void UnitTestDialog::clearErrorList()
{

View File

@@ -24,6 +24,7 @@
# Workbench test module
import FreeCAD, FreeCADGui, os, unittest
import tempfile
from PySide2 import QtWidgets, QtCore
from PySide2.QtWidgets import QApplication
@@ -79,3 +80,16 @@ class WorkbenchTestCase(unittest.TestCase):
def tearDown(self):
FreeCADGui.activateWorkbench(self.Active.name())
FreeCAD.Console.PrintLog(self.Active.name())
class CommandTestCase(unittest.TestCase):
def testPR6889(self):
# Fixes a crash
TempPath = tempfile.gettempdir()
macroName = TempPath + os.sep + "testmacro.py"
macroFile = open(macroName, "w")
macroFile.write("print ('Hello, World!')")
macroFile.close()
name = FreeCADGui.Command.createCustomCommand(macroName)
cmd = FreeCADGui.Command.get(name)
cmd.run()