Ajout d'un widget permettant d'editer l'angle d'orientation d'un texte plus intuitivement.

git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/branches/0.3@833 bfdf4180-ca20-0410-9c96-a3a8aa849046
This commit is contained in:
xavier 2010-01-03 19:45:33 +00:00
parent 27e2c052b2
commit 07bf60674d
4 changed files with 460 additions and 3 deletions

View File

@ -17,7 +17,8 @@
*/ */
#include "textfieldeditor.h" #include "textfieldeditor.h"
#include "parttextfield.h" #include "parttextfield.h"
#include "qtextorientationwidget.h"
#include "qetapp.h"
/** /**
Constructeur Constructeur
@param editor L'editeur d'element concerne @param editor L'editeur d'element concerne
@ -36,8 +37,20 @@ TextFieldEditor::TextFieldEditor(QETElementEditor *editor, PartTextField *textfi
rotate -> setChecked(true); rotate -> setChecked(true);
rotation_angle_ = new QDoubleSpinBox(); rotation_angle_ = new QDoubleSpinBox();
rotation_angle_ -> setRange(-360.0, 360.0); rotation_angle_ -> setRange(-360.0, 360.0);
rotation_angle_ -> setSingleStep(-90.0);
rotation_angle_ -> setSuffix("\260"); rotation_angle_ -> setSuffix("\260");
rotation_widget_ = new QTextOrientationWidget();
rotation_widget_ -> setFont(QETApp::diagramTextsFont());
rotation_widget_ -> setUsableTexts(QList<QString>()
<< tr("Q", "Single-letter example text - translate length, not meaning")
<< tr("QET", "Small example text - translate length, not meaning")
<< tr("Schema", "Normal example text - translate length, not meaning")
<< tr("Electrique", "Normal example text - translate length, not meaning")
<< tr("QElectroTech", "Long example text - translate length, not meaning")
);
rotation_widget_ -> setMinimumSize(90.0, 90.0);
connect(rotation_angle_, SIGNAL(valueChanged(double)), rotation_widget_, SLOT(setOrientation(double)));
connect(rotation_widget_, SIGNAL(orientationChanged(double)), rotation_angle_, SLOT(setValue(double)));
connect(rotation_widget_, SIGNAL(orientationChanged(double)), rotation_angle_, SIGNAL(editingFinished()));
qle_x -> setValidator(new QDoubleValidator(qle_x)); qle_x -> setValidator(new QDoubleValidator(qle_x));
qle_y -> setValidator(new QDoubleValidator(qle_y)); qle_y -> setValidator(new QDoubleValidator(qle_y));
@ -64,6 +77,7 @@ TextFieldEditor::TextFieldEditor(QETElementEditor *editor, PartTextField *textfi
QHBoxLayout *rotation_angle_layout = new QHBoxLayout(); QHBoxLayout *rotation_angle_layout = new QHBoxLayout();
rotation_angle_layout -> addWidget(new QLabel(tr("Angle de rotation par d\351faut : "))); rotation_angle_layout -> addWidget(new QLabel(tr("Angle de rotation par d\351faut : ")));
rotation_angle_layout -> addWidget(rotation_widget_);
rotation_angle_layout -> addWidget(rotation_angle_); rotation_angle_layout -> addWidget(rotation_angle_);
main_layout -> addLayout(rotation_angle_layout); main_layout -> addLayout(rotation_angle_layout);
@ -113,7 +127,8 @@ void TextFieldEditor::updateForm() {
qle_text -> setText(part -> property("text").toString()); qle_text -> setText(part -> property("text").toString());
font_size -> setValue(part -> property("size").toInt()); font_size -> setValue(part -> property("size").toInt());
rotate -> setChecked(!part -> property("rotate").toBool()); rotate -> setChecked(!part -> property("rotate").toBool());
rotation_angle_ -> setValue(part -> property("rotation angle").toInt()); rotation_angle_ -> setValue(part -> property("rotation angle").toDouble());
rotation_widget_ -> setOrientation(part -> property("rotation angle").toDouble());
activeConnections(true); activeConnections(true);
} }

View File

@ -20,6 +20,7 @@
#include <QtGui> #include <QtGui>
#include "elementitemeditor.h" #include "elementitemeditor.h"
class PartTextField; class PartTextField;
class QTextOrientationWidget;
/** /**
Cette classe represente un editeur de champ de texte Cette classe represente un editeur de champ de texte
Elle permet d'editer a travers une interface graphique les Elle permet d'editer a travers une interface graphique les
@ -42,6 +43,7 @@ class TextFieldEditor : public ElementItemEditor {
QSpinBox *font_size; QSpinBox *font_size;
QCheckBox *rotate; QCheckBox *rotate;
QDoubleSpinBox *rotation_angle_; QDoubleSpinBox *rotation_angle_;
QTextOrientationWidget *rotation_widget_;
// methodes // methodes
public slots: public slots:

View File

@ -0,0 +1,349 @@
/*
Copyright 2006-2010 Xavier Guerrin
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 "qtextorientationwidget.h"
/**
Constructeur
Par defaut, ce widget met en valeur les angles multiples de 45 degres
et presente un texte oriente a 0 degre, avec la police par defaut de
l'application. Le texte affiche est
@param parent Widget parent
*/
QTextOrientationWidget::QTextOrientationWidget(QWidget *parent) :
QWidget(parent),
squares_interval_(45.0),
current_orientation_(0.0),
display_text_(true),
must_highlight_angle_(false),
read_only_(false)
{
// chaines par defaut
text_size_hash_.insert(tr("Ex.", "Short example string"), -1);
text_size_hash_.insert(tr("Exemple", "Longer example string"), -1);
// definit la politique de gestion de la taille de ce widget :
// on prefere la sizeHint()
QSizePolicy size_policy(QSizePolicy::Minimum, QSizePolicy::Minimum);
// on souhaite conserver le rapport entre sa hauteur et sa largeur
size_policy.setHeightForWidth(true);
setSizePolicy(size_policy);
// suivi de la souris : permet de recevoir les evenements relatifs aux
// mouvement de la souris sans que l'utilisateur n'ait a cliquer
setMouseTracking(true);
}
/**
Destructeur
*/
QTextOrientationWidget::~QTextOrientationWidget() {
}
/**
@param angle la nouvelle orientation / le nouvel angle selectionne(e)
0 degre correspond a un texte horizontal, de gauche a droite
90 degres correspondent a un texte vertical de haut en bas
*/
void QTextOrientationWidget::setOrientation(const double &angle) {
current_orientation_ = angle;
update();
}
/**
@return l'orientation / l'angle actuellement selectionne(e)
0 degre correspond a un texte horizontal, de gauche a droite
90 degres correspondent a un texte vertical de haut en bas
*/
double QTextOrientationWidget::orientation() const {
return(current_orientation_);
}
/**
Definit la police de caracteres a utiliser pour le texte affiche
@param font Une police de caracteres
*/
void QTextOrientationWidget::setFont(const QFont &font) {
text_font_ = font;
// invalide le cache contenant les longueurs des textes a disposition
foreach(QString text, text_size_hash_.keys()) {
text_size_hash_[text] = -1;
}
}
/**
@return la police utilisee pour le texte affiche
*/
QFont QTextOrientationWidget::font() const {
return(text_font_);
}
/**
@param display_text true pour afficher un texte, false sinon
*/
void QTextOrientationWidget::setDisplayText(bool display_text) {
display_text_ = display_text;
}
/**
@return la police utilisee pour le texte affiche
*/
bool QTextOrientationWidget::textDisplayed() const {
return(display_text_);
}
/**
@param texts_list Une liste de chaines de caracteres utilisables par le
widget afin d'afficher un texte en guise d'exemple. Le widget choisit la
chaine la plus appropriee en fonction de sa taille.
Note : la liste fournie ne doit pas etre vide. Utilisez setDisplayText si
vous ne voulez plus afficher de texte.
*/
void QTextOrientationWidget::setUsableTexts(const QStringList &texts_list) {
if (texts_list.isEmpty()) return;
// on oublie les anciennes chaines
foreach(QString text, text_size_hash_.keys()) {
// il faut oublier les anciennes chaines
if (!texts_list.contains(text)) {
text_size_hash_.remove(text);
}
}
// on ajoute les nouvelles, sans les calculer (on met -1 en guise de longueur)
foreach(QString text, texts_list) {
if (!text_size_hash_.contains(text)) {
text_size_hash_[text] = -1;
}
}
}
/**
@return la liste des chaines dont le widget dispose pour afficher un texte
*/
QStringList QTextOrientationWidget::usableTexts() const {
return(text_size_hash_.keys());
}
/**
@return true si le widget est en mode "lecture seule", false sinon
*/
bool QTextOrientationWidget::isReadOnly() const {
return(read_only_);
}
/**
@param ro true pour passer le widget en mode "lecture seule", false sinon
*/
void QTextOrientationWidget::setReadOnly(bool ro) {
read_only_ = ro;
}
/**
@return la taille recommandee pour ce widget
*/
QSize QTextOrientationWidget::sizeHint() const {
return(QSize(50, 50));
}
/**
@param w une largeur donnee
@return la hauteur preferee pour une largeur donnee
Pour ce widget : retourne la largeur fournie afin de maintenir le widget carre
*/
int QTextOrientationWidget::heightForWidth(int w) const {
return(w);
}
/**
Effectue le rendu du widget
@param event Evenement decrivant la demande de rendu du widget
*/
void QTextOrientationWidget::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
// rectangle de travail avec son centre et son rayon
QRect drawing_rectangle(QPoint(0, 0), size());
drawing_rectangle.adjust(5, 5, -5, -5);
QPointF drawing_rectangle_center(drawing_rectangle.center());
qreal drawing_rectangle_radius = drawing_rectangle.width() / 2.0;
QPainter p;
p.begin(this);
p.setRenderHint(QPainter::Antialiasing, true);
p.setRenderHint(QPainter::TextAntialiasing, true);
// cercle gris a fond jaune
p.setPen(QPen(QBrush(QColor("#9FA8A8")), 2.0));
p.setBrush(QBrush(QColor("#ffffaa")));
p.drawEllipse(drawing_rectangle);
// ligne rouge indiquant l'angle actuel
p.setPen(QPen(QBrush(Qt::red), 1.0));
p.translate(drawing_rectangle_center);
p.rotate(current_orientation_);
p.drawLine(QLineF(QPointF(), QPointF(drawing_rectangle_radius, 0.0)));
// texte optionnel
if (display_text_) {
// determine le texte a afficher
QString chosen_text = getMostUsableStringForRadius(drawing_rectangle_radius);
if (!chosen_text.isEmpty()) {
p.resetTransform();
p.setPen(Qt::black);
p.setFont(text_font_);
p.translate(drawing_rectangle_center);
p.rotate(current_orientation_);
p.drawText(QPoint(), chosen_text);
}
}
// carres verts a fond vert
qreal squares_size = size().width() / 15.0;
qreal square_offset = - squares_size / 2.0;
QRectF square_qrect = QRect(square_offset, square_offset, squares_size, squares_size);
p.setPen(Qt::NoPen);
p.setBrush(QBrush(QColor("#248A34")));
for (double drawing_angle = 0.0 ; drawing_angle < 360.0 ; drawing_angle += squares_interval_) {
if (must_highlight_angle_ && highlight_angle_ == drawing_angle && underMouse()) {
p.setBrush(QBrush(QColor("#43FF5F")));
}
p.resetTransform();
p.translate(drawing_rectangle_center);
p.rotate(drawing_angle);
p.translate(drawing_rectangle_radius - 1.0, 0.0);
p.rotate(-45.0);
p.drawRect(square_qrect);
if (must_highlight_angle_ && highlight_angle_ == drawing_angle) {
p.setBrush(QBrush(QColor("#248A34")));
}
}
p.end();
}
/**
Gere les mouvements de la souris sur ce widget
@param event Evenement decrivant le mouvement de la souris
*/
void QTextOrientationWidget::mouseMoveEvent(QMouseEvent *event) {
if (read_only_) return;
bool drawn_angle_hovered = positionIsASquare(event -> posF(), &highlight_angle_);
if (must_highlight_angle_ != drawn_angle_hovered) {
must_highlight_angle_ = drawn_angle_hovered;
update();
}
}
/**
Gere les relachements de la souris sur ce widget
@param event Evenement decrivant le relachement de la souris
*/
void QTextOrientationWidget::mouseReleaseEvent(QMouseEvent *event) {
if (read_only_) return;
double clicked_angle;
bool drawn_angle_clicked = positionIsASquare(event -> posF(), &clicked_angle);
if (drawn_angle_clicked) {
setOrientation(clicked_angle);
emit(orientationChanged(clicked_angle));
must_highlight_angle_ = false;
update();
}
}
/**
@param radius Rayon du cercle qui limitera le rendu du texte
@return la chaine la plus appropriee en fonction de la taille du widget.
*/
QString QTextOrientationWidget::getMostUsableStringForRadius(const qreal &radius) {
// s'assure que l'on connait la longueur de chaque texte a disposition
generateTextSizeHash();
// recupere les longueurs a disposition
QList<qreal> available_lengths = text_size_hash_.values();
// trie les longueurs par ordre croissant
qSort(available_lengths.begin(), available_lengths.end());
// recherche la position ou l'on insererait le rayon
QList<qreal>::const_iterator i = qUpperBound(available_lengths, radius);
// la valeur precedent cette position est donc celle qui nous interesse
if (i == available_lengths.begin()) {
// nous sommes au debut de la liste - nous ne pouvons donc pas afficher de chaine
return(QString());
} else {
-- i;
qreal final_length = *i;
QString final_string = text_size_hash_.keys(final_length).first();
return(final_string);
}
}
/**
S'assure que le hash associant les textes utilisables a leur taille soit
correctement rempli.
*/
void QTextOrientationWidget::generateTextSizeHash() {
QFontMetrics font_metrics(text_font_);
foreach(QString text, text_size_hash_.keys()) {
if (text_size_hash_[text] == -1) {
text_size_hash_[text] = font_metrics.boundingRect(text).width();
}
}
}
/**
Determine si une position donnee correspond a un des carres representant un
angle pertinent.
@param pos Position donnee
@param angle_value_ptr Si different de 0, le double pointe par ce parametre
vaudra l'angle pertinent concerne
*/
bool QTextOrientationWidget::positionIsASquare(const QPointF &pos, double *angle_value_ptr) {
// rectangle de travail avec son centre et son rayon
QRect drawing_rectangle(QPoint(0, 0), size());
drawing_rectangle.adjust(5, 5, -5, -5);
QPointF drawing_rectangle_center(drawing_rectangle.center());
qreal drawing_rectangle_radius = drawing_rectangle.width() / 2.0;
qreal squares_size = size().width() / 15.0;
qreal square_offset = - squares_size / 2.0;
QRectF square_qrect = QRect(square_offset, square_offset, squares_size, squares_size);
for (double drawing_angle = 0.0 ; drawing_angle < 360.0 ; drawing_angle += squares_interval_) {
QTransform transform = QTransform()
.translate(drawing_rectangle_center.x(), drawing_rectangle_center.y())
.rotate(drawing_angle)
.translate(drawing_rectangle_radius - 1.0, 0.0)
.rotate(-45.0);
QRectF mapped_rectangle = transform.mapRect(square_qrect);
if (mapped_rectangle.contains(pos)) {
if (angle_value_ptr) *angle_value_ptr = drawing_angle;
return(true);
}
}
return(false);
}

View File

@ -0,0 +1,91 @@
/*
Copyright 2006-2010 Xavier Guerrin
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 Q_TEXT_ORIENTATION_WIDGET_H
#define Q_TEXT_ORIENTATION_WIDGET_H
#include <QtGui>
/**
Cette classe permet de representer graphiquement l'orientation d'un texte.
*/
class QTextOrientationWidget : public QWidget {
Q_OBJECT
// constructeurs, destructeur
public:
QTextOrientationWidget(QWidget * = 0);
virtual ~QTextOrientationWidget();
private:
QTextOrientationWidget(const QTextOrientationWidget &);
QTextOrientationWidget &operator=(const QTextOrientationWidget &);
// methodes publiques
public:
double orientation() const;
void setFont(const QFont &);
QFont font() const;
void setDisplayText(bool);
bool textDisplayed() const;
void setUsableTexts(const QStringList &);
QStringList usableTexts() const;
bool isReadOnly() const;
void setReadOnly(bool);
// slots publics
public slots:
void setOrientation(const double &);
protected:
virtual QSize sizeHint () const;
int heightForWidth(int) const;
virtual void paintEvent(QPaintEvent *);
void mouseMoveEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
// signaux
signals:
/**
Signal emis lorsque l'utilisateur specifie une orientation en cliquant
sur le widget
*/
void orientationChanged(double);
// attributs prives
private:
/// Intervalle entre les petits angles privilegies, en degres
double squares_interval_;
/// angle represente
double current_orientation_;
/// Booleen indiquant s'il faut afficher ou non un texte
bool display_text_;
/// Police utilisee pour le texte affiche
QFont text_font_;
/// Hash associant les textes disponible a leur longueur en pixels
QHash<QString, qreal> text_size_hash_;
/// Angle specifique a mettre en valeur
double highlight_angle_;
/// Booleen indiquant s'il faut mettre en valeur un des angles
bool must_highlight_angle_;
/// Booleen indiquant si le widget est en mode "lecture seule" ou non
bool read_only_;
// methodes privees
private:
QString getMostUsableStringForRadius(const qreal &);
void generateTextSizeHash();
bool positionIsASquare(const QPointF &, double * = 0);
};
#endif