App.ActiveContainer

Discussion about the development of the Assembly workbench.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
peterl94
Veteran
Posts: 1001
Joined: Thu May 23, 2013 7:31 pm
Location: United States

Re: App.ActiveContainer

Post by peterl94 »

First impressions:
1. Part Design, create Part, create Sketch now creates new body inside the Part. I think that is a good improvement.
2. All geometry objects are added to the active Part. I think this is also good.
3. I don't like that you have to a deactivate a body before being able to create another one. I think it should allow adding new body when a body is active and have it added to the parent of the active body.
3. I was surprised that all 2d geometry is added to the active body.
3. I was also surprised that even non-geometry objects are added to the active Part such as Spreadsheet and TechDraw Page.

Also found one error. After creating a Part, create additive box, then make the Part active and switch to Path and create a Job from the Body.

Code: Select all

Traceback (most recent call last):
  File "C:\Users\peter\dev\FreeCAD\build-kcleung\Mod\Path\PathScripts\PathToolController.py", line 139, in onChanged
    job = PathScripts.PathUtils.findParentJob(obj)
  File "C:\Users\peter\dev\FreeCAD\build-kcleung\Mod\Path\PathScripts\PathUtils.py", line 416, in findParentJob
    if isinstance(i.Proxy, PathScripts.PathJob.ObjectPathJob):
<type 'exceptions.AttributeError'>: 'App.Part' object has no attribute 'Proxy'
You will also notice that the tree view doesn't correctly display the Parts children after that, but this happens in master too.

In general, I like it so far. You just have to pay more attention to what container is active to avoid accidentally adding things to the wrong container. Not a problem for my workflow because I almost exclusively use Part Design.

I haven't decided yet whether I like abstracting away active Parts and Bodies to just one active "container." More on this later...
kcleung
Posts: 162
Joined: Sun Apr 24, 2011 11:56 am

Re: App.ActiveContainer

Post by kcleung »

peterl94 wrote: Sat Jul 15, 2017 2:19 am First impressions:
1. Part Design, create Part, create Sketch now creates new body inside the Part. I think that is a good improvement.
2. All geometry objects are added to the active Part. I think this is also good.
3. I don't like that you have to a deactivate a body before being able to create another one. I think it should allow adding new body when a body is active and have it added to the parent of the active body.
3. I was surprised that all 2d geometry is added to the active body.
3. I was also surprised that even non-geometry objects are added to the active Part such as Spreadsheet and TechDraw Page.

Also found one error. After creating a Part, create additive box, then make the Part active and switch to Path and create a Job from the Body.

Code: Select all

Traceback (most recent call last):
  File "C:\Users\peter\dev\FreeCAD\build-kcleung\Mod\Path\PathScripts\PathToolController.py", line 139, in onChanged
    job = PathScripts.PathUtils.findParentJob(obj)
  File "C:\Users\peter\dev\FreeCAD\build-kcleung\Mod\Path\PathScripts\PathUtils.py", line 416, in findParentJob
    if isinstance(i.Proxy, PathScripts.PathJob.ObjectPathJob):
<type 'exceptions.AttributeError'>: 'App.Part' object has no attribute 'Proxy'
You will also notice that the tree view doesn't correctly display the Parts children after that, but this happens in master too.

In general, I like it so far. You just have to pay more attention to what container is active to avoid accidentally adding things to the wrong container. Not a problem for my workflow because I almost exclusively use Part Design.

I haven't decided yet whether I like abstracting away active Parts and Bodies to just one active "container." More on this later...
I agree with (3), that it would be more intuitive that when a body is added, it is added to the parent of the active body.

I believe the behaviour is that if you create a new object with src/App Document::addObject(), then the object is added as a child of the current active container if that object can be accepted by the current active container, otherwise it is added to either the parent of the current active container, or directly to the document.

My commit:

https://github.com/kcleung/FreeCAD/comm ... 498149e22b

uses the latter approach, i.e. in add directly to the document if the new object is not accepted by the current active container as a child. If this causes things to be accidentally added to the wrong place, I can change the fallback behaviour to add to the parent of the active container instead.

Do you think this is the cause of things being added to the wrong place?



with regards to:
peterl94 wrote: Sat Jul 15, 2017 2:19 am Also found one error. After creating a Part, create additive box, then make the Part active and switch to Path and create a Job from the Body.

Code: Select all

Traceback (most recent call last):
  File "C:\Users\peter\dev\FreeCAD\build-kcleung\Mod\Path\PathScripts\PathToolController.py", line 139, in onChanged
    job = PathScripts.PathUtils.findParentJob(obj)
  File "C:\Users\peter\dev\FreeCAD\build-kcleung\Mod\Path\PathScripts\PathUtils.py", line 416, in findParentJob
    if isinstance(i.Proxy, PathScripts.PathJob.ObjectPathJob):
<type 'exceptions.AttributeError'>: 'App.Part' object has no attribute 'Proxy'
You will also notice that the tree view doesn't correctly display the Parts children after that, but this happens in master too.
I am trying to reproduce this bug.

When you said "after creating a Part", do you mean a PartDesign Part, or an object such as a cube, that is created in the path? When you say "additive box", do you mean a cube created in Part workbench, or a sketch that is padded in PartDesign? I am confused.
peterl94
Veteran
Posts: 1001
Joined: Thu May 23, 2013 7:31 pm
Location: United States

Re: App.ActiveContainer

Post by peterl94 »

kcleung wrote: Sat Jul 15, 2017 9:19 am add directly to the document if the new object is not accepted by the current active container as a child
Makes sense, but it doesn't seem to work for Body? For example, create a new document, click on part design "new body" command. Now the "new body" command is disabled and can't be clicked until you deactivate the previous body.
kcleung wrote: Sat Jul 15, 2017 9:19 am When you said "after creating a Part", do you mean a PartDesign Part
Yes, I mean an App.Part. Since I was in Part Design to create the App.Part I used a Part Design primitive, but the kind of geometry doesn't matter. The object just needs to bee in a App.Part. Here is more detailed instructions:
1. Switch to Part Design and create an App.Part
2. Add some geometry to the Part. Doesn't matter how. Could be just a Cube from Part workbench.
3. Switch to the Path workbench, make the App.Part active if it is not already, and then create a Job object (base model should be set to the object you created in step 2).
kcleung
Posts: 162
Joined: Sun Apr 24, 2011 11:56 am

Re: App.ActiveContainer

Post by kcleung »

peterl94 wrote: Sat Jul 15, 2017 11:08 am
kcleung wrote: Sat Jul 15, 2017 9:19 am add directly to the document if the new object is not accepted by the current active container as a child
Makes sense, but it doesn't seem to work for Body? For example, create a new document, click on part design "new body" command. Now the "new body" command is disabled and can't be clicked until you deactivate the previous body.
Sorry for the late reply. I was busy in a project at work, but I have just rebased the ActiveContainer branch and pushed. It is supposed to work, though I am also puzzled how come when you have just created a body, it does not let you create another one under the parent of the body, no matter whether the body is in the part folder, or the body is under the root.



I am going to look at this issue, and the next issue very soon.


kcleung wrote: Sat Jul 15, 2017 9:19 am When you said "after creating a Part", do you mean a PartDesign Part
Yes, I mean an App.Part. Since I was in Part Design to create the App.Part I used a Part Design primitive, but the kind of geometry doesn't matter. The object just needs to bee in a App.Part. Here is more detailed instructions:
1. Switch to Part Design and create an App.Part
2. Add some geometry to the Part. Doesn't matter how. Could be just a Cube from Part workbench.
3. Switch to the Path workbench, make the App.Part active if it is not already, and then create a Job object (base model should be set to the object you created in step 2).
[/quote]
kcleung
Posts: 162
Joined: Sun Apr 24, 2011 11:56 am

Re: App.ActiveContainer

Post by kcleung »

DeepSOIC: Can you see the reason that our activecontainer still fails to go to the parent of the current container and let us create the new body?

Also, I have just tried to rebase my ActiveContainer branch to the current master, but the rebase conflicts when the system applies my commit "PartDesign: port to use ActiveContainer" in Utils.cpp

Code: Select all

PartDesign::Body *getBody(bool messageIfNot)
{
<<<<<<< d36161725348f7998d8f9fb36ced0a4538a90e87
    PartDesign::Body * activeBody = nullptr;
    Gui::MDIView *activeView = Gui::Application::Instance->activeView();
    bool singleBodyDocument = activeView->getAppDocument()->
        countObjectsOfType(PartDesign::Body::getClassTypeId()) == 1;

    if (activeView) {
        if ( PartDesignGui::assureModernWorkflow ( activeView->getAppDocument() ) ) {
            activeBody = activeView->getActiveObject<PartDesign::Body*>(PDBODYKEY);

            if (!activeBody && singleBodyDocument) {
                Gui::Command::doCommand( Gui::Command::Gui,
                    "Gui.activeView().setActiveObject('pdbody',App.ActiveDocument.findObjects('PartDesign::Body')[0])");
                activeBody = activeView->getActiveObject<PartDesign::Body*>(PDBODYKEY);
                return activeBody;
            }
            if (!activeBody && messageIfNot) {
                QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No active Body"),
                    QObject::tr("In order to use PartDesign you need an active Body object in the document. "
                                "Please make one active (double click) or create one.\n\nIf you have a legacy document "
                                "with PartDesign objects without Body, use the transfer function in "
                                "PartDesign to put them into a Body."
                                ));
            }
=======
    PartDesign::Body* activeBody = PartDesign::Body::activeBody();

    if ( PartDesignGui::assureModernWorkflow ( App::GetApplication().getActiveDocument() ) ) {
        if (!activeBody && messageIfNot) {
            QMessageBox::warning(Gui::getMainWindow(), QObject::tr("No active Body"),
                QObject::tr("In order to use PartDesign you need an active Body object in the document. "
                            "Please make one active (double click) or create one. If you have a legacy document "
                            "with PartDesign objects without Body, use the transfer function in "
                            "PartDesign to put them into a Body."
                            ));
>>>>>>> PartDesign: port to use ActiveContainer
        }
    }

    return activeBody;
}

and CommandBody.cpp:.

Code: Select all

void CmdPartDesignBody::activated(int iMsg)
{
    Q_UNUSED(iMsg);
    if ( !PartDesignGui::assureModernWorkflow( getDocument() ) )
        return;
    App::Part *actPart = PartDesignGui::getActivePart ();
    App::Part* partOfBaseFeature = nullptr;

    std::vector<App::DocumentObject*> features =
        getSelection().getObjectsOfType(Part::Feature::getClassTypeId());
    App::DocumentObject* baseFeature = nullptr;
    bool viewAll = features.empty();


    if (!features.empty()) {
        if (features.size() == 1) {
            baseFeature = features[0];
            if ( baseFeature->isDerivedFrom ( PartDesign::Feature::getClassTypeId() ) &&
                    PartDesign::Body::findBodyOf ( baseFeature ) ) {
                // Prevent creating bodies based on features already belonging to other bodies
                QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Bad base feature"),
                        QObject::tr("Body can't be based on a PartDesign feature."));
                baseFeature = nullptr;
            }
            else if (PartDesign::Body::findBodyOf ( baseFeature )){
                QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Bad base feature"),
                        QObject::tr("%1 already belongs to a body, can't use it as base feature for another body.")
                                     .arg(QString::fromUtf8(baseFeature->Label.getValue())));
                baseFeature = nullptr;
            }
            else if ( baseFeature->isDerivedFrom ( Part::BodyBase::getClassTypeId() ) )  {
                // Prevent creating bodies based on bodies
                QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Bad base feature"),
                        QObject::tr("Body can't be based on another body."));
                baseFeature = nullptr;
            }
            else {
                partOfBaseFeature = App::Part::getPartOfObject(baseFeature);
                if (partOfBaseFeature != 0  &&  partOfBaseFeature != actPart){
                    //prevent cross-part mess
                    QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Bad base feature"),
                            QObject::tr("Base feature (%1) belongs to other part.")
                                         .arg(QString::fromUtf8(baseFeature->Label.getValue())));
                    baseFeature = nullptr;
                };
            }

        } else {
            QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Bad base feature"),
                QObject::tr("Body may be based no more than on one feature."));
            return;
        }
    }


    openCommand("Add a Body");

    std::string bodyName = getUniqueObjectName("Body");

    // add the Body feature itself, and make it active
    doCommand(Doc,"App.activeDocument().addObject('PartDesign::Body','%s')", bodyName.c_str());
    if (baseFeature) {
        if (partOfBaseFeature){
            //withdraw base feature from Part, otherwise visibility mandess results
            doCommand(Doc,"App.activeDocument().%s.removeObject(App.activeDocument().%s)",
                    partOfBaseFeature->getNameInDocument(), baseFeature->getNameInDocument());
        }
        doCommand(Doc,"App.activeDocument().%s.BaseFeature = App.activeDocument().%s",
                bodyName.c_str(), baseFeature->getNameInDocument());
    }
    addModule(Gui,"PartDesignGui"); // import the Gui module only once a session
<<<<<<< d36161725348f7998d8f9fb36ced0a4538a90e87
    doCommand(Gui::Command::Gui, "Gui.activeView().setActiveObject('%s', App.activeDocument().%s)",
            PDBODYKEY, bodyName.c_str());
=======
    doCommand(Gui::Command::Gui, "App.ActiveDocument.ActiveContainer = '%s'",
            bodyName.c_str());
>>>>>>> PartDesign: port to use ActiveContainer

    // Make the "Create sketch" prompt appear in the task panel
    doCommand(Gui,"Gui.Selection.clearSelection()");
    doCommand(Gui,"Gui.Selection.addSelection(App.ActiveDocument.%s)", bodyName.c_str());

    // The method 'SoCamera::viewBoundingBox' is still declared as protected in Coin3d versions
    // older than 4.0.
#if COIN_MAJOR_VERSION >= 4
    // if no part feature was there then auto-adjust the camera
    if (viewAll) {
        Gui::Document* doc = Gui::Application::Instance->getDocument(getDocument());
        Gui::View3DInventor* view = doc ? qobject_cast<Gui::View3DInventor*>(doc->getActiveView()) : nullptr;
        if (view) {
            SoCamera* camera = view->getViewer()->getCamera();
            SbViewportRegion vpregion = view->getViewer()->getViewportRegion();
            float aspectratio = vpregion.getViewportAspectRatio();

            float size = Gui::ViewProviderOrigin::defaultSize();
            SbBox3f bbox;
            bbox.setBounds(-size,-size,-size,size,size,size);
            camera->viewBoundingBox(bbox, aspectratio, 1.0f);
        }
    }
#endif

    updateActive();
}


Apparently, the conflict is due to the introduction of the ActiveView in the master branch, and we somehow need to tie ActiveContainer to the ActiveVew.

Last time, you said you will be back in August, and it is already close to September, are you ready to have a look?
User avatar
DeepSOIC
Veteran
Posts: 7896
Joined: Fri Aug 29, 2014 12:45 am
Location: used to be Saint-Petersburg, Russia

Re: App.ActiveContainer

Post by DeepSOIC »

Sorry, I'm very unlikely to return to this branch :(
If anything, I may do some small contributions. I'm not ready for anything serious.
kcleung
Posts: 162
Joined: Sun Apr 24, 2011 11:56 am

Re: App.ActiveContainer

Post by kcleung »

DeepSOIC: It is all right, but could you please read my above post about the interaction between activecontainer and activeview?

I need to sort this out to rebase our activecontainer branch onto the current trunk.
User avatar
industromatic
Posts: 150
Joined: Thu Mar 27, 2014 4:30 pm
Location: Austin Texas
Contact:

Re: App.ActiveContainer

Post by industromatic »

by DeepSOIC » Mon May 15, 2017 1:38 pm It doesn't have to deactivate a container to add an object to a parent container. It can do App.ActiveContainer.Parent.newObject(...).

wmayer wrote:
You are focusing on part design and assembly. But what when wanting to create technical drawings, create an FEM analysis, creating a raytracing image, working with expressions in the spreadsheet workbench and so on? These are all examples where objects don't go to a body object and as ickby said it's pretty annoying to manually activate or deactivate a container each time when you switch.

I am introducing a general way of dealing with active container. Isn't it natural that all local own container management subsystems come into conflict with it? and have to be ported to work properly?
by DeepSOIC » Sat Aug 26, 2017 4:29 pm
Sorry, I'm very unlikely to return to this branch :(
I have been trying part-o-magic, but I have questions on how to resolve errors that come when I try to cut and paste anything
Probably I cannot use old designs and the part-o-magic addon that way.

What is the way for a fairly inexperienced with freecad, but experienced CAD user who wants to be able to reuse pieces of molds with draft angles and have them be spreadsheet parameterized or generated by python? the GUI tree is not giving me much I understand since cut and paste fails with errors-- I cannot rearrange the tree to make sense to me. I get errors like this when trying to copy and paste and delete the original:

Code: Select all

PartDesign::AdditiveCylinder: Links go out of the allowed scope
Sketcher::SketchObject: Links go out of the allowed scope
PartDesign::Plane: Links go out of the allowed scope
Sketcher::SketchObject: Links go out of the allowed scope
Should I be learning to use python next or study some of the new docs for part design workbench? I've been reading for days and not much progress.
User avatar
DeepSOIC
Veteran
Posts: 7896
Joined: Fri Aug 29, 2014 12:45 am
Location: used to be Saint-Petersburg, Russia

Re: App.ActiveContainer

Post by DeepSOIC »

industromatic wrote: Wed Oct 10, 2018 9:43 pm but I have questions on how to resolve errors that come when I try to cut and paste anything
Feel free to ask them, but please, not in this thread.

Make sure to ping me with a quote, otherwise I can overlook your questions.
use this:

Code: Select all

[quote="DeepSOIC"]ping[/quote]
Jee-Bee
Veteran
Posts: 2566
Joined: Tue Jun 16, 2015 10:32 am
Location: Netherlands

Re: App.ActiveContainer

Post by Jee-Bee »

yuo have to add the user id to... (also inside the rackets and a space in between. for DeepSOIC it is

Code: Select all

[quote="DeepSOIC" user_id=3888]ping[/quote]
Post Reply