diff --git a/aboutqet.cpp b/aboutqet.cpp index 4d731a676..0ce4eb130 100644 --- a/aboutqet.cpp +++ b/aboutqet.cpp @@ -33,7 +33,7 @@ AboutQET::AboutQET(QWidget *parent) : QDialog(parent) { /** @return Le titre QElectroTech avec son icone */ -QWidget *AboutQET::titre() { +QWidget *AboutQET::titre() const { QWidget *icone_et_titre = new QWidget(); // icone QLabel *icone = new QLabel(); @@ -54,7 +54,7 @@ QWidget *AboutQET::titre() { /** @return Le widget contenu par l'onglet « A propos » */ -QWidget *AboutQET::ongletAPropos() { +QWidget *AboutQET::ongletAPropos() const { QLabel *apropos = new QLabel( tr("QElectroTech, une application de r\351alisation de sch\351mas \351lectriques.") + "

" + @@ -72,7 +72,7 @@ QWidget *AboutQET::ongletAPropos() { /** @return Le widget contenu par l'onglet « Auteurs » */ -QWidget *AboutQET::ongletAuteurs() { +QWidget *AboutQET::ongletAuteurs() const { QLabel *auteurs = new QLabel( "" + tr("Id\351e originale") + @@ -95,7 +95,7 @@ QWidget *AboutQET::ongletAuteurs() { /** @return Le widget contenu par l'onglet « Accord de Licence » */ -QWidget *AboutQET::ongletLicence() { +QWidget *AboutQET::ongletLicence() const { QWidget *licence = new QWidget(); // label QLabel *titre_licence = new QLabel(tr("Ce programme est sous licence GNU/GPL.")); diff --git a/aboutqet.h b/aboutqet.h index bc5123ca7..307c38895 100644 --- a/aboutqet.h +++ b/aboutqet.h @@ -10,9 +10,9 @@ public: AboutQET(QWidget * = 0); private: - QWidget *titre(); - QWidget *ongletAPropos(); - QWidget *ongletAuteurs(); - QWidget *ongletLicence(); + QWidget *titre() const; + QWidget *ongletAPropos() const; + QWidget *ongletAuteurs() const; + QWidget *ongletLicence() const; }; #endif diff --git a/borne.cpp b/borne.cpp index 404e6ebee..9a708de6a 100644 --- a/borne.cpp +++ b/borne.cpp @@ -97,7 +97,7 @@ Borne::Orientation Borne::orientation() const { Borne::Orientation ori_def = elt -> defaultOrientation(); if (ori_cur == ori_def) return(sens); else { - /* calcul l'angle de rotation implique par l'orientation de l'element parent */ + // calcul l'angle de rotation implique par l'orientation de l'element parent // angle de rotation de la borne sur la scene, divise par 90 int angle = ori_cur - ori_def + sens; while (angle >= 4) angle -= 4; @@ -361,7 +361,7 @@ QList Borne::conducteurs() const { @param doc Le Document XML a utiliser pour creer l'element XML @return un QDomElement representant cette borne */ -QDomElement Borne::toXml(QDomDocument &doc) { +QDomElement Borne::toXml(QDomDocument &doc) const { QDomElement qdo = doc.createElement("borne"); qdo.setAttribute("x", amarrage_elmt.x()); qdo.setAttribute("y", amarrage_elmt.y()); diff --git a/borne.h b/borne.h index a974fda86..c5c33551f 100644 --- a/borne.h +++ b/borne.h @@ -34,7 +34,7 @@ // methodes de manipulation des conducteurs lies a cette borne bool addConducteur(Conducteur *); void removeConducteur(Conducteur *); - inline int nbConducteurs() { return(liste_conducteurs.size()); } + inline int nbConducteurs() const { return(liste_conducteurs.size()); } // methodes de lecture QList conducteurs() const; @@ -45,8 +45,9 @@ // methodes relatives a l'import/export au format XML static bool valideXml(QDomElement &); bool fromXml (QDomElement &); - QDomElement toXml (QDomDocument &); + QDomElement toXml (QDomDocument &) const; + protected: // methodes de gestion des evenements void hoverEnterEvent (QGraphicsSceneHoverEvent *); void hoverMoveEvent (QGraphicsSceneHoverEvent *); diff --git a/element.cpp b/element.cpp index cfc86854f..33efe9f86 100644 --- a/element.cpp +++ b/element.cpp @@ -62,8 +62,8 @@ QPoint Element::setHotspot(QPoint hs) { if (dimensions.isNull()) hotspot_coord = QPoint(0, 0); else { // les coordonnees indiquees ne doivent pas depasser les dimensions de l'element - int hsx = hs.x() > dimensions.width() ? dimensions.width() : hs.x(); - int hsy = hs.y() > dimensions.height() ? dimensions.height() : hs.y(); + int hsx = qMin(hs.x(), dimensions.width()); + int hsy = qMin(hs.y(), dimensions.height()); hotspot_coord = QPoint(hsx, hsy); } return(hotspot_coord); diff --git a/exportdialog.cpp b/exportdialog.cpp new file mode 100644 index 000000000..76b80de82 --- /dev/null +++ b/exportdialog.cpp @@ -0,0 +1,210 @@ +#include "exportdialog.h" + +/** + Constructeur + @param schema Le schema a exporter + @param parent Le Widget parent de ce dialogue +*/ +ExportDialog::ExportDialog(Schema &schema, QWidget *parent) : QDialog(parent) { + // recupere le schema a exporter, sa taille et ses proportions + schema_schema = &schema; + schema_size = schema_schema -> imageSize(); + schema_ratio = (qreal)schema_size.width() / (qreal)schema_size.height(); + + // la taille du dialogue est fixee + setFixedSize(400, 310); + setWindowTitle(tr("Exporter")); + + // le dialogue est un empilement vertical d'elements + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + + /* le dialogue comprend une ligne permettant d'indiquer un chemin de fichier (hboxLayout) */ + QHBoxLayout *hboxLayout = new QHBoxLayout(); + hboxLayout -> addWidget(new QLabel(tr("Nom de fichier :"), this)); + hboxLayout -> addWidget(filename = new QLineEdit(this)); + hboxLayout -> addWidget(button_browse = new QPushButton(tr("Parcourir"), this)); + + vboxLayout -> addLayout(hboxLayout); + + /* une ligne permettant de choisir le format (hboxLayout1) */ + QHBoxLayout *hboxLayout1 = new QHBoxLayout(); + hboxLayout1 -> addWidget(new QLabel(tr("Format :"), this)); + hboxLayout1 -> addWidget(format = new QComboBox(this)); + format -> addItem(tr("PNG (*.png)"), "PNG"); + format -> addItem(tr("JPEG (*.jpg)"), "JPG"); + format -> addItem(tr("Bitmap (*.bmp)"), "BMP"); + + vboxLayout -> addLayout(hboxLayout1); + + /* un cadre permettant de specifier les dimensions de l'image finale */ + vboxLayout -> addWidget(setupDimensionsGroupBox()); + + /* un cadre permettant de specifier les options de l'image finale */ + vboxLayout -> addWidget(setupOptionsGroupBox()); + + /* et deux boutons */ + buttons = new QDialogButtonBox(this); + buttons -> setOrientation(Qt::Horizontal); + buttons -> setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Save); + + vboxLayout -> addWidget(buttons); + + // ordre des input selectionnes avec la touche tab + setTabOrder(filename, button_browse); + setTabOrder(button_browse, format); + setTabOrder(format, width); + setTabOrder(width, height); + setTabOrder(height, keep_aspect_ratio); + setTabOrder(keep_aspect_ratio, buttons); + + // connexions signaux/slots + connect(button_browse, SIGNAL(released()), this, SLOT(slot_chooseAFile())); + connect(width, SIGNAL(valueChanged(int)), this, SLOT(slot_correctHeight())); + connect(keep_aspect_ratio, SIGNAL(stateChanged(int)), this, SLOT(slot_correctHeight())); + connect(height, SIGNAL(valueChanged(int)), this, SLOT(slot_correctWidth())); + connect(buttons, SIGNAL(accepted()), this, SLOT(slot_check())); + connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); +} + +/** + Met en place la partie du dialogue dans lequel l'utilisateur entre les + dimensions souhaitees de l'image. + @return La QGroupBox permettant de regler les dimensions de l'image +*/ +QGroupBox *ExportDialog::setupDimensionsGroupBox() { + QGroupBox *groupbox_dimensions = new QGroupBox(tr("Dimensions"), this); + QGridLayout *gridLayout = new QGridLayout(groupbox_dimensions); + + // hauteur + gridLayout -> addWidget(new QLabel(tr("Hauteur :"), groupbox_dimensions), 2, 0, 1, 1); + + width = new QSpinBox(groupbox_dimensions); + width -> setRange(1, 10000); + width -> setValue(schema_size.width()); + gridLayout -> addWidget(width, 0, 1, 1, 1); + + gridLayout -> addWidget(new QLabel(tr("px"), groupbox_dimensions), 0, 2, 1, 1); + + // largeur + gridLayout -> addWidget(new QLabel(tr("Largeur :"), groupbox_dimensions), 0, 0, 1, 1); + + height = new QSpinBox(groupbox_dimensions); + height -> setRange(1, 10000); + height -> setValue(schema_size.height()); + gridLayout -> addWidget(height, 2, 1, 1, 1); + + gridLayout -> addWidget(new QLabel(tr("px"), groupbox_dimensions), 2, 2, 1, 1); + + // conserver les proportions + keep_aspect_ratio = new QCheckBox(tr("Conserver les proportions"), groupbox_dimensions); + keep_aspect_ratio -> setChecked(true); + + gridLayout -> addWidget(keep_aspect_ratio, 1, 3, 1, 1); + + return(groupbox_dimensions); +} + +/** + Met en place la partie du dialogue dans lequel l'utilisateur entre les + options souhaitees de l'image. + @return La QGroupBox permettant de regler les options de l'image +*/ +QGroupBox *ExportDialog::setupOptionsGroupBox() { + QGroupBox *groupbox_options = new QGroupBox(tr("Options"), this); + QHBoxLayout *optionshlayout = new QHBoxLayout(groupbox_options); + + // exporter la grille + export_grid = new QCheckBox(tr("Exporter la grille"), groupbox_options); + optionshlayout -> addWidget(export_grid); + + // Conserver les couleurs + keep_colors = new QCheckBox(tr("Conserver les couleurs"), groupbox_options); + optionshlayout -> addWidget(keep_colors); + + return(groupbox_options); +} + +void ExportDialog::slot_correctWidth() { + if (!keep_aspect_ratio -> isChecked() || dontchangewidth) return; + dontchangeheight = true; + width -> setValue(qRound(height -> value() * schema_ratio)); + dontchangeheight = false; +} + +void ExportDialog::slot_correctHeight() { + if (!keep_aspect_ratio -> isChecked() || dontchangeheight) return; + dontchangewidth = true; + height -> setValue(qRound(width -> value() / schema_ratio)); + dontchangewidth = false; +} + +void ExportDialog::slot_chooseAFile() { + QString user_file = QFileDialog::getSaveFileName( + this, + tr("Exporter vers le fichier"), + QDir::homePath(), + tr("Images (*.png *.bmp *.jpg)") + ); + if (user_file != "") { + schema_path = user_file; + filename -> setText(schema_path); + } +} + +void ExportDialog::slot_check() { + + // verifie que le fichier a ete specifie + if (schema_path == "") { + QMessageBox::information( + this, + tr("Fichier non sp\351cifi\351"), + tr("Vous devez sp\351cifier le chemin du fichier dans lequel sera enregistr\351e l'image."), + QMessageBox::Ok + ); + return; + } + + // recupere le format a utiliser (acronyme et extension) + QString format_acronym = format -> itemData(format -> currentIndex()).toString(); + QString format_extension = "." + format_acronym.toLower(); + + // corrige l'extension du fichier + if (!schema_path.endsWith(format_extension, Qt::CaseInsensitive)) schema_path += format_extension; + + // recupere des informations sur le fichier specifie + QFileInfo file_infos(schema_path); + + // verifie qu'il est possible d'ecrire dans le fichier en question + if (file_infos.exists() && !file_infos.isWritable()) { + QMessageBox::critical( + this, + tr("Impossible d'\351crire dans ce fichier"), + tr("Il semblerait que vous n'ayez pas les permissions n\351cessaires pour \351crire dans ce fichier.."), + QMessageBox::Ok + ); + return; + } + + // ouvre le fichier + QFile fichier(schema_path); + + // genere l'image + if (!export_grid -> isChecked()) schema_schema -> setAffichageGrille(false); + QImage image = schema_schema -> toImage(width -> value(), height -> value(), keep_aspect_ratio -> isChecked()); + if (!export_grid -> isChecked()) schema_schema -> setAffichageGrille(true); + + // convertit l'image en niveaux de gris si besoin + if (!keep_colors -> isChecked()) { + QVector ColorTab; + for (int i = 0 ; i < 256 ; ++ i) ColorTab << qRgb(i, i, i); + image = image.convertToFormat(QImage::Format_Indexed8, ColorTab, Qt::ThresholdDither); + } + + // enregistre l'image dans le fichier + image.save(&fichier, format_acronym.toUtf8().data()); + fichier.close(); + + // fermeture du dialogue + accept(); +} + diff --git a/exportdialog.h b/exportdialog.h new file mode 100644 index 000000000..5f6903a60 --- /dev/null +++ b/exportdialog.h @@ -0,0 +1,45 @@ +#ifndef EXPORTDIALOG_H + #define EXPORTDIALOG_H + #include + #include "schema.h" + /** + Cette classe represente le dialogue permettant d'exporter un schema + sous forme d'image selon les desirs de l'utilisateur + */ + class ExportDialog : public QDialog { + Q_OBJECT + public: + ExportDialog(Schema &, QWidget * = 0); + + private: + // elements graphiques + QLineEdit *filename; + QPushButton *button_browse; + QComboBox *format; + QSpinBox *width; + QSpinBox *height; + QCheckBox *keep_aspect_ratio; + QCheckBox *export_grid; + QCheckBox *keep_colors; + QDialogButtonBox *buttons; + + // booleens pour ne pas avoir de boucle lors de l'edition des dimensions de l'image + bool dontchangewidth; + bool dontchangeheight; + + // elements relatifs au traitement effectue par le dialogue + Schema *schema_schema; + QSize schema_size; + QString schema_path; + qreal schema_ratio; + + QGroupBox *setupDimensionsGroupBox(); + QGroupBox *setupOptionsGroupBox(); + + public slots: + void slot_correctWidth(); + void slot_correctHeight(); + void slot_chooseAFile(); + void slot_check(); + }; +#endif diff --git a/qelectrotech.pro b/qelectrotech.pro index 9b09f172a..51d15e759 100644 --- a/qelectrotech.pro +++ b/qelectrotech.pro @@ -4,7 +4,7 @@ TEMPLATE = app TARGET = -DEPENDPATH += . +DEPENDPATH += . lang INCLUDEPATH += . # Input @@ -14,6 +14,7 @@ HEADERS += aboutqet.h \ element.h \ elementfixe.h \ elementperso.h \ + exportdialog.h \ panelappareils.h \ qetapp.h \ schema.h \ @@ -24,12 +25,13 @@ SOURCES += aboutqet.cpp \ element.cpp \ elementfixe.cpp \ elementperso.cpp \ + exportdialog.cpp \ main.cpp \ panelappareils.cpp \ qetapp.cpp \ schema.cpp \ schemavue.cpp RESOURCES += qelectrotech.qrc -TRANSLATIONS += qet_en.ts +TRANSLATIONS += lang/qet_en.ts lang/qt_fr.ts QT += xml diff --git a/qetapp.cpp b/qetapp.cpp index f695457ac..8daabe3b7 100644 --- a/qetapp.cpp +++ b/qetapp.cpp @@ -3,6 +3,7 @@ #include "schema.h" #include "panelappareils.h" #include "aboutqet.h" +#include "exportdialog.h" /** constructeur @@ -515,13 +516,12 @@ void QETApp::dialogue_imprimer() { Gere l'export de schema vers un autre format (PNG pour le moment) */ void QETApp::dialogue_exporter() { + Schema *sc = schemaEnCours() -> scene; + ExportDialog ed(*sc); + ed.exec(); + /* // demande un nom de fichier - QString nom_fichier = QFileDialog::getSaveFileName( - this, - tr("Exporter vers le fichier"), - QDir::homePath(), - tr("Image PNG (*.png)") - ); + // exporte le schema if (nom_fichier != "") { if (!nom_fichier.endsWith(".png", Qt::CaseInsensitive)) nom_fichier += ".png"; @@ -533,6 +533,7 @@ void QETApp::dialogue_exporter() { image.save(&fichier, "PNG"); fichier.close(); } + */ } /** diff --git a/schema.cpp b/schema.cpp index 231433daa..df925e1d1 100644 --- a/schema.cpp +++ b/schema.cpp @@ -67,16 +67,26 @@ void Schema::drawBackground(QPainter *p, const QRectF &r) { Exporte le schema vers une image @return Une QImage representant le schema */ -QImage Schema::toImage() { - QRectF vue = itemsBoundingRect(); - // la marge = 5 % de la longueur necessaire - qreal marge = 0.05 * vue.width(); - vue.translate(-marge, -marge); - vue.setWidth(vue.width() + 2.0 * marge); - vue.setHeight(vue.height() + 2.0 * marge); - QSize dimensions_image = vue.size().toSize(); +QImage Schema::toImage(int width, int height, bool respectRatio) { + // determine le contenu du schema + QRectF schema_content = itemsBoundingRect(); - QImage pix = QImage(dimensions_image, QImage::Format_RGB32); + // calcule la marge = 5 % de la longueur necessaire + qreal margin = 0.05 * schema_content.width(); + + // en deduit la zone source utilisee pour l'image + QRectF source_area = schema_content; + source_area.translate(-margin, -margin); + source_area.setWidth(schema_content.width() + 2.0 * margin); + source_area.setHeight(schema_content.height() + 2.0 * margin); + + // si les dimensions ne sont pas precisees, l'image est exportee a l'echelle 1:1 + QSize image_size = (width == -1 && height == -1) ? source_area.size().toSize() : QSize(width, height); + + // initialise une image avec ces dimensions + QImage pix = QImage(image_size, QImage::Format_RGB32); + + // prepare le rendu QPainter p; bool painter_ok = p.begin(&pix); if (!painter_ok) return(QImage()); @@ -86,11 +96,41 @@ QImage Schema::toImage() { p.setRenderHint(QPainter::TextAntialiasing, true); p.setRenderHint(QPainter::SmoothPixmapTransform, true); - render(&p, pix.rect(), vue, Qt::KeepAspectRatio); + // deselectionne tous les elements + QList selected_elmts = selectedItems(); + foreach (QGraphicsItem *qgi, selected_elmts) qgi -> setSelected(false); + + // effectue le rendu lui-meme + render(&p, pix.rect(), source_area, respectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio); p.end(); + + // restaure les elements selectionnes + foreach (QGraphicsItem *qgi, selected_elmts) qgi -> setSelected(true); + return(pix); } +/** + Permet de connaitre les dimensions qu'aura l'image generee par la methode toImage() + @return La taille de l'image generee par toImage() +*/ +QSize Schema::imageSize() const { + // determine le contenu du schema + QRectF schema_content = itemsBoundingRect(); + + // calcule la marge = 5 % de la longueur necessaire + qreal margin = 0.05 * schema_content.width(); + + // en deduit la zone source utilisee pour l'image + QRectF source_area = schema_content; + source_area.translate(-margin, -margin); + source_area.setWidth(schema_content.width() + 2.0 * margin); + source_area.setHeight(schema_content.height() + 2.0 * margin); + + // renvoie la taille de la zone source + return(source_area.size().toSize()); +} + /** Exporte tout ou partie du schema @param schema Booleen (a vrai par defaut) indiquant si le XML genere doit diff --git a/schema.h b/schema.h index c6ea13e58..b7a218a7e 100644 --- a/schema.h +++ b/schema.h @@ -21,7 +21,8 @@ } inline void setDepart (QPointF d) { poseur_de_conducteur -> setLine(QLineF(d, poseur_de_conducteur -> line().p2())); } inline void setArrivee(QPointF a) { poseur_de_conducteur -> setLine(QLineF(poseur_de_conducteur -> line().p1(), a)); } - QImage toImage(); + QImage toImage(int = -1, int = -1, bool = true); + QSize imageSize() const; QDomDocument toXml(bool = true); bool fromXml(QDomDocument &, QPointF = QPointF()); QGraphicsItem *getElementById(uint id);