Engauge Digitizer  2
DlgSettingsPointMatch.cpp
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 "CmdMediator.h"
8 #include "CmdSettingsPointMatch.h"
9 #include "DlgSettingsPointMatch.h"
10 #include "EngaugeAssert.h"
11 #include "Logger.h"
12 #include "MainWindow.h"
13 #include <QComboBox>
14 #include <QGraphicsEllipseItem>
15 #include <QGraphicsPixmapItem>
16 #include <QGraphicsRectItem>
17 #include <QGraphicsScene>
18 #include <QGridLayout>
19 #include <QLabel>
20 #include <qmath.h>
21 #include <QPen>
22 #include <QSpinBox>
23 #include "ViewPreview.h"
24 
25 const int MINIMUM_HEIGHT = 480;
26 const int POINT_SIZE_MAX = 1024;
27 const int POINT_SIZE_MIN = 5;
28 
30  DlgSettingsAbstractBase (tr ("Point Match"),
31  "DlgSettingsPointMatch",
32  mainWindow),
33  m_scenePreview (0),
34  m_viewPreview (0),
35  m_circle (0),
36  m_modelPointMatchBefore (0),
37  m_modelPointMatchAfter (0)
38 {
39  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::DlgSettingsPointMatch";
40 
41  QWidget *subPanel = createSubPanel ();
42  finishPanel (subPanel);
43 }
44 
45 DlgSettingsPointMatch::~DlgSettingsPointMatch()
46 {
47  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::~DlgSettingsPointMatch";
48 }
49 
50 QPointF DlgSettingsPointMatch::boxPositionConstraint(const QPointF &posIn)
51 {
52  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::boxPositionConstraint";
53 
54  double radius = radiusAlongDiagonal();
55  double diameter = 2.0 * radius;
56 
57  // Do not move any part outside the preview window or else ugly, and unwanted, shifting will occur
58  QPointF pos (posIn);
59  if (pos.x() - radius < 0) {
60  pos.setX (radius);
61  }
62 
63  if (pos.y() - radius < 0) {
64  pos.setY (radius);
65  }
66 
67  if (pos.x() + diameter > m_scenePreview->sceneRect().width ()) {
68  pos.setX (m_scenePreview->sceneRect().width() - diameter);
69  }
70 
71  if (pos.y() + diameter > m_scenePreview->sceneRect().height ()) {
72  pos.setY (m_scenePreview->sceneRect().height() - diameter);
73  }
74 
75  return pos;
76 }
77 
78 void DlgSettingsPointMatch::createControls (QGridLayout *layout,
79  int &row)
80 {
81  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::createControls";
82 
83  QLabel *labelPointSize = new QLabel (QString ("%1:").arg (tr ("Maximum point size (pixels)")));
84  layout->addWidget (labelPointSize, row, 1);
85 
86  m_spinPointSize = new QSpinBox;
87  m_spinPointSize->setWhatsThis (tr ("Select a maximum point size in pixels.\n\n"
88  "Sample match points must fit within a square box, around the cursor, having width and height "
89  "equal to this maximum.\n\n"
90  "This size is also used to determine if a region of pixels that are on, in the processed image, "
91  "should be ignored since that region is wider or taller than this limit.\n\n"
92  "This value has a lower limit"));
93  m_spinPointSize->setMinimum (POINT_SIZE_MIN);
94  m_spinPointSize->setMaximum (POINT_SIZE_MAX);
95  connect (m_spinPointSize, SIGNAL (valueChanged (int)), this, SLOT (slotMaxPointSize (int)));
96  layout->addWidget (m_spinPointSize, row++, 2);
97 
98  QLabel *labelAcceptedPointColor = new QLabel (QString ("%1:").arg (tr ("Accepted point color")));
99  layout->addWidget (labelAcceptedPointColor, row, 1);
100 
101  m_cmbAcceptedPointColor = new QComboBox;
102  m_cmbAcceptedPointColor->setWhatsThis (tr ("Select a color for matched points that are accepted"));
103  populateColorComboWithTransparent (*m_cmbAcceptedPointColor);
104  connect (m_cmbAcceptedPointColor, SIGNAL (activated (const QString &)), this, SLOT (slotAcceptedPointColor (const QString &))); // activated() ignores code changes
105  layout->addWidget (m_cmbAcceptedPointColor, row++, 2);
106 
107  QLabel *labelRejectedPointColor = new QLabel (QString ("%1:").arg (tr ("Rejected point color")));
108  layout->addWidget (labelRejectedPointColor, row, 1);
109 
110  m_cmbRejectedPointColor = new QComboBox;
111  m_cmbRejectedPointColor->setWhatsThis (tr ("Select a color for matched points that are rejected"));
112  populateColorComboWithTransparent (*m_cmbRejectedPointColor);
113  connect (m_cmbRejectedPointColor, SIGNAL (activated (const QString &)), this, SLOT (slotRejectedPointColor (const QString &))); // activated() ignores code changes
114  layout->addWidget (m_cmbRejectedPointColor, row++, 2);
115 
116  QLabel *labelCandidatePointColor = new QLabel (QString ("%1:").arg (tr ("Candidate point color")));
117  layout->addWidget (labelCandidatePointColor, row, 1);
118 
119  m_cmbCandidatePointColor = new QComboBox;
120  m_cmbCandidatePointColor->setWhatsThis (tr ("Select a color for the point being decided upon"));
121  populateColorComboWithTransparent (*m_cmbCandidatePointColor);
122  connect (m_cmbCandidatePointColor, SIGNAL (activated (const QString &)), this, SLOT (slotCandidatePointColor (const QString &))); // activated() ignores code changes
123  layout->addWidget (m_cmbCandidatePointColor, row++, 2);
124 }
125 
126 void DlgSettingsPointMatch::createOptionalSaveDefault (QHBoxLayout * /* layout */)
127 {
128 }
129 
130 void DlgSettingsPointMatch::createPreview (QGridLayout *layout,
131  int &row)
132 {
133  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::createPreview";
134 
135  QLabel *labelPreview = new QLabel (tr ("Preview"));
136  layout->addWidget (labelPreview, row++, 0, 1, 4);
137 
138  m_scenePreview = new QGraphicsScene (this);
139  m_viewPreview = new ViewPreview (m_scenePreview,
140  ViewPreview::VIEW_ASPECT_RATIO_VARIABLE,
141  this);
142  m_viewPreview->setWhatsThis (tr ("Preview window shows how current settings affect "
143  "point matching, and how the marked and candidate points are displayed.\n\nThe points are separated "
144  "by the point separation value, and the maximum point size is shown as a box in the center"));
145  m_viewPreview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
146  m_viewPreview->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
147  m_viewPreview->setMinimumHeight (MINIMUM_PREVIEW_HEIGHT);
148  connect (m_viewPreview, SIGNAL (signalMouseMove (QPointF)), this, SLOT (slotMouseMove (QPointF)));
149 
150  layout->addWidget (m_viewPreview, row++, 0, 1, 4);
151 }
152 
154 {
155  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::createSubPanel";
156 
157  QWidget *subPanel = new QWidget ();
158  QGridLayout *layout = new QGridLayout (subPanel);
159  subPanel->setLayout (layout);
160 
161  layout->setColumnStretch(0, 1); // Empty column
162  layout->setColumnStretch(1, 0); // Labels
163  layout->setColumnStretch(2, 0); // Controls
164  layout->setColumnStretch(3, 1); // Empty column
165 
166  int row = 0;
167  createControls (layout, row);
168  createPreview (layout, row);
169  createTemplate ();
170 
171  return subPanel;
172 }
173 
174 void DlgSettingsPointMatch::createTemplate ()
175 {
176  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::createTemplate";
177 
178  QPen pen (QBrush (Qt::black), 0);
179 
180  m_circle = new QGraphicsEllipseItem;
181  m_circle->setPen (pen);
182  m_circle->setZValue (100);
183  m_scenePreview->addItem (m_circle);
184 }
185 
187 {
188  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::handleOk";
189 
191  cmdMediator ().document(),
192  *m_modelPointMatchBefore,
193  *m_modelPointMatchAfter);
194  cmdMediator ().push (cmd);
195 
196  hide ();
197 }
198 
199 void DlgSettingsPointMatch::initializeBox ()
200 {
201  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::initializeBox";
202 
203  m_circle->setPos (cmdMediator().document().pixmap().width () / 2.0,
204  cmdMediator().document().pixmap().height () / 2.0); // Initially box is in center of preview
205 }
206 
208 {
209  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::load";
210 
212 
213  // Flush old data
214  delete m_modelPointMatchBefore;
215  delete m_modelPointMatchAfter;
216 
217  // Save new data
218  m_modelPointMatchBefore = new DocumentModelPointMatch (cmdMediator.document());
219  m_modelPointMatchAfter = new DocumentModelPointMatch (cmdMediator.document());
220 
221  // Sanity checks. Incoming defaults must be acceptable to the local limits
222  ENGAUGE_ASSERT (POINT_SIZE_MIN <= m_modelPointMatchAfter->maxPointSize());
223  ENGAUGE_ASSERT (POINT_SIZE_MAX > m_modelPointMatchAfter->maxPointSize());
224 
225  // Populate controls
226  m_spinPointSize->setValue(m_modelPointMatchAfter->maxPointSize());
227 
228  int indexAccepted = m_cmbAcceptedPointColor->findData(QVariant(m_modelPointMatchAfter->paletteColorAccepted()));
229  ENGAUGE_ASSERT (indexAccepted >= 0);
230  m_cmbAcceptedPointColor->setCurrentIndex(indexAccepted);
231 
232  int indexCandidate = m_cmbCandidatePointColor->findData(QVariant(m_modelPointMatchAfter->paletteColorCandidate()));
233  ENGAUGE_ASSERT (indexCandidate >= 0);
234  m_cmbCandidatePointColor->setCurrentIndex(indexCandidate);
235 
236  int indexRejected = m_cmbRejectedPointColor->findData(QVariant(m_modelPointMatchAfter->paletteColorRejected()));
237  ENGAUGE_ASSERT (indexRejected >= 0);
238  m_cmbRejectedPointColor->setCurrentIndex(indexRejected);
239 
240  initializeBox ();
241 
242  // Fix the preview size using an invisible boundary
243  QGraphicsRectItem *boundary = m_scenePreview->addRect (QRect (0,
244  0,
245  cmdMediator.document().pixmap().width (),
246  cmdMediator.document().pixmap().height ()));
247  boundary->setVisible (false);
248 
249  m_scenePreview->addPixmap (cmdMediator.document().pixmap());
250 
251  updateControls();
252  enableOk (false); // Disable Ok button since there not yet any changes
253  updatePreview();
254 }
255 
256 double DlgSettingsPointMatch::radiusAlongDiagonal () const
257 {
258  double maxPointSize = m_modelPointMatchAfter->maxPointSize();
259 
260  return qSqrt (2.0) * maxPointSize / 2.0;
261 }
262 
264 {
265  if (!smallDialogs) {
266  setMinimumHeight (MINIMUM_HEIGHT);
267  }
268 }
269 
270 void DlgSettingsPointMatch::slotAcceptedPointColor (const QString &)
271 {
272  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotAcceptedPointColor";
273 
274  m_modelPointMatchAfter->setPaletteColorAccepted((ColorPalette) m_cmbAcceptedPointColor->currentData().toInt());
275 
276  updateControls();
277  updatePreview();
278 }
279 
280 void DlgSettingsPointMatch::slotCandidatePointColor (const QString &)
281 {
282  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotCandidatePointColor";
283 
284  m_modelPointMatchAfter->setPaletteColorCandidate((ColorPalette) m_cmbCandidatePointColor->currentData().toInt());
285  updateControls();
286  updatePreview();
287 }
288 
289 void DlgSettingsPointMatch::slotMaxPointSize (int maxPointSize)
290 {
291  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotMaxPointSize";
292 
293  m_modelPointMatchAfter->setMaxPointSize(maxPointSize);
294  updateControls();
295  updatePreview();
296 }
297 
298 void DlgSettingsPointMatch::slotMouseMove (QPointF pos)
299 {
300  // Move the box so it follows the mouse move, making sure to keep it entirely inside the view to
301  // prevent autoresizing by QGraphicsView
302  pos = boxPositionConstraint (pos);
303 
304  m_circle->setPos (pos);
305 }
306 
307 void DlgSettingsPointMatch::slotRejectedPointColor (const QString &)
308 {
309  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotRejectedPointColor";
310 
311  m_modelPointMatchAfter->setPaletteColorRejected((ColorPalette) m_cmbRejectedPointColor->currentData().toInt());
312  updateControls();
313  updatePreview();
314 }
315 
316 void DlgSettingsPointMatch::updateControls()
317 {
318  // All controls in this dialog are always fully validated so the ok button is always enabled (after the first change)
319  enableOk (true);
320 }
321 
322 void DlgSettingsPointMatch::updatePreview()
323 {
324  // Geometry parameters
325  double maxPointSize = m_modelPointMatchAfter->maxPointSize();
326 
327  double xLeft = -1.0 * maxPointSize / 2.0;
328  double yTop = -1.0 * maxPointSize / 2.0;
329 
330  // Update circle size
331  m_circle->setRect (xLeft,
332  yTop,
333  maxPointSize,
334  maxPointSize);
335 }
Model for DlgSettingsPointMatch and CmdSettingsPointMatch.
void setPaletteColorCandidate(ColorPalette paletteColorCandidate)
Set method for candidate color.
void setCmdMediator(CmdMediator &cmdMediator)
Store CmdMediator for easy access by the leaf class.
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Definition: CmdMediator.cpp:72
void finishPanel(QWidget *subPanel, int minimumWidth=MINIMUM_DIALOG_WIDTH, int minimumHeightOrZero=0)
Add Ok and Cancel buttons to subpanel to get the whole dialog.
Command for DlgSettingsPointMatch.
ColorPalette paletteColorCandidate() const
Get method for candidate color.
Class that modifies QGraphicsView to automatically expand/shrink the view to fit the window,...
Definition: ViewPreview.h:14
void setMaxPointSize(double maxPointSize)
Set method for max point size.
ColorPalette paletteColorAccepted() const
Get method for accepted color.
void setPaletteColorRejected(ColorPalette paletteColorRejected)
Set method for rejected color.
static int MINIMUM_PREVIEW_HEIGHT
Dialog layout constant that guarantees preview has sufficent room.
virtual void handleOk()
Process slotOk.
void enableOk(bool enable)
Let leaf subclass control the Ok button.
Command queue stack.
Definition: CmdMediator.h:23
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void populateColorComboWithTransparent(QComboBox &combo)
Add colors in color palette to combobox, with transparent entry at end.
Abstract base class for all Settings dialogs.
virtual QWidget * createSubPanel()
Create dialog-specific panel to which base class will add Ok and Cancel buttons.
QPixmap pixmap() const
Return the image that is being digitized.
Definition: Document.cpp:813
DlgSettingsPointMatch(MainWindow &mainWindow)
Single constructor.
ColorPalette paletteColorRejected() const
Get method for rejected color.
MainWindow & mainWindow()
Get method for MainWindow.
virtual void createOptionalSaveDefault(QHBoxLayout *layout)
Let subclass define an optional Save As Default button.
double maxPointSize() const
Get method for max point size.
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:91
CmdMediator & cmdMediator()
Provide access to Document information wrapped inside CmdMediator.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
void setPaletteColorAccepted(ColorPalette paletteColorAccepted)
Set method for accepted color.