Engauge Digitizer  2
DlgEditPointAxis.cpp
Go to the documentation of this file.
1 /******************************************************************************************************
2  * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3  * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4  * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5  ******************************************************************************************************/
6 
7 #include "DlgEditPointAxis.h"
8 #include "DlgValidatorAbstract.h"
9 #include "DlgValidatorFactory.h"
11 #include "DocumentModelCoords.h"
12 #include "DocumentModelGeneral.h"
13 #include "EngaugeAssert.h"
14 #include "FormatCoordsUnits.h"
15 #include "FormatDateTime.h"
18 #include "Logger.h"
19 #include "MainWindow.h"
20 #include "MainWindowModel.h"
21 #include <QDoubleValidator>
22 #include <QGridLayout>
23 #include <QGroupBox>
24 #include <QHBoxLayout>
25 #include <QLabel>
26 #include <QLineEdit>
27 #include <QRect>
28 #include "QtToString.h"
29 #include <QVBoxLayout>
30 #include "Transformation.h"
31 
32 const Qt::Alignment ALIGNMENT = Qt::AlignCenter;
33 
35 
36 const bool IS_X_THETA = true;
37 const bool IS_NOT_X_THETA = false;
38 
40  const DocumentModelCoords &modelCoords,
41  const DocumentModelGeneral &modelGeneral,
42  const MainWindowModel &modelMainWindow,
43  const Transformation &transformation,
44  DocumentAxesPointsRequired documentAxesPointsRequired,
45  bool isXOnly,
46  const double *xInitialValue,
47  const double *yInitialValue) :
48  QDialog (&mainWindow),
49  m_documentAxesPointsRequired (documentAxesPointsRequired),
50  m_modelCoords (modelCoords),
51  m_modelGeneral (modelGeneral),
52  m_modelMainWindow (modelMainWindow)
53 {
54  LOG4CPP_INFO_S ((*mainCat)) << "DlgEditPointAxis::DlgEditPointAxis";
55 
56  // Either one or two coordinates are desired
57  bool isX = (documentAxesPointsRequired == DOCUMENT_AXES_POINTS_REQUIRED_3) || isXOnly;
58  bool isY = (documentAxesPointsRequired == DOCUMENT_AXES_POINTS_REQUIRED_3) || !isXOnly;
59 
60  QVBoxLayout *layout = new QVBoxLayout;
61  setLayout (layout);
62 
63  setCursor (QCursor (Qt::ArrowCursor));
64  setModal(true);
65  setWindowTitle (tr ("Edit Axis Point"));
66 
67  createCoords (layout);
68  createHints (layout,
69  documentAxesPointsRequired);
70  createOkCancel (layout);
71 
72  initializeGraphCoordinates (xInitialValue,
73  yInitialValue,
74  transformation,
75  isX,
76  isY);
77 
78  updateControls ();
79 }
80 
82 {
83  LOG4CPP_INFO_S ((*mainCat)) << "DlgEditPointAxis::~DlgEditPointAxis";
84 }
85 
86 void DlgEditPointAxis::createCoords (QVBoxLayout *layoutOuter)
87 {
88  // Constraints on x and y are needed for log scaling
89  bool isConstraintX = (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG);
90  bool isConstraintY = (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG);
91  DlgValidatorFactory dlgValidatorFactory;
92  m_validatorGraphX = dlgValidatorFactory.createCartesianOrPolarWithPolarPolar (m_modelCoords.coordScaleXTheta(),
93  isCartesian (),
94  m_modelCoords.coordUnitsX(),
95  m_modelCoords.coordUnitsTheta(),
96  m_modelCoords.coordUnitsDate(),
97  m_modelCoords.coordUnitsTime(),
98  m_modelMainWindow.locale());
99  m_validatorGraphY = dlgValidatorFactory.createCartesianOrPolarWithNonPolarPolar (m_modelCoords.coordScaleYRadius(),
100  isCartesian (),
101  m_modelCoords.coordUnitsY(),
102  m_modelCoords.coordUnitsRadius(),
103  m_modelCoords.coordUnitsDate(),
104  m_modelCoords.coordUnitsTime(),
105  m_modelMainWindow.locale());
106 
107  // Label, with guidance in terms of legal ranges and units
108  QString description = QString ("%1 (%2, %3)%4%5%6%7%8%9 %10 (%11, %12):")
109  .arg (tr ("Graph Coordinates"))
110  .arg (nameXTheta ())
111  .arg (nameYRadius ())
112  .arg (isConstraintX || isConstraintY ? " with " : "")
113  .arg (isConstraintX ? QString (nameXTheta ()) : "")
114  .arg (isConstraintX ? " > 0" : "")
115  .arg (isConstraintX && isConstraintY ? " and " : "")
116  .arg ( isConstraintY ? QString (nameYRadius ()) : "")
117  .arg ( isConstraintY ? " > 0" : "")
118  .arg (tr ("as"))
119  .arg (unitsType (IS_X_THETA))
120  .arg (unitsType (IS_NOT_X_THETA));
121  QGroupBox *panel = new QGroupBox (description, this);
122  layoutOuter->addWidget (panel);
123 
124  QHBoxLayout *layout = new QHBoxLayout (panel);
125  panel->setLayout (layout);
126 
127  // Row
128  QLabel *labelGraphParLeft = new QLabel (tr ("("), this);
129  layout->addWidget(labelGraphParLeft, 0);
130 
131  m_editGraphX = new QLineEdit;
132  m_editGraphX->setMinimumWidth(MIN_WIDTH_TO_FIT_STRANGE_UNITS);
133  m_editGraphX->setAlignment (ALIGNMENT);
134  m_editGraphX->setValidator (m_validatorGraphX);
135  // setStatusTip does not work for modal dialogs
136  m_editGraphX->setWhatsThis (tr ("Enter the first graph coordinate of the axis point.\n\n"
137  "For cartesian plots this is X. For polar plots this is the angle Theta.\n\n"
138  "The expected format of the coordinate value is determined by the locale setting. If "
139  "typed values are not recognized as expected, check the locale setting in Settings / Main Window..."));
140  layout->addWidget(m_editGraphX, 0);
141  connect (m_editGraphX, SIGNAL (textChanged (const QString &)), this, SLOT (slotTextChanged (const QString &)));
142 
143  QLabel *labelGraphComma = new QLabel (tr (", "), this);
144  layout->addWidget(labelGraphComma, 0);
145 
146  m_editGraphY = new QLineEdit;
147  m_editGraphY->setMinimumWidth(MIN_WIDTH_TO_FIT_STRANGE_UNITS);
148  m_editGraphY->setAlignment (ALIGNMENT);
149  m_editGraphY->setValidator (m_validatorGraphY);
150  // setStatusTip does not work for modal dialogs
151  m_editGraphY->setWhatsThis (tr ("Enter the second graph coordinate of the axis point.\n\n"
152  "For cartesian plots this is Y. For polar plots this is the radius R.\n\n"
153  "The expected format of the coordinate value is determined by the locale setting. If "
154  "typed values are not recognized as expected, check the locale setting in Settings / Main Window..."));
155  layout->addWidget(m_editGraphY, 0);
156  connect (m_editGraphY, SIGNAL (textChanged (const QString &)), this, SLOT (slotTextChanged (const QString &)));
157 
158  QLabel *labelGraphParRight = new QLabel (tr (")"), this);
159  layout->addWidget(labelGraphParRight, 0);
160 }
161 
162 void DlgEditPointAxis::createHints (QVBoxLayout *layoutOuter,
163  DocumentAxesPointsRequired documentAxesPointsRequired)
164 {
165  // Insert:
166  // 1) a hint to advertise that axes with only one coordinate can be handled
167  // 2) a hint explaining why decimal points may not be accepted. Very confusing for user to figure out the problem at first, and
168  // then figure out which setting should change to fix it. The hint is centered so it is slightly less intrusive
169 
170  const int MIN_EDIT_WIDTH = 180;
171 
172  QWidget *widget = new QWidget;
173  layoutOuter->addWidget (widget, 0, Qt::AlignCenter);
174 
175  QGridLayout *layout = new QGridLayout;
176  widget->setLayout (layout);
177  int row = 0;
178 
179  // Hint 1
180  QLabel *labelNumberCoordinates = new QLabel (tr ("Number of coordinates per axis point:"));
181  layout->addWidget (labelNumberCoordinates, row, 0, 1, 1);
182  QLineEdit *editNumberCoordinates = new QLineEdit;
183  editNumberCoordinates->setWhatsThis (tr ("Three axis points with two coordinates each are normally used. "
184  "If each axis point has only one known coordinate, then start over "
185  "with File / Import (Advanced) / 4 Axis Points."));
186  editNumberCoordinates->setReadOnly (true);
187  editNumberCoordinates->setText (documentAxesPointsRequired == DOCUMENT_AXES_POINTS_REQUIRED_3 ?
188  "2" :
189  "1");
190  editNumberCoordinates->setMinimumWidth (MIN_EDIT_WIDTH);
191  layout->addWidget (editNumberCoordinates, row++, 1, 1, 1);
192 
193  // Hint 2
194  QLabel *labelLocale = new QLabel (tr ("Number format:"));
195  layout->addWidget (labelLocale, row, 0, 1, 1);
196  QLineEdit *editLocale = new QLineEdit;
197  editLocale->setWhatsThis (tr ("Locale which determines the allowed number formats. This is set by Settings / Main Window."));
198  editLocale->setReadOnly (true);
199  QString locale = QLocaleToString (m_modelMainWindow.locale ());
200  editLocale->setText (locale);
201  editLocale->setMinimumWidth (MIN_EDIT_WIDTH);
202  layout->addWidget (editLocale, row++, 1, 1, 1);
203 }
204 
205 void DlgEditPointAxis::createOkCancel (QVBoxLayout *layoutOuter)
206 {
207  QWidget *panel = new QWidget (this);
208  layoutOuter->addWidget (panel, 0, Qt::AlignCenter);
209 
210  QHBoxLayout *layout = new QHBoxLayout (panel);
211  panel->setLayout (layout);
212 
213  m_btnOk = new QPushButton (tr ("Ok"), this);
214  layout->addWidget(m_btnOk);
215  connect (m_btnOk, SIGNAL (released ()), this, SLOT (accept ()));
216 
217  m_btnCancel = new QPushButton (tr ("Cancel"), this);
218  layout->addWidget(m_btnCancel);
219  connect (m_btnCancel, SIGNAL (released ()), this, SLOT (reject ()));
220 }
221 
222 void DlgEditPointAxis::initializeGraphCoordinates (const double *xInitialValue,
223  const double *yInitialValue,
224  const Transformation &transformation,
225  bool isX,
226  bool isY)
227 {
228  LOG4CPP_INFO_S ((*mainCat)) << "DlgEditPointAxis::initializeGraphCoordinates";
229 
230  QString xTheta, yRadius;
231  if ((xInitialValue != nullptr) &&
232  (yInitialValue != nullptr)) {
233 
234  FormatCoordsUnits format;
235  format.unformattedToFormatted (*xInitialValue,
236  *yInitialValue,
237  m_modelCoords,
238  m_modelGeneral,
239  m_modelMainWindow,
240  xTheta,
241  yRadius,
242  transformation);
243  }
244 
245  if (isX) {
246  m_editGraphX->setText (xTheta);
247  } else {
248  m_editGraphX->setText ("");
249  }
250 
251  if (isY) {
252  m_editGraphY->setText (yRadius);
253  } else {
254  m_editGraphY->setText ("");
255  }
256 }
257 
258 bool DlgEditPointAxis::isCartesian () const
259 {
260  return (m_modelCoords.coordsType() == COORDS_TYPE_CARTESIAN);
261 }
262 
263 QChar DlgEditPointAxis::nameXTheta () const
264 {
265  return (isCartesian () ? QChar ('X') : THETA);
266 }
267 
268 QChar DlgEditPointAxis::nameYRadius () const
269 {
270  return (isCartesian () ? QChar ('Y') : QChar ('R'));
271 }
272 
273 QPointF DlgEditPointAxis::posGraph (bool &isXOnly) const
274 {
275  double xTheta, yRadius;
276 
277  FormatCoordsUnits format;
278 
279  format.formattedToUnformatted (m_editGraphX->text(),
280  m_editGraphY->text(),
281  m_modelCoords,
282  m_modelMainWindow,
283  xTheta,
284  yRadius);
285 
286  // If yRadius value is empty then this is the xTheta value only
287  isXOnly = m_editGraphY->text().isEmpty();
288 
289  return QPointF (xTheta,
290  yRadius);
291 }
292 
293 void DlgEditPointAxis::slotTextChanged (const QString &)
294 {
295  updateControls ();
296 }
297 
298 QString DlgEditPointAxis::unitsType (bool isXTheta) const
299 {
300  if (isCartesian ()) {
301  if (isXTheta) {
302  return coordUnitsNonPolarThetaToBriefType (m_modelCoords.coordUnitsX());
303  } else {
304  return coordUnitsNonPolarThetaToBriefType (m_modelCoords.coordUnitsY());
305  }
306  } else {
307  if (isXTheta) {
308  return coordUnitsPolarThetaToBriefType (m_modelCoords.coordUnitsTheta());
309  } else {
310  return coordUnitsNonPolarThetaToBriefType (m_modelCoords.coordUnitsRadius());
311  }
312  }
313 }
314 
315 void DlgEditPointAxis::updateControls ()
316 {
317  QString textX = m_editGraphX->text();
318  QString textY = m_editGraphY->text();
319 
320  int posX, posY;
321 
322  if (m_documentAxesPointsRequired == DOCUMENT_AXES_POINTS_REQUIRED_4) {
323 
324  bool gotX = (!textX.isEmpty() &&
325  (m_validatorGraphX->validate(textX, posX) == QValidator::Acceptable));
326  bool gotY = (!textY.isEmpty() &&
327  (m_validatorGraphY->validate(textY, posY) == QValidator::Acceptable));
328 
329  // Check for not empty in one coordinate and for valid number in the other coordinate
330  m_btnOk->setEnabled ((textX.isEmpty() && gotY) ||
331  (textY.isEmpty() && gotX));
332 
333  // Emphasize that only one coordinate is to be filled in at a time
334  m_editGraphX->setEnabled (!gotY);
335  m_editGraphY->setEnabled (!gotX);
336 
337  } else {
338 
339  // Check for not empty (which allows single minus sign) and for valid number (which prevents single minus sign)
340  m_btnOk->setEnabled (!textX.isEmpty () &&
341  !textY.isEmpty () &&
342  (m_validatorGraphX->validate(textX, posX) == QValidator::Acceptable) &&
343  (m_validatorGraphY->validate(textY, posY) == QValidator::Acceptable));
344 
345  }
346 }
Model for DlgSettingsGeneral and CmdSettingsGeneral.
CoordUnitsNonPolarTheta coordUnitsRadius() const
Get method for radius units.
void formattedToUnformatted(const QString &xThetaFormatted, const QString &yRadiusFormatted, const DocumentModelCoords &modelCoords, const MainWindowModel &mainWindowModel, double &xThetaUnformatted, double &yRadiusUnformatted) const
Convert formatted string to unformatted numeric value.
CoordUnitsNonPolarTheta coordUnitsY() const
Get method for x units.
virtual QValidator::State validate(QString &input, int &pos) const =0
Validate according to the numeric format specific to the leaf class.
#define LOG4CPP_INFO_S(logger)
Definition: convenience.h:18
CoordUnitsPolarTheta coordUnitsTheta() const
Get method for theta unit.
const bool IS_NOT_X_THETA
const bool IS_X_THETA
DlgEditPointAxis(MainWindow &mainWindow, const DocumentModelCoords &modelCoords, const DocumentModelGeneral &modelGeneral, const MainWindowModel &modelMainWindow, const Transformation &transformation, DocumentAxesPointsRequired documentAxesPointsRequired, bool isXOnly=false, const double *xInitialValue=0, const double *yInitialValue=0)
Constructor for existing point which already has graph coordinates (which may be changed using this d...
QPointF posGraph(bool &isXOnly) const
Return the graph coordinates position specified by the user. Only applies if dialog was accepted...
void unformattedToFormatted(double xThetaUnformatted, double yRadiusUnformatted, const DocumentModelCoords &modelCoords, const DocumentModelGeneral &modelGeneral, const MainWindowModel &mainWindowModel, QString &xThetaFormatted, QString &yRadiusFormatted, const Transformation &transformation) const
Convert unformatted numeric value to formatted string. Transformation is used to determine best resol...
const int MIN_EDIT_WIDTH
Affine transformation between screen and graph coordinates, based on digitized axis points...
CoordUnitsTime coordUnitsTime() const
Get method for time format when used.
Model for DlgSettingsMainWindow.
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
CoordUnitsDate coordUnitsDate() const
Get method for date format when used.
Model for DlgSettingsCoords and CmdSettingsCoords.
const QChar THETA
const int MIN_WIDTH_TO_FIT_STRANGE_UNITS
const Qt::Alignment ALIGNMENT
Highest-level wrapper around other Formats classes.
CoordScale coordScaleYRadius() const
Get method for linear/log scale on y/radius.
QLocale locale() const
Get method for locale.
log4cpp::Category * mainCat
Definition: Logger.cpp:14
DlgValidatorAbstract * createCartesianOrPolarWithNonPolarPolar(CoordScale coordScale, bool isCartesian, CoordUnitsNonPolarTheta coordUnitsCartesian, CoordUnitsNonPolarTheta coordUnitsPolar, CoordUnitsDate coordUnitsDate, CoordUnitsTime coordUnitsTime, const QLocale &locale) const
Factory method for generating validators for either cartesian or polar case, when polar format is spe...
CoordsType coordsType() const
Get method for coordinates type.
QString coordUnitsPolarThetaToBriefType(CoordUnitsPolarTheta coordUnits)
QString coordUnitsNonPolarThetaToBriefType(CoordUnitsNonPolarTheta coordUnits)
CoordUnitsNonPolarTheta coordUnitsX() const
Get method for x units.
Validator factory.
DocumentAxesPointsRequired
QString QLocaleToString(const QLocale &locale)
Definition: QtToString.cpp:59
DlgValidatorAbstract * createCartesianOrPolarWithPolarPolar(CoordScale coordScale, bool isCartesian, CoordUnitsNonPolarTheta coordUnitsCartesian, CoordUnitsPolarTheta coordUnitsPolar, CoordUnitsDate coordUnitsDate, CoordUnitsTime coordUnitsTime, const QLocale &locale) const
Factory method for generating validators for either cartesian or polar case, when polar format is spe...
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:91