Element editor : improve polygon edition

This commit is contained in:
joshua
2019-09-01 20:29:26 +02:00
parent 3fa071841c
commit 86b610dc84
10 changed files with 456 additions and 279 deletions

View File

@@ -91,6 +91,8 @@ class CustomElementGraphicPart : public QGraphicsObject, public CustomElementPar
QVariant property (const char *name) const override {return QObject::property(name);}
virtual QPainterPath shadowShape ()const = 0;
virtual void setHandlerColor(QPointF /*pos*/, const QColor &/*color*/) {}
virtual void resetAllHandlerColor() {}
protected:
void stylesToXml (QDomElement &) const;

View File

@@ -257,6 +257,32 @@ void PartPolygon::setClosed(bool close)
emit closedChange();
}
/**
* @brief PartPolygon::setHandlerColor
* Set the handler at pos @pos (in polygon coordinate) to color @color.
* @param pos
* @param color
*/
void PartPolygon::setHandlerColor(QPointF pos, const QColor &color)
{
for (QetGraphicsHandlerItem *qghi : m_handler_vector) {
if (qghi->pos() == mapToScene(pos)) {
qghi->setColor(color);
}
}
}
/**
* @brief PartPolygon::resetAllHandlerColor
* Reset the color of every handlers
*/
void PartPolygon::resetAllHandlerColor()
{
for (QetGraphicsHandlerItem *qghi : m_handler_vector) {
qghi->setColor(Qt::blue);
}
}
/**
* @brief PartPolygon::itemChange
* @param change
@@ -518,6 +544,7 @@ void PartPolygon::removePoint()
if (index > -1 && index<m_handler_vector.count())
{
QPolygonF polygon = this->polygon();
qDebug() << index;
polygon.removeAt(index);
//Wrap the undo for avoid to merge the undo commands when user add several points.

View File

@@ -84,6 +84,9 @@ class PartPolygon : public CustomElementGraphicPart
bool isClosed () const {return m_closed;}
void setClosed (bool close);
void setHandlerColor(QPointF pos, const QColor &color) final;
void resetAllHandlerColor() final;
protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) override;

View File

@@ -1,206 +0,0 @@
/*
Copyright 2006-2019 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "polygoneditor.h"
#include "partpolygon.h"
#include "elementscene.h"
#include "qetmessagebox.h"
#include "styleeditor.h"
#include "QPropertyUndoCommand/qpropertyundocommand.h"
/**
Constructeur
@param editor L'editeur d'element concerne
@param p Le polygone a editer
@param parent le Widget parent
*/
PolygonEditor::PolygonEditor(QETElementEditor *editor, PartPolygon *p, QWidget *parent) :
ElementItemEditor(editor, parent),
part(p),
points_list(this),
close_polygon(tr("Polygone fermé"), this)
{
style_ = new StyleEditor(editor);
// prepare la liste de points
points_list.setColumnCount(2);
QStringList headers;
headers << tr("x") << tr("y");
points_list.setHeaderLabels(headers);
points_list.setRootIsDecorated(false);
updateForm();
// layout
QVBoxLayout *layout = new QVBoxLayout(this);
layout -> addWidget(style_);
layout -> addWidget(new QLabel(tr("Points du polygone :")));
layout -> addWidget(&points_list);
layout -> addWidget(&close_polygon);
layout->addStretch();
updateForm();
}
/// Destructeur
PolygonEditor::~PolygonEditor() {
}
/**
Met a jour les points du polygone et cree un objet d'annulation
*/
void PolygonEditor::updatePolygonPoints()
{
if (!part) return;
QPolygonF points = getPointsFromTree();
if (points.count() < 2)
{
QET::QetMessageBox::warning(this, tr("Erreur", "message box title"), tr("Le polygone doit comporter au moins deux points.", "message box content"));
return;
}
if (points != part->polygon())
{
QPropertyUndoCommand *undo = new QPropertyUndoCommand(part, "polygon", part->property("polygon"), points);
undo->setText(tr("Modifier un polygone"));
undoStack().push(undo);
}
}
/**
Met a jour l'etat ferme ou non du polygone
*/
void PolygonEditor::updatePolygonClosedState()
{
if (!part) return;
bool close = close_polygon.isChecked();
if (close != part->isClosed())
{
QPropertyUndoCommand *undo = new QPropertyUndoCommand(part, "closed", part->property("closed"), close);
undo->setText(tr("Modifier un polygone"));
undoStack().push(undo);
}
}
/**
Met a jour le formulaire d'edition
*/
void PolygonEditor::updateForm() {
if (!part) return;
activeConnections(false);
while(points_list.takeTopLevelItem(0)) {}
foreach(QPointF point, part -> polygon()) {
point = part -> mapToScene(point);
QStringList qsl;
qsl << QString("%1").arg(point.x()) << QString("%1").arg(point.y());
QTreeWidgetItem *qtwi = new QTreeWidgetItem(qsl);
qtwi -> setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
points_list.addTopLevelItem(qtwi);
}
close_polygon.setChecked(part -> isClosed());
activeConnections(true);
}
/**
Permet de specifier a cet editeur quelle primitive il doit editer. A noter
qu'un editeur peut accepter ou refuser d'editer une primitive.
L'editeur de polygone acceptera d'editer la primitive new_part s'il s'agit
d'un objet de la classe PartPolygon.
@param new_part Nouvelle primitive a editer
@return true si l'editeur a accepter d'editer la primitive, false sinon
*/
bool PolygonEditor::setPart(CustomElementPart *new_part)
{
if (!new_part)
{
if (part)
{
disconnect(part, &PartPolygon::polygonChanged, this, &PolygonEditor::updateForm);
disconnect(part, &PartPolygon::closedChange, this, &PolygonEditor::updateForm);
}
part = nullptr;
style_ -> setPart(nullptr);
return(true);
}
if (PartPolygon *part_polygon = dynamic_cast<PartPolygon *>(new_part))
{
if (part == part_polygon) return true;
if (part)
{
disconnect(part, &PartPolygon::polygonChanged, this, &PolygonEditor::updateForm);
disconnect(part, &PartPolygon::closedChange, this, &PolygonEditor::updateForm);
}
part = part_polygon;
style_ -> setPart(part);
updateForm();
connect(part, &PartPolygon::polygonChanged, this, &PolygonEditor::updateForm);
connect(part, &PartPolygon::closedChange, this, &PolygonEditor::updateForm);
return(true);
}
return(false);
}
/**
@return la primitive actuellement editee, ou 0 si ce widget n'en edite pas
*/
CustomElementPart *PolygonEditor::currentPart() const {
return(part);
}
/**
@return Un vecteur contenant les points composant le polygone a partir du
formulaire d'edition
*/
QVector<QPointF> PolygonEditor::getPointsFromTree() {
if (!part) return(QVector<QPointF>());
QVector<QPointF> points;
for(int i = 0 ; i < points_list.topLevelItemCount() ; ++ i) {
QTreeWidgetItem *qtwi = points_list.topLevelItem(i);
bool x_convert_ok, y_convert_ok;
qreal x = qtwi -> text(0).toDouble(&x_convert_ok);
qreal y = qtwi -> text(1).toDouble(&y_convert_ok);
if (!x_convert_ok || !y_convert_ok) continue;
points << part -> mapFromScene(QPointF(x, y));
}
return(points);
}
/**
@param qtwi QTreeWidgetItem a valider
@param column Colonne exacte du QTreeWidgetItem a valider
*/
void PolygonEditor::validColumn(QTreeWidgetItem *qtwi, int column) {
bool convert_ok;
qtwi -> text(column).toDouble(&convert_ok);
if (convert_ok) {
points_list.closePersistentEditor(qtwi, column);
updatePolygonPoints();
} else points_list.openPersistentEditor(qtwi, column);
}
/**
Active ou desactive les connexionx signaux/slots entre les widgets internes.
@param active true pour activer les connexions, false pour les desactiver
*/
void PolygonEditor::activeConnections(bool active) {
if (active) {
connect(&close_polygon, SIGNAL(stateChanged(int)), this, SLOT(updatePolygonClosedState()));
connect(&points_list, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(validColumn(QTreeWidgetItem *, int)));
} else {
disconnect(&close_polygon, SIGNAL(stateChanged(int)), this, SLOT(updatePolygonClosedState()));
disconnect(&points_list, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(validColumn(QTreeWidgetItem *, int)));
}
}

View File

@@ -1,68 +0,0 @@
/*
Copyright 2006-2019 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef POLYGON_EDITOR_H
#define POLYGON_EDITOR_H
#include "elementitemeditor.h"
#include <QTreeWidget>
#include <QCheckBox>
class PartPolygon;
class StyleEditor;
class QTreeWidget;
/**
This class provides a widget to edit polygons within the element editor.
*/
class PolygonEditor : public ElementItemEditor {
Q_OBJECT
// constructors, destructor
public:
PolygonEditor(QETElementEditor *, PartPolygon * = nullptr, QWidget * = nullptr);
~PolygonEditor() override;
private:
PolygonEditor(const PolygonEditor &);
// attributes
private:
PartPolygon *part;
StyleEditor *style_;
QTreeWidget points_list;
QCheckBox close_polygon;
// methods
public:
bool setPart(CustomElementPart *) override;
CustomElementPart *currentPart() const override;
private:
QVector<QPointF> getPointsFromTree();
public slots:
void updatePolygonPoints();
void updatePolygonClosedState();
void updateForm() override;
void validColumn(QTreeWidgetItem *qtwi, int column);
private:
void activeConnections(bool);
};
#endif

View File

@@ -0,0 +1,289 @@
/*
Copyright 2006-2019 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#include "polygoneditor.h"
#include "ui_polygoneditor.h"
#include "partpolygon.h"
#include "elementscene.h"
#include "qetmessagebox.h"
#include "styleeditor.h"
#include "QPropertyUndoCommand/qpropertyundocommand.h"
/**
* @brief PolygonEditor::PolygonEditor
* @param editor
* @param part
* @param parent
*/
PolygonEditor::PolygonEditor(QETElementEditor *editor, PartPolygon *part, QWidget *parent) :
ElementItemEditor(editor, parent),
ui(new Ui::PolygonEditor),
m_part(part)
{
ui->setupUi(this);
m_style = new StyleEditor(editor);
ui->m_main_layout->insertWidget(0, m_style);
updateForm();
ui->m_points_list_tree->installEventFilter(this);
ui->m_points_list_tree->addAction(ui->m_add_point_action);
ui->m_points_list_tree->addAction(ui->m_remove_point_action);
}
/**
* @brief PolygonEditor::~PolygonEditor
*/
PolygonEditor::~PolygonEditor() {
delete ui;
}
/**
* @brief PolygonEditor::setPart
* @param new_part
* @return
*/
bool PolygonEditor::setPart(CustomElementPart *new_part)
{
if (!new_part)
{
if (m_part)
{
disconnect(m_part, &PartPolygon::polygonChanged, this, &PolygonEditor::updateForm);
disconnect(m_part, &PartPolygon::closedChange, this, &PolygonEditor::updateForm);
}
m_part = nullptr;
m_style -> setPart(nullptr);
return(true);
}
if (PartPolygon *part_polygon = dynamic_cast<PartPolygon *>(new_part))
{
if (m_part == part_polygon) return true;
if (m_part)
{
disconnect(m_part, &PartPolygon::polygonChanged, this, &PolygonEditor::updateForm);
disconnect(m_part, &PartPolygon::closedChange, this, &PolygonEditor::updateForm);
disconnect(m_part, &PartPolygon::xChanged, this, &PolygonEditor::updateForm);
disconnect(m_part, &PartPolygon::yChanged, this, &PolygonEditor::updateForm);
}
m_part = part_polygon;
m_style -> setPart(m_part);
updateForm();
connect(m_part, &PartPolygon::polygonChanged, this, &PolygonEditor::updateForm);
connect(m_part, &PartPolygon::closedChange, this, &PolygonEditor::updateForm);
connect(m_part, &PartPolygon::xChanged, this, &PolygonEditor::updateForm);
connect(m_part, &PartPolygon::yChanged, this, &PolygonEditor::updateForm);
return(true);
}
return(false);
}
/**
* @brief PolygonEditor::currentPart
* @return the curent edited part
*/
CustomElementPart *PolygonEditor::currentPart() const {
return m_part;
}
/**
* @brief PolygonEditor::updateForm
* Update the widget
*/
void PolygonEditor::updateForm()
{
if (!m_part) {
return;
}
ui->m_points_list_tree->clear();
for(QPointF point : m_part->polygon())
{
point = m_part->mapToScene(point);
QTreeWidgetItem *qtwi = new QTreeWidgetItem();
qtwi->setData(0, Qt::EditRole, point.x());
qtwi->setData(1, Qt::EditRole, point.y());
qtwi -> setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled);
ui->m_points_list_tree->addTopLevelItem(qtwi);
}
ui->m_close_polygon_cb->setChecked(m_part->isClosed());
ui->m_remove_point_action->setEnabled(m_part->polygon().size() > 2 ? true : false);
}
/**
* @brief PolygonEditor::pointsFromTree
* @return the point of polygon from the current value of the tree editor
* if part coordinate.
*/
QVector<QPointF> PolygonEditor::pointsFromTree()
{
QVector<QPointF> points;
if (!m_part) {
return points;
}
for(int i = 0 ; i < ui->m_points_list_tree->topLevelItemCount() ; ++ i)
{
QTreeWidgetItem *qtwi = ui->m_points_list_tree->topLevelItem(i);
bool x_convert_ok, y_convert_ok;
qreal x = qtwi->data(0, Qt::EditRole).toReal(&x_convert_ok);
qreal y = qtwi->data(1, Qt::EditRole).toReal(&y_convert_ok);
if (x_convert_ok && y_convert_ok) {
points << m_part->mapFromScene(QPointF(x, y));
}
}
return(points);
}
bool PolygonEditor::eventFilter(QObject *watched, QEvent *event)
{
if (watched == ui->m_points_list_tree &&
event->type() == QEvent::FocusOut &&
m_part)
{
m_part->resetAllHandlerColor();
return true;
}
return false;
}
/**
* @brief PolygonEditor::on_m_close_polygon_cb_stateChanged
*/
void PolygonEditor::on_m_close_polygon_cb_stateChanged(int arg1)
{
Q_UNUSED(arg1);
if (!m_part) {
return;
}
bool close = ui->m_close_polygon_cb->isChecked();
if (close != m_part->isClosed())
{
QPropertyUndoCommand *undo = new QPropertyUndoCommand(m_part, "closed", m_part->property("closed"), close);
undo->setText(tr("Modifier un polygone"));
undoStack().push(undo);
}
}
/**
* @brief PolygonEditor::on_m_points_list_tree_itemChanged
* Update the polygon according to the current value of the tree editor
*/
void PolygonEditor::on_m_points_list_tree_itemChanged(QTreeWidgetItem *item, int column)
{
Q_UNUSED(item);
Q_UNUSED(column);
if (!m_part) {
return;
}
QPolygonF points = pointsFromTree();
if (points.count() < 2)
{
QET::QetMessageBox::warning(this, tr("Erreur", "message box title"), tr("Le polygone doit comporter au moins deux points.", "message box content"));
return;
}
if (points != m_part->polygon())
{
QPropertyUndoCommand *undo = new QPropertyUndoCommand(m_part, "polygon", m_part->property("polygon"), points);
undo->setText(tr("Modifier un polygone"));
undoStack().push(undo);
}
}
/**
* @brief PolygonEditor::on_m_points_list_tree_itemSelectionChanged
* Used to change the color of the current selected point.
*/
void PolygonEditor::on_m_points_list_tree_itemSelectionChanged()
{
//Prevent when selection change but the widget ins't focused
if (!ui->m_points_list_tree->hasFocus()) {
return;
}
QTreeWidgetItem *qtwi = ui->m_points_list_tree->currentItem();
if (!qtwi || !m_part) {
return;
}
m_part->resetAllHandlerColor();
int index = ui->m_points_list_tree->indexOfTopLevelItem(qtwi);
//We need to check if index isn't out of range of polygon
//this case can occur when user remove the last point of the polygon
//with the context menu of the tree widget
if(index >= 0 &&
index < m_part->polygon().size())
{
m_part->setHandlerColor(m_part->polygon().at(index), QColor(0, 255, 128));
}
}
void PolygonEditor::on_m_add_point_action_triggered()
{
QTreeWidgetItem *qtwi = ui->m_points_list_tree->currentItem();
if (!qtwi || !m_part) {
return;
}
int index = ui->m_points_list_tree->indexOfTopLevelItem(qtwi);
QPolygonF new_polygon = m_part->polygon();
//Special case when user add a point after the last point of the polygon
if (index == m_part->polygon().size()-1)
{
QPointF p = m_part->polygon().last();
p.rx()+=20;
p.ry()+=20;
new_polygon.append(p);
}
else
{
QPointF p = m_part->polygon().at(index) +
m_part->polygon().at(index+1);
p/=2;
new_polygon.insert(index+1, p);
}
//Wrap the undo for avoid to merge the undo commands when user add several points.
QUndoCommand *undo = new QUndoCommand(tr("Ajouter un point à un polygone"));
new QPropertyUndoCommand(m_part, "polygon", m_part->polygon(), new_polygon, undo);
elementScene()->undoStack().push(undo);
m_part->resetAllHandlerColor();
m_part->setHandlerColor(m_part->polygon().at(index+1), QColor(0, 255, 128));
}
void PolygonEditor::on_m_remove_point_action_triggered()
{
QTreeWidgetItem *qtwi = ui->m_points_list_tree->currentItem();
if (!qtwi || !m_part) {
return;
}
QPolygonF new_polygon = m_part->polygon();
new_polygon.removeAt(ui->m_points_list_tree->indexOfTopLevelItem(qtwi));
//Wrap the undo for avoid to merge the undo commands when user remove several points.
QUndoCommand *undo = new QUndoCommand(tr("Supprimer un point d'un polygone"));
new QPropertyUndoCommand(m_part, "polygon", m_part->polygon(), new_polygon, undo);
elementScene()->undoStack().push(undo);
}

View File

@@ -0,0 +1,58 @@
/*
Copyright 2006-2019 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef POLYGONEDITOR_H
#define POLYGONEDITOR_H
#include "elementitemeditor.h"
class StyleEditor;
class PartPolygon;
class QTreeWidgetItem;
namespace Ui {
class PolygonEditor;
}
class PolygonEditor : public ElementItemEditor
{
Q_OBJECT
public:
explicit PolygonEditor(QETElementEditor *editor, PartPolygon *part = nullptr, QWidget *parent = nullptr);
~PolygonEditor() override;
bool setPart(CustomElementPart *part) override;
CustomElementPart *currentPart() const override;
void updateForm() override;
QVector<QPointF> pointsFromTree();
bool eventFilter(QObject *watched, QEvent *event) override;
private slots:
void on_m_close_polygon_cb_stateChanged(int arg1);
void on_m_points_list_tree_itemChanged(QTreeWidgetItem *item, int column);
void on_m_points_list_tree_itemSelectionChanged();
void on_m_add_point_action_triggered();
void on_m_remove_point_action_triggered();
private:
Ui::PolygonEditor *ui;
StyleEditor *m_style = nullptr;
PartPolygon *m_part = nullptr;
};
#endif // POLYGONEDITOR_H

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PolygonEditor</class>
<widget class="QWidget" name="PolygonEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="m_main_layout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Points du polygone :</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="m_points_list_tree">
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<column>
<property name="text">
<string>X</string>
</property>
</column>
<column>
<property name="text">
<string>Y</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QCheckBox" name="m_close_polygon_cb">
<property name="text">
<string>Polygone fermé</string>
</property>
</widget>
</item>
</layout>
<action name="m_add_point_action">
<property name="icon">
<iconset resource="../../../qelectrotech.qrc">
<normaloff>:/ico/16x16/list-add.png</normaloff>:/ico/16x16/list-add.png</iconset>
</property>
<property name="text">
<string>Ajouter un point</string>
</property>
</action>
<action name="m_remove_point_action">
<property name="icon">
<iconset resource="../../../qelectrotech.qrc">
<normaloff>:/ico/16x16/list-remove.png</normaloff>:/ico/16x16/list-remove.png</iconset>
</property>
<property name="text">
<string>Supprimer le point</string>
</property>
</action>
</widget>
<resources>
<include location="../../../qelectrotech.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -38,7 +38,7 @@ class RectangleEditor : public ElementItemEditor
public:
explicit RectangleEditor(QETElementEditor *editor, PartRectangle *rect = nullptr, QWidget *parent = nullptr);
~RectangleEditor();
~RectangleEditor() override;
bool setPart(CustomElementPart *part) override;
CustomElementPart *currentPart() const override;