[Gui] Add command and task for unified measurement facility
This commit is contained in:
@@ -455,6 +455,7 @@ SET(Dialog_CPP_SRCS
|
||||
DownloadManager.cpp
|
||||
DocumentRecovery.cpp
|
||||
TaskElementColors.cpp
|
||||
TaskMeasure.cpp
|
||||
DlgObjectSelection.cpp
|
||||
DlgAddProperty.cpp
|
||||
VectorListEditor.cpp
|
||||
@@ -494,6 +495,7 @@ SET(Dialog_HPP_SRCS
|
||||
DownloadManager.h
|
||||
DocumentRecovery.h
|
||||
TaskElementColors.h
|
||||
TaskMeasure.h
|
||||
DlgObjectSelection.h
|
||||
DlgAddProperty.h
|
||||
VectorListEditor.h
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
#include "SelectionObject.h"
|
||||
#include "SoAxisCrossKit.h"
|
||||
#include "SoFCOffscreenRenderer.h"
|
||||
#include "TaskMeasure.h"
|
||||
#include "TextureMapping.h"
|
||||
#include "Tools.h"
|
||||
#include "Tree.h"
|
||||
@@ -3189,6 +3190,38 @@ bool StdCmdMeasureDistance::isActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Std_Measure
|
||||
// this is the Unified Measurement Facility Measure command
|
||||
//===========================================================================
|
||||
|
||||
|
||||
DEF_STD_CMD_A(StdCmdMeasure)
|
||||
|
||||
StdCmdMeasure::StdCmdMeasure()
|
||||
:Command("Std_Measure")
|
||||
{
|
||||
sGroup = "Measure";
|
||||
sMenuText = QT_TR_NOOP("&Measure");
|
||||
sToolTipText = QT_TR_NOOP("Measure a feature");
|
||||
sWhatsThis = "Std_Measure";
|
||||
sStatusTip = QT_TR_NOOP("Measure a feature");
|
||||
sPixmap = "umf-measurement";
|
||||
}
|
||||
|
||||
void StdCmdMeasure::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
|
||||
TaskMeasure *task = new TaskMeasure();
|
||||
Gui::Control().showDialog(task);
|
||||
}
|
||||
|
||||
|
||||
bool StdCmdMeasure::isActive(){
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Std_SceneInspector
|
||||
//===========================================================================
|
||||
@@ -4117,6 +4150,7 @@ void CreateViewStdCommands()
|
||||
rcCmdMgr.addCommand(new StdCmdTreeCollapse());
|
||||
rcCmdMgr.addCommand(new StdCmdTreeSelectAllInstances());
|
||||
rcCmdMgr.addCommand(new StdCmdMeasureDistance());
|
||||
rcCmdMgr.addCommand(new StdCmdMeasure());
|
||||
rcCmdMgr.addCommand(new StdCmdSceneInspector());
|
||||
rcCmdMgr.addCommand(new StdCmdTextureMapping());
|
||||
rcCmdMgr.addCommand(new StdCmdDemoMode());
|
||||
|
||||
@@ -135,6 +135,7 @@
|
||||
<file>view-rotate-right.svg</file>
|
||||
<file>view-measurement.svg</file>
|
||||
<file>view-measurement-cross.svg</file>
|
||||
<file>umf-measurement.svg</file>
|
||||
<file>Tree_Annotation.svg</file>
|
||||
<file>Tree_Dimension.svg</file>
|
||||
<file>Tree_Python.svg</file>
|
||||
|
||||
317
src/Gui/Icons/umf-measurement.svg
Normal file
317
src/Gui/Icons/umf-measurement.svg
Normal file
@@ -0,0 +1,317 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg2943"
|
||||
version="1.1">
|
||||
<defs
|
||||
id="defs2945">
|
||||
<linearGradient
|
||||
id="linearGradient4158">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4160" />
|
||||
<stop
|
||||
style="stop-color:#f6f6f6;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4162" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4122">
|
||||
<stop
|
||||
style="stop-color:#e3d328;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4124" />
|
||||
<stop
|
||||
style="stop-color:#e1dec3;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4126" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4088">
|
||||
<stop
|
||||
style="stop-color:#e9cd23;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4090" />
|
||||
<stop
|
||||
style="stop-color:#040000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4092" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4060">
|
||||
<stop
|
||||
style="stop-color:#ada9a9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4062" />
|
||||
<stop
|
||||
style="stop-color:#ada9a9;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4064" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4052">
|
||||
<stop
|
||||
style="stop-color:#ada9a9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4054" />
|
||||
<stop
|
||||
style="stop-color:#ada9a9;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop4056" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4349">
|
||||
<stop
|
||||
style="stop-color:#898709;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4351" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4353" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5241">
|
||||
<stop
|
||||
style="stop-color:#212c45;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5243" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop5245" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient5227"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop5229" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3902">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.58823532;"
|
||||
offset="0"
|
||||
id="stop3904" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0.39215687;"
|
||||
offset="1"
|
||||
id="stop3906" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3894">
|
||||
<stop
|
||||
style="stop-color:#45351d;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3896" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3898" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3886">
|
||||
<stop
|
||||
style="stop-color:#45351d;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3888" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3890" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3792">
|
||||
<stop
|
||||
style="stop-color:#aaaaaa;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3794" />
|
||||
<stop
|
||||
style="stop-color:#d2d2d2;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3796" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3784">
|
||||
<stop
|
||||
style="stop-color:#bebebe;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3786" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.39215687;"
|
||||
offset="1"
|
||||
id="stop3788" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3377">
|
||||
<stop
|
||||
id="stop3379"
|
||||
offset="0"
|
||||
style="stop-color:#71b2f8;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3381"
|
||||
offset="1"
|
||||
style="stop-color:#002795;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4052"
|
||||
id="linearGradient4058"
|
||||
x1="138.99986"
|
||||
y1="44.863674"
|
||||
x2="92.497559"
|
||||
y2="-14.356517"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(168.6744,65.825928)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4060"
|
||||
id="linearGradient4066"
|
||||
x1="103.93729"
|
||||
y1="49.179436"
|
||||
x2="120.49899"
|
||||
y2="0.21229285"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(168.6744,65.825928)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4122"
|
||||
id="linearGradient4128"
|
||||
x1="391.3074"
|
||||
y1="120.81136"
|
||||
x2="394.43201"
|
||||
y2="112.43636"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-88.034794,-1.0606602)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient4158"
|
||||
id="linearGradient4164"
|
||||
x1="419.99387"
|
||||
y1="102.77802"
|
||||
x2="458.7193"
|
||||
y2="69.431564"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-129.22376,-0.88388348)" />
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3953"
|
||||
id="linearGradient3959"
|
||||
x1="214.70918"
|
||||
y1="80.886589"
|
||||
x2="218.70918"
|
||||
y2="104.88659"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(80,0)" />
|
||||
<linearGradient
|
||||
id="linearGradient3953">
|
||||
<stop
|
||||
style="stop-color:#babdb6;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3955" />
|
||||
<stop
|
||||
style="stop-color:#555753;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3957" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
xlink:href="#linearGradient3961"
|
||||
id="linearGradient3967"
|
||||
x1="196.70918"
|
||||
y1="106.88659"
|
||||
x2="190.70918"
|
||||
y2="80.886589"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(80,0)" />
|
||||
<linearGradient
|
||||
id="linearGradient3961">
|
||||
<stop
|
||||
style="stop-color:#babdb6;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3963" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3965" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata2948">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1">
|
||||
<g
|
||||
id="g3629"
|
||||
transform="translate(-256.70919,-66.886588)">
|
||||
<path
|
||||
style="fill:#e3d328;fill-opacity:1;stroke:#040400;stroke-width:0.08838835;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d=""
|
||||
id="path4102"
|
||||
transform="translate(256.70919,66.886588)" />
|
||||
<path
|
||||
style="fill:#babdb6;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 285.70919,77.886588 -26,-4 c 0,11 0,26.000002 2,36.000002 l 30,12 0,-6 -2,-4 c 8,-12 0,-24.000002 -4,-34.000002 z"
|
||||
id="path3100" />
|
||||
<path
|
||||
style="fill:#555753;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 291.70919,121.88659 12,-4 0,-6 -12,4 z"
|
||||
id="path3890" />
|
||||
<path
|
||||
style="fill:#d3d7cf;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 301.70919,107.88659 -12,4 2,4 12,-4 z"
|
||||
id="path3892" />
|
||||
<path
|
||||
style="fill:#888a85;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 285.70919,77.886588 c 4,10 12,22.000002 4,34.000002 l 12,-4 c 8,-12.000002 0,-24.000002 -4,-34.000002 z"
|
||||
id="path3894" />
|
||||
<path
|
||||
style="fill:#d3d7cf;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 259.70919,73.886588 12,-4 26,4 -12,4 z"
|
||||
id="path3888" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3967);fill-opacity:1;stroke:#d3d7cf;stroke-width:1.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 284.30919,79.786588 -22.6,-3.9 c 0,10.043478 -0.0125,23.469562 1.8,32.600002 l 26.2,10.4 0,-2.5 -2.2,-4.5 c 8,-12 1.2,-22.000002 -3.2,-32.100002 z"
|
||||
id="path3100-6" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3959);fill-opacity:1;stroke:#babdb6;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 288.30919,79.086588 c 4,10 9.4,18.3 5.4,29.300002 l 6.6,-2.2 c 6,-9.000002 1.4,-19.300002 -3.8,-29.900002 z"
|
||||
id="path3894-7" />
|
||||
<path
|
||||
style="fill:#555753;fill-opacity:1;stroke:#2e3436;stroke-width:2.25831866;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:1.6"
|
||||
id="path3932"
|
||||
d="m -78,39 a 15,15 0 1 1 -30,0 15,15 0 1 1 30,0 z"
|
||||
transform="matrix(0.79999998,0.19607832,0,0.9803916,350.10919,74.8866)" />
|
||||
<path
|
||||
style="fill:#edd400;fill-opacity:1;stroke:#302b00;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:1.6"
|
||||
d="m 287.70919,97.827762 -24,-5.882349 c 0,0 -0.82229,-14.095015 12,-11.764699 11.12322,2.021527 12,17.647048 12,17.647048 z"
|
||||
id="path3932-5" />
|
||||
<path
|
||||
style="fill:#edd400;fill-opacity:1;stroke:#302b00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 294.70919,90.886588 c 2,4 3,11.000002 1,15.000002 l 4,-1 c 2,-4 2,-11.000002 0,-15.000002 z"
|
||||
id="path3894-7-9" />
|
||||
<path
|
||||
style="fill:#edd400;stroke:#302b00;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 293.70919,117.88659 8,-3 16,5 -6,4 z"
|
||||
id="path4003" />
|
||||
<path
|
||||
style="fill:#d3d7cf;stroke:#2e3436;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
|
||||
d="m 317.70919,119.88659 0,4 -6,4 0,-4 z"
|
||||
id="path4005" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
388
src/Gui/TaskMeasure.cpp
Normal file
388
src/Gui/TaskMeasure.cpp
Normal file
@@ -0,0 +1,388 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <QApplication>
|
||||
# include <QKeyEvent>
|
||||
#endif
|
||||
|
||||
|
||||
#include "TaskMeasure.h"
|
||||
|
||||
#include "Control.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Application.h"
|
||||
#include "App/Document.h"
|
||||
#include "App/DocumentObjectGroup.h"
|
||||
#include <Gui/BitmapFactory.h>
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QPushButton>
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
|
||||
TaskMeasure::TaskMeasure()
|
||||
{
|
||||
qApp->installEventFilter(this);
|
||||
|
||||
this->setButtonPosition(TaskMeasure::South);
|
||||
auto taskbox = new Gui::TaskView::TaskBox(Gui::BitmapFactory().pixmap("umf-measurement"), tr("Measurement"), true, nullptr);
|
||||
|
||||
// Create mode dropdown and add all registered measuretypes
|
||||
modeSwitch = new QComboBox();
|
||||
modeSwitch->addItem(QString::fromLatin1("Auto"));
|
||||
|
||||
for (App::MeasureType* mType : App::MeasureManager::getMeasureTypes()){
|
||||
modeSwitch->addItem(QString::fromLatin1(mType->label.c_str()));
|
||||
}
|
||||
|
||||
// Connect dropdown's change signal to our onModeChange slot
|
||||
connect(modeSwitch, qOverload<int>(&QComboBox::currentIndexChanged), this, &TaskMeasure::onModeChanged);
|
||||
|
||||
// Result widget
|
||||
valueResult = new QLineEdit();
|
||||
valueResult->setReadOnly(true);
|
||||
|
||||
// Main layout
|
||||
QBoxLayout *layout = taskbox->groupLayout();
|
||||
|
||||
QFormLayout* formLayout = new QFormLayout();
|
||||
formLayout->setHorizontalSpacing(10);
|
||||
// Note: How can the split between columns be kept in the middle?
|
||||
// formLayout->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy::ExpandingFieldsGrow);
|
||||
formLayout->setFormAlignment(Qt::AlignCenter);
|
||||
|
||||
formLayout->addRow(QString::fromLatin1("Mode:"), modeSwitch);
|
||||
formLayout->addRow(QString::fromLatin1("Result:"), valueResult);
|
||||
layout->addLayout(formLayout);
|
||||
|
||||
Content.emplace_back(taskbox);
|
||||
|
||||
// engage the selectionObserver
|
||||
attachSelection();
|
||||
|
||||
// Set selection style
|
||||
Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::GreedySelection);
|
||||
|
||||
if(!App::GetApplication().getActiveTransaction())
|
||||
App::GetApplication().setActiveTransaction("Add Measurement");
|
||||
|
||||
|
||||
// Call invoke method delayed, otherwise the dialog might not be fully initialized
|
||||
QTimer::singleShot(0, this, &TaskMeasure::invoke);
|
||||
}
|
||||
|
||||
TaskMeasure::~TaskMeasure(){
|
||||
Gui::Selection().setSelectionStyle(Gui::SelectionSingleton::SelectionStyle::NormalSelection);
|
||||
detachSelection();
|
||||
qApp->removeEventFilter(this);
|
||||
}
|
||||
|
||||
|
||||
void TaskMeasure::modifyStandardButtons(QDialogButtonBox* box) {
|
||||
|
||||
QPushButton* btn = box->button(QDialogButtonBox::Apply);
|
||||
btn->setText(tr("Annotate"));
|
||||
btn->setToolTip(tr("Press the Annotate button to add measurement to the document."));
|
||||
connect(btn, &QPushButton::released, this, &TaskMeasure::apply);
|
||||
|
||||
// Disable button by default
|
||||
btn->setEnabled(false);
|
||||
btn = box->button(QDialogButtonBox::Abort);
|
||||
btn->setText(QString::fromLatin1("Close"));
|
||||
btn->setToolTip(tr("Press the Close button to exit."));
|
||||
|
||||
// Connect reset button
|
||||
btn = box->button(QDialogButtonBox::Reset);
|
||||
connect(btn, &QPushButton::released, this, &TaskMeasure::reset);
|
||||
}
|
||||
|
||||
bool canAnnotate(Measure::MeasureBase* obj) {
|
||||
if (obj == nullptr) {
|
||||
// null object, can't annotate this
|
||||
return false;
|
||||
}
|
||||
|
||||
auto vpName = obj->getViewProviderName();
|
||||
// if there is not a vp, return false
|
||||
if ((vpName == nullptr) || (vpName[0] == '\0')){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TaskMeasure::enableAnnotateButton(bool state) {
|
||||
// if the task ui is not init yet we don't have a button box.
|
||||
if (!this->buttonBox) {
|
||||
return;
|
||||
}
|
||||
// Enable/Disable annotate button
|
||||
auto btn = this->buttonBox->button(QDialogButtonBox::Apply);
|
||||
btn->setEnabled(state);
|
||||
}
|
||||
|
||||
void TaskMeasure::setMeasureObject(Measure::MeasureBase* obj) {
|
||||
_mMeasureObject = obj;
|
||||
}
|
||||
|
||||
|
||||
void TaskMeasure::update() {
|
||||
|
||||
// Reset selection if the selected object is not valid
|
||||
for(auto sel : Gui::Selection().getSelection()) {
|
||||
App::DocumentObject* ob = sel.pObject;
|
||||
App::DocumentObject* sub = ob->getSubObject(sel.SubName);
|
||||
std::string mod = Base::Type::getModuleName(sub->getTypeId().getName());
|
||||
|
||||
if (!App::MeasureManager::hasMeasureHandler(mod.c_str())) {
|
||||
Base::Console().Message("No measure handler available for geometry of module: %s\n", mod);
|
||||
clearSelection();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
valueResult->setText(QString::asprintf("-"));
|
||||
|
||||
// Get valid measure type
|
||||
App::MeasureType *measureType(nullptr);
|
||||
|
||||
|
||||
std::string mode = explicitMode ? modeSwitch->currentText().toStdString() : "";
|
||||
|
||||
App::MeasureSelection selection;
|
||||
for (auto s : Gui::Selection().getSelection()) {
|
||||
App::SubObjectT sub(s.pObject, s.SubName);
|
||||
|
||||
App::MeasureSelectionItem item = { sub, Base::Vector3d(s.x, s.y, s.z) };
|
||||
selection.push_back(item);
|
||||
}
|
||||
|
||||
auto measureTypes = App::MeasureManager::getValidMeasureTypes(selection, mode);
|
||||
if (measureTypes.size() > 0) {
|
||||
measureType = measureTypes.front();
|
||||
}
|
||||
|
||||
|
||||
if (!measureType) {
|
||||
|
||||
// Note: If there's no valid measure type we might just restart the selection,
|
||||
// however this requires enough coverage of measuretypes that we can access all of them
|
||||
|
||||
// std::tuple<std::string, std::string> sel = selection.back();
|
||||
// clearSelection();
|
||||
// addElement(measureModule.c_str(), get<0>(sel).c_str(), get<1>(sel).c_str());
|
||||
|
||||
// Reset measure object
|
||||
if (!explicitMode) {
|
||||
setModeSilent(nullptr);
|
||||
}
|
||||
removeObject();
|
||||
enableAnnotateButton(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update tool mode display
|
||||
setModeSilent(measureType);
|
||||
|
||||
if (!_mMeasureObject || measureType->measureObject != _mMeasureObject->getTypeId().getName()) {
|
||||
// we don't already have a measureobject or it isn't the same type as the new one
|
||||
removeObject();
|
||||
|
||||
App::Document *doc = App::GetApplication().getActiveDocument();
|
||||
if (measureType->isPython) {
|
||||
Base::PyGILStateLocker lock;
|
||||
auto pyMeasureClass = measureType->pythonClass;
|
||||
|
||||
// Create a MeasurePython instance
|
||||
auto featurePython = doc->addObject("Measure::MeasurePython", measureType->label.c_str());
|
||||
setMeasureObject((Measure::MeasureBase*)featurePython);
|
||||
|
||||
// Create an instance of the pyMeasureClass, the classe's initializer sets the object as proxy
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::asObject(featurePython->getPyObject()));
|
||||
PyObject_CallObject(pyMeasureClass, args.ptr());
|
||||
}
|
||||
else {
|
||||
// Create measure object
|
||||
setMeasureObject(
|
||||
(Measure::MeasureBase*)doc->addObject(measureType->measureObject.c_str(), measureType->label.c_str())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// we have a valid measure object so we can enable the annotate button
|
||||
enableAnnotateButton(true);
|
||||
|
||||
// Fill measure object's properties from selection
|
||||
_mMeasureObject->parseSelection(selection);
|
||||
|
||||
// Get result
|
||||
valueResult->setText(_mMeasureObject->getResultString());
|
||||
}
|
||||
|
||||
void TaskMeasure::close(){
|
||||
Control().closeDialog();
|
||||
}
|
||||
|
||||
|
||||
void ensureGroup(Measure::MeasureBase* measurement) {
|
||||
// Ensure measurement object is part of the measurements group
|
||||
|
||||
const char* measurementGroupName = "Measurements";
|
||||
if (measurement == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
App::Document* doc = App::GetApplication().getActiveDocument();
|
||||
App::DocumentObject* obj = doc->getObject(measurementGroupName);
|
||||
if (!obj || !obj->isValid()) {
|
||||
obj = doc->addObject("App::DocumentObjectGroup", measurementGroupName);
|
||||
}
|
||||
|
||||
auto group = static_cast<App::DocumentObjectGroup*>(obj);
|
||||
group->addObject(measurement);
|
||||
}
|
||||
|
||||
|
||||
// Runs after the dialog is created
|
||||
void TaskMeasure::invoke() {
|
||||
update();
|
||||
}
|
||||
|
||||
bool TaskMeasure::apply(){
|
||||
ensureGroup(_mMeasureObject);
|
||||
_mMeasureObject = nullptr;
|
||||
reset();
|
||||
|
||||
// Commit transaction
|
||||
App::GetApplication().closeActiveTransaction();
|
||||
App::GetApplication().setActiveTransaction("Add Measurement");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TaskMeasure::reject(){
|
||||
removeObject();
|
||||
close();
|
||||
|
||||
// Abort transaction
|
||||
App::GetApplication().closeActiveTransaction(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
void TaskMeasure::reset() {
|
||||
// Reset tool state
|
||||
this->clearSelection();
|
||||
|
||||
// Should the explicit mode also be reset?
|
||||
// setModeSilent(nullptr);
|
||||
// explicitMode = false;
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
|
||||
void TaskMeasure::removeObject() {
|
||||
if (_mMeasureObject == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (_mMeasureObject->isRemoving() ) {
|
||||
return;
|
||||
}
|
||||
_mMeasureObject->getDocument()->removeObject (_mMeasureObject->getNameInDocument());
|
||||
setMeasureObject(nullptr);
|
||||
}
|
||||
|
||||
bool TaskMeasure::hasSelection(){
|
||||
return !Gui::Selection().getSelection().empty();
|
||||
}
|
||||
|
||||
void TaskMeasure::clearSelection(){
|
||||
Gui::Selection().clearSelection();
|
||||
}
|
||||
|
||||
void TaskMeasure::onSelectionChanged(const Gui::SelectionChanges& msg)
|
||||
{
|
||||
// Skip non-relevant events
|
||||
if (msg.Type != SelectionChanges::AddSelection && msg.Type != SelectionChanges::RmvSelection
|
||||
&& msg.Type != SelectionChanges::SetSelection && msg.Type != SelectionChanges::ClrSelection) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
bool TaskMeasure::eventFilter(QObject* obj, QEvent* event) {
|
||||
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
auto keyEvent = static_cast<QKeyEvent*>(event);
|
||||
|
||||
if (keyEvent->key() == Qt::Key_Escape) {
|
||||
|
||||
if (this->hasSelection()) {
|
||||
this->reset();
|
||||
} else {
|
||||
this->reject();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
|
||||
this->apply();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return TaskDialog::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void TaskMeasure::onModeChanged(int index) {
|
||||
explicitMode = (index != 0);
|
||||
this->update();
|
||||
}
|
||||
|
||||
void TaskMeasure::setModeSilent(App::MeasureType* mode) {
|
||||
modeSwitch->blockSignals(true);
|
||||
|
||||
if (mode == nullptr) {
|
||||
modeSwitch->setCurrentIndex(0);
|
||||
}
|
||||
else {
|
||||
modeSwitch->setCurrentText(QString::fromLatin1(mode->label.c_str()));
|
||||
}
|
||||
modeSwitch->blockSignals(false);
|
||||
}
|
||||
|
||||
// Get explicitly set measure type from the mode switch
|
||||
App::MeasureType* TaskMeasure::getMeasureType() {
|
||||
for (App::MeasureType* mType : App::MeasureManager::getMeasureTypes()) {
|
||||
if (mType->label.c_str() == modeSwitch->currentText().toLatin1()) {
|
||||
return mType;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
87
src/Gui/TaskMeasure.h
Normal file
87
src/Gui/TaskMeasure.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2023 David Friedli <david[at]friedli-be.ch> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
#include <qcolumnview.h>
|
||||
#include <QString>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/MeasureManager.h>
|
||||
|
||||
#include <Mod/Measure/App/MeasureBase.h>
|
||||
|
||||
#include "TaskView/TaskDialog.h"
|
||||
#include "TaskView/TaskView.h"
|
||||
#include "Selection.h"
|
||||
|
||||
namespace Gui {
|
||||
|
||||
class TaskMeasure : public TaskView::TaskDialog, public Gui::SelectionObserver {
|
||||
|
||||
public:
|
||||
TaskMeasure();
|
||||
~TaskMeasure() override;
|
||||
|
||||
void modifyStandardButtons(QDialogButtonBox* box) override;
|
||||
QDialogButtonBox::StandardButtons getStandardButtons() const override {
|
||||
return QDialogButtonBox::Apply | QDialogButtonBox::Abort | QDialogButtonBox::Reset;
|
||||
}
|
||||
|
||||
void invoke();
|
||||
void update();
|
||||
void close();
|
||||
bool apply();
|
||||
bool reject() override;
|
||||
void reset();
|
||||
|
||||
bool hasSelection();
|
||||
void clearSelection();
|
||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
void setMeasureObject(Measure::MeasureBase* obj);
|
||||
|
||||
private:
|
||||
QColumnView* dialog{nullptr};
|
||||
|
||||
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
|
||||
|
||||
Measure::MeasureBase *_mMeasureObject = nullptr;
|
||||
|
||||
QLineEdit* valueResult{nullptr};
|
||||
QLabel* labelResult{nullptr};
|
||||
QComboBox* modeSwitch{nullptr};
|
||||
|
||||
void removeObject();
|
||||
void onModeChanged(int index);
|
||||
void setModeSilent(App::MeasureType* mode);
|
||||
App::MeasureType* getMeasureType();
|
||||
void enableAnnotateButton(bool state);
|
||||
|
||||
// List of measure types
|
||||
std::vector<App::DocumentObject> measureObjects;
|
||||
|
||||
// Stores if the mode is explicitly set by the user or implicitly through the selection
|
||||
bool explicitMode = false;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Gui
|
||||
@@ -814,7 +814,7 @@ ToolBarItem* StdWorkbench::setupToolBars() const
|
||||
view->setCommand("View");
|
||||
*view << "Std_ViewFitAll" << "Std_ViewFitSelection" << "Std_ViewGroup"
|
||||
<< "Separator" << "Std_DrawStyle" << "Std_TreeViewActions"
|
||||
<< "Separator" << "Std_MeasureDistance";
|
||||
<< "Separator" << "Std_MeasureDistance" << "Std_Measure";
|
||||
|
||||
// Individual views
|
||||
auto individualViews = new ToolBarItem(root, ToolBarItem::DefaultVisibility::Hidden);
|
||||
|
||||
Reference in New Issue
Block a user