[Sketcher] Make status message styleable

Reduce the size and verbosity of the solver and constraint status
messages, and make them styleable both via stylesheets and user
preferences.
This commit is contained in:
Chris Hennes
2021-10-05 23:24:09 -05:00
parent 224ffe37e2
commit f2a073ca5a
5 changed files with 121 additions and 130 deletions

View File

@@ -59,8 +59,7 @@ TaskSketcherMessages::TaskSketcherMessages(ViewProviderSketch *sketchView) :
this->groupLayout()->addWidget(proxy); this->groupLayout()->addWidget(proxy);
connectionSetUp = sketchView->signalSetUp.connect(boost::bind(&SketcherGui::TaskSketcherMessages::slotSetUp, this, bp::_1, bp::_2)); connectionSetUp = sketchView->signalSetUp.connect(boost::bind(&SketcherGui::TaskSketcherMessages::slotSetUp, this, bp::_1, bp::_2, bp::_3, bp::_4));
connectionSolved = sketchView->signalSolved.connect(boost::bind(&SketcherGui::TaskSketcherMessages::slotSolved, this, bp::_1, bp::_2));
ui->labelConstrainStatus->setOpenExternalLinks(false); ui->labelConstrainStatus->setOpenExternalLinks(false);
@@ -72,19 +71,22 @@ TaskSketcherMessages::TaskSketcherMessages(ViewProviderSketch *sketchView) :
else else
sketchView->getSketchObject()->noRecomputes=true; sketchView->getSketchObject()->noRecomputes=true;
// Set up the possible state values for the solver status label // Set up the possible state values for the status label
const std::string paramGroup ("User parameter:BaseApp/Preferences/Mod/Sketcher"); ui->labelConstrainStatus->setParameterGroup("User parameter:BaseApp/Preferences/Mod/Sketcher/General");
ui->labelConstrainStatus->registerState(QString::fromUtf8("empty_sketch"), QColor("black"), paramGroup, "emptySketchMessageColor"); ui->labelConstrainStatus->registerState(QString::fromUtf8("empty_sketch"), QColor("black"), std::string("EmptySketchMessageColor"));
ui->labelConstrainStatus->registerState(QString::fromUtf8("under_constrained"), QColor("black"), paramGroup, "underconstrainedMessageColor"); ui->labelConstrainStatus->registerState(QString::fromUtf8("under_constrained"), QColor("black"), std::string("UnderconstrainedMessageColor"));
ui->labelConstrainStatus->registerState(QString::fromUtf8("malformed_constraints"), QColor("red"), paramGroup, "malformedConstraintMessageColor"); ui->labelConstrainStatus->registerState(QString::fromUtf8("malformed_constraints"), QColor("red"), std::string("MalformedConstraintMessageColor"));
ui->labelConstrainStatus->registerState(QString::fromUtf8("conflicting_constraints"), QColor("orangered"), paramGroup, "conflictingConstraintMessageColor"); ui->labelConstrainStatus->registerState(QString::fromUtf8("conflicting_constraints"), QColor("orangered"), std::string("ConflictingConstraintMessageColor"));
ui->labelConstrainStatus->registerState(QString::fromUtf8("redundant_constraints"), QColor("red"), paramGroup, "redundantConstraintMessageColor"); ui->labelConstrainStatus->registerState(QString::fromUtf8("redundant_constraints"), QColor("red"), std::string("RedundantConstraintMessageColor"));
ui->labelConstrainStatus->registerState(QString::fromUtf8("partially_redundant_constraints"), QColor("royalblue"), paramGroup, "partiallyRedundantConstraintMessageColor"); ui->labelConstrainStatus->registerState(QString::fromUtf8("partially_redundant_constraints"), QColor("royalblue"), std::string("PartiallyRedundantConstraintMessageColor"));
ui->labelConstrainStatus->registerState(QString::fromUtf8("fully_constrained"), QColor("green"), paramGroup, "fullyConstrainedMessageColor"); ui->labelConstrainStatus->registerState(QString::fromUtf8("solver_failed"), QColor("red"), std::string("SolverFailedMessageColor"));
ui->labelConstrainStatus->registerState(QString::fromUtf8("fully_constrained"), QColor("green"), std::string("FullyConstrainedMessageColor"));
ui->labelSolverStatus->registerState(QString::fromUtf8("good"), QColor("green"), QColor(255, 255, 255, 50), paramGroup, "solverGoodMessageColor"); ui->labelConstrainStatusLink->setLaunchExternal(false);
ui->labelSolverStatus->registerState(QString::fromUtf8("bad"), QColor("red"), QColor(255, 255, 255, 50), paramGroup, "solverBadMessageColor");
ui->labelSolverStatus->registerState(QString::fromUtf8("neutral"), QColor("black"), paramGroup, "solverNeutralMessageColor"); // Manually connect the link since it uses "clicked()", which labels don't have natively
connect(ui->labelConstrainStatusLink, &Gui::UrlLabel::linkClicked,
this, &TaskSketcherMessages::on_labelConstrainStatusLink_linkClicked);
/*QObject::connect( /*QObject::connect(
ui->labelConstrainStatus, SIGNAL(linkActivated(const QString &)), ui->labelConstrainStatus, SIGNAL(linkActivated(const QString &)),
@@ -103,22 +105,17 @@ TaskSketcherMessages::TaskSketcherMessages(ViewProviderSketch *sketchView) :
TaskSketcherMessages::~TaskSketcherMessages() TaskSketcherMessages::~TaskSketcherMessages()
{ {
connectionSetUp.disconnect(); connectionSetUp.disconnect();
connectionSolved.disconnect();
} }
void TaskSketcherMessages::slotSetUp(const QString &state, const QString &msg) void TaskSketcherMessages::slotSetUp(const QString& state, const QString& msg, const QString& link, const QString& linkText)
{ {
ui->labelConstrainStatus->setState(state); ui->labelConstrainStatus->setState(state);
ui->labelConstrainStatus->setText(msg); ui->labelConstrainStatus->setText(msg);
ui->labelConstrainStatusLink->setUrl(link);
ui->labelConstrainStatusLink->setText(linkText);
} }
void TaskSketcherMessages::slotSolved(const QString& state, const QString& msg) void TaskSketcherMessages::on_labelConstrainStatusLink_linkClicked(const QString &str)
{
ui->labelSolverStatus->setState(state);
ui->labelSolverStatus->setText(msg);
}
void TaskSketcherMessages::on_labelConstrainStatus_linkActivated(const QString &str)
{ {
if( str == QString::fromLatin1("#conflicting")) if( str == QString::fromLatin1("#conflicting"))
Gui::Application::Instance->commandManager().runCommandByName("Sketcher_SelectConflictingConstraints"); Gui::Application::Instance->commandManager().runCommandByName("Sketcher_SelectConflictingConstraints");

View File

@@ -47,11 +47,10 @@ public:
TaskSketcherMessages(ViewProviderSketch *sketchView); TaskSketcherMessages(ViewProviderSketch *sketchView);
~TaskSketcherMessages(); ~TaskSketcherMessages();
void slotSetUp(const QString &state, const QString &msg); void slotSetUp(const QString &state, const QString &msg, const QString& link, const QString& linkText);
void slotSolved(const QString& state, const QString &msg);
private Q_SLOTS: private Q_SLOTS:
void on_labelConstrainStatus_linkActivated(const QString &); void on_labelConstrainStatusLink_linkClicked(const QString &);
void on_autoUpdate_stateChanged(int state); void on_autoUpdate_stateChanged(int state);
void on_autoRemoveRedundants_stateChanged(int state); void on_autoRemoveRedundants_stateChanged(int state);
void on_manualUpdate_clicked(bool checked); void on_manualUpdate_clicked(bool checked);
@@ -59,7 +58,6 @@ private Q_SLOTS:
protected: protected:
ViewProviderSketch *sketchView; ViewProviderSketch *sketchView;
Connection connectionSetUp; Connection connectionSetUp;
Connection connectionSolved;
private: private:
QWidget* proxy; QWidget* proxy;

View File

@@ -15,24 +15,35 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="Gui::StatefulLabel" name="labelConstrainStatus"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="text"> <item>
<string>Undefined degrees of freedom</string> <widget class="Gui::StatefulLabel" name="labelConstrainStatus">
</property> <property name="text">
<property name="wordWrap"> <string>DOF</string>
<bool>true</bool> </property>
</property> </widget>
</widget> </item>
</item> <item>
<item> <widget class="Gui::UrlLabel" name="labelConstrainStatusLink">
<widget class="Gui::StatefulLabel" name="labelSolverStatus"> <property name="text">
<property name="text"> <string>Link</string>
<string>Not solved yet</string> </property>
</property> </widget>
<property name="wordWrap"> </item>
<bool>true</bool> <item>
</property> <spacer name="horizontalSpacer">
</widget> <property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item> </item>
<item> <item>
<widget class="Gui::PrefCheckBox" name="autoRemoveRedundants"> <widget class="Gui::PrefCheckBox" name="autoRemoveRedundants">
@@ -94,11 +105,16 @@
<extends>QCheckBox</extends> <extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header> <header>Gui/PrefWidgets.h</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>Gui::StatefulLabel</class> <class>Gui::StatefulLabel</class>
<extends>QLabel</extends> <extends>QLabel</extends>
<header>Gui/Widgets.h</header> <header>Gui/Widgets.h</header>
</customwidget> </customwidget>
<customwidget>
<class>Gui::UrlLabel</class>
<extends>QLabel</extends>
<header>Gui/Widgets.h</header>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@@ -1312,10 +1312,6 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor
if (getSketchObject()->moveTemporaryPoint(GeoId, PosId, vec, false) == 0) { if (getSketchObject()->moveTemporaryPoint(GeoId, PosId, vec, false) == 0) {
setPositionText(Base::Vector2d(x,y)); setPositionText(Base::Vector2d(x,y));
draw(true,false); draw(true,false);
signalSolved(QString::fromUtf8("neutral"), QString::fromLatin1("Solved in %1 sec").arg(getSolvedSketch().getSolveTime()));
} else {
signalSolved(QString::fromUtf8("bad"), QString::fromLatin1("Unsolved (%1 sec)").arg(getSolvedSketch().getSolveTime()));
//Base::Console().Log("Error solving:%d\n",ret);
} }
} }
} }
@@ -1353,9 +1349,6 @@ bool ViewProviderSketch::mouseMove(const SbVec2s &cursorPos, Gui::View3DInventor
if (getSketchObject()->moveTemporaryPoint(edit->DragCurve, Sketcher::none, vec, relative) == 0) { if (getSketchObject()->moveTemporaryPoint(edit->DragCurve, Sketcher::none, vec, relative) == 0) {
setPositionText(Base::Vector2d(x,y)); setPositionText(Base::Vector2d(x,y));
draw(true,false); draw(true,false);
signalSolved(QString::fromUtf8("neutral"), QString::fromLatin1("Solved in %1 sec").arg(getSolvedSketch().getSolveTime()));
} else {
signalSolved(QString::fromUtf8("bad"), QString::fromLatin1("Unsolved (%1 sec)").arg(getSolvedSketch().getSolveTime()));
} }
} }
return true; return true;
@@ -6584,6 +6577,29 @@ QString ViewProviderSketch::appendConstraintMsg(const QString & singularmsg,
return msg; return msg;
} }
inline QString intListHelper(const std::vector<int> &ints)
{
QString results;
if (ints.size() < 8) { // The 8 is a bit heuristic... more than that and we shift formats
for (const auto i : ints) {
if (results.isEmpty())
results.append(QString::fromUtf8("%1").arg(i));
else
results.append(QString::fromUtf8(", %1").arg(i));
}
}
else {
const int numToShow = 3;
int more = ints.size() - numToShow;
for (int i = 0; i < numToShow; ++i) {
results.append(QString::fromUtf8("%1, ").arg(ints[i]));
}
results.append(QCoreApplication::translate("ViewProviderSketch","and %1 more").arg(more));
}
std::string testString = results.toStdString();
return results;
}
void ViewProviderSketch::UpdateSolverInformation() void ViewProviderSketch::UpdateSolverInformation()
{ {
// Updates Solver Information with the Last solver execution at SketchObject level // Updates Solver Information with the Last solver execution at SketchObject level
@@ -6594,82 +6610,48 @@ void ViewProviderSketch::UpdateSolverInformation()
bool hasMalformed = getSketchObject()->getLastHasMalformedConstraints(); bool hasMalformed = getSketchObject()->getLastHasMalformedConstraints();
if (getSketchObject()->Geometry.getSize() == 0) { if (getSketchObject()->Geometry.getSize() == 0) {
signalSetUp(QString::fromUtf8("empty_sketch"), tr("Empty sketch")); signalSetUp(QString::fromUtf8("empty_sketch"), tr("Empty sketch"), QString(), QString());
signalSolved(QString::fromUtf8("neutral"), QString());
} }
else if (dofs < 0) { // over-constrained sketch else if (dofs < 0 || hasConflicts) { // over-constrained sketch
std::string msg; signalSetUp(QString::fromUtf8("conflicting_constraints"),
SketchObject::appendConflictMsg(getSketchObject()->getLastConflicting(), msg); tr("Over-constrained: "),
signalSetUp(QString::fromUtf8("conflicting_constraints"), QString::fromUtf8("#conflicting"),
QString::fromLatin1("%1 <a href=\"#conflicting\"><span style=\" text-decoration: underline; color:#0000ff; background-color: #F8F8FF;\">%2</span></a><br/>%3<br/>") QString::fromUtf8("(%1)").arg(intListHelper(getSketchObject()->getLastConflicting())));
.arg(tr("Over-constrained sketch"))
.arg(tr("(click to select)"))
.arg(QString::fromStdString(msg)));
signalSolved(QString::fromUtf8("neutral"), QString());
} }
else if (hasMalformed) { // malformed constraints else if (hasMalformed) { // malformed constraints
signalSetUp(QString::fromUtf8("malformed_constraints"), signalSetUp(QString::fromUtf8("malformed_constraints"),
QString::fromLatin1("%1 <a href=\"#malformed\"><span style=\" text-decoration: underline; color:#0000ff; background-color: #F8F8FF;\">%2</span></a><br/>%3<br/>") tr("Malformed constraints: "),
.arg(tr("Sketch contains malformed constraints")) QString::fromUtf8("#malformed"),
.arg(tr("(click to select)")) QString::fromUtf8("(%1)").arg(intListHelper(getSketchObject()->getLastMalformedConstraints())));
.arg(appendMalformedMsg(getSketchObject()->getLastMalformedConstraints())));
signalSolved(QString::fromUtf8("neutral"), QString());
} }
else if (hasConflicts) { // conflicting constraints else if (hasRedundancies) {
signalSetUp(QString::fromUtf8("conflicting_constraints"), signalSetUp(QString::fromUtf8("redundant_constraints"),
QString::fromLatin1("%1 <a href=\"#conflicting\"><span style=\" text-decoration: underline; color:#0000ff; background-color: #F8F8FF;\">%2</span></a><br/>%3<br/>") tr("Redundant constraints:"),
.arg(tr("Sketch contains conflicting constraints")) QString::fromUtf8("#redundant"),
.arg(tr("(click to select)")) QString::fromUtf8("(%1)").arg(intListHelper(getSketchObject()->getLastRedundant())));
.arg(appendConflictMsg(getSketchObject()->getLastConflicting()))); }
signalSolved(QString::fromUtf8("neutral"), QString()); else if (hasPartiallyRedundant) {
signalSetUp(QString::fromUtf8("partially_redundant_constraints"),
tr("Partially redundant:"),
QString::fromUtf8("#partiallyredundant"),
QString::fromUtf8("(%1)").arg(intListHelper(getSketchObject()->getLastPartiallyRedundant())));
}
else if (getSketchObject()->getLastSolverStatus() != 0) {
signalSetUp(QString::fromUtf8("solver_failed"),
tr("Solver failed to converge"),
QString::fromUtf8(""),
QString::fromUtf8(""));
} else if (dofs > 0) {
signalSetUp(QString::fromUtf8("under_constrained"),
tr("Under constrained:"),
QString::fromUtf8("#dofs"),
QString::fromUtf8("%1 %2").arg(dofs).arg(tr("DoF")));
} }
else { else {
if (hasRedundancies) { // redundant constraints signalSetUp(QString::fromUtf8("fully_constrained"), tr("Fully constrained"), QString(), QString());
signalSetUp(QString::fromUtf8("redundant_constraints"), // color the sketch as fully constrained if it has geometry (other than the axes)
QString::fromLatin1("%1 <a href=\"#redundant\"><span style=\" text-decoration: underline; color:#0000ff; background-color: #F8F8FF;\">%2</span></a><br/>%3<br/>") if(getSolvedSketch().getGeometrySize()>2)
.arg(tr("Sketch contains redundant constraints")) edit->FullyConstrained = true;
.arg(tr("(click to select)"))
.arg(appendRedundantMsg(getSketchObject()->getLastRedundant())));
}
QString partiallyRedundantString;
if(hasPartiallyRedundant) {
partiallyRedundantString = QString::fromLatin1("<br/><span style=\"background-color: rgba(255,255,255,50) ;\">%1 <a href=\"#partiallyredundant\"><span style=\" text-decoration: underline; color:#0000ff; background-color: #F8F8FF;\">%2</span></a><br/>%3</span><br/>")
.arg(tr("Sketch contains partially redundant constraints"))
.arg(tr("(click to select)"))
.arg(appendPartiallyRedundantMsg(getSketchObject()->getLastPartiallyRedundant()));
}
if (getSketchObject()->getLastSolverStatus() == 0) {
if (dofs == 0) {
// color the sketch as fully constrained if it has geometry (other than the axes)
if(getSolvedSketch().getGeometrySize()>2)
edit->FullyConstrained = true;
if (!hasRedundancies) {
signalSetUp(QString::fromUtf8("fully_constrained"),
partiallyRedundantString + tr("Fully constrained sketch"));
}
}
else if (!hasRedundancies) {
QString infoString;
if (dofs == 1)
signalSetUp(QString::fromUtf8("under_constrained"),
tr("Under-constrained sketch with <a href=\"#dofs\"><span style=\" text-decoration: underline; color:#0000ff; background-color: #F8F8FF;\">1 degree</span></a> of freedom. %1")
.arg(partiallyRedundantString));
else
signalSetUp(QString::fromUtf8("under_constrained"),
tr("Under-constrained sketch with <a href=\"#dofs\"><span style=\" text-decoration: underline; color:#0000ff; background-color: #F8F8FF;\">%1 degrees</span></a> of freedom. %2")
.arg(dofs)
.arg(partiallyRedundantString));
}
signalSolved(QString::fromUtf8("good"), tr("Solved in %1 sec").arg(getSketchObject()->getLastSolveTime()));
}
else {
signalSolved(QString::fromUtf8("bad"), tr("Unsolved (%1 sec)").arg(getSketchObject()->getLastSolveTime()));
}
} }
} }

View File

@@ -273,9 +273,7 @@ public:
/// signals if the constraints list has changed /// signals if the constraints list has changed
boost::signals2::signal<void ()> signalConstraintsChanged; boost::signals2::signal<void ()> signalConstraintsChanged;
/// signals if the sketch has been set up /// signals if the sketch has been set up
boost::signals2::signal<void (const QString &state, const QString &msg)> signalSetUp; boost::signals2::signal<void (const QString &state, const QString &msg, const QString &url, const QString &linkText)> signalSetUp;
/// signals if the sketch has been solved
boost::signals2::signal<void (const QString &state, const QString &msg)> signalSolved;
/// signals if the elements list has changed /// signals if the elements list has changed
boost::signals2::signal<void ()> signalElementsChanged; boost::signals2::signal<void ()> signalElementsChanged;