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 (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 (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 (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 (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 
211  setCmdMediator (cmdMediator);
212 
213  // Flush old data
214  if (m_modelPointMatchBefore != 0) {
215  delete m_modelPointMatchBefore;
216  }
217  if (m_modelPointMatchAfter != 0) {
218  delete m_modelPointMatchAfter;
219  }
220 
221  // Save new data
222  m_modelPointMatchBefore = new DocumentModelPointMatch (cmdMediator.document());
223  m_modelPointMatchAfter = new DocumentModelPointMatch (cmdMediator.document());
224 
225  // Sanity checks. Incoming defaults must be acceptable to the local limits
226  ENGAUGE_ASSERT (POINT_SIZE_MIN <= m_modelPointMatchAfter->maxPointSize());
227  ENGAUGE_ASSERT (POINT_SIZE_MAX > m_modelPointMatchAfter->maxPointSize());
228 
229  // Populate controls
230  m_spinPointSize->setValue(m_modelPointMatchAfter->maxPointSize());
231 
232  int indexAccepted = m_cmbAcceptedPointColor->findData(QVariant(m_modelPointMatchAfter->paletteColorAccepted()));
233  ENGAUGE_ASSERT (indexAccepted >= 0);
234  m_cmbAcceptedPointColor->setCurrentIndex(indexAccepted);
235 
236  int indexCandidate = m_cmbCandidatePointColor->findData(QVariant(m_modelPointMatchAfter->paletteColorCandidate()));
237  ENGAUGE_ASSERT (indexCandidate >= 0);
238  m_cmbCandidatePointColor->setCurrentIndex(indexCandidate);
239 
240  int indexRejected = m_cmbRejectedPointColor->findData(QVariant(m_modelPointMatchAfter->paletteColorRejected()));
241  ENGAUGE_ASSERT (indexRejected >= 0);
242  m_cmbRejectedPointColor->setCurrentIndex(indexRejected);
243 
244  initializeBox ();
245 
246  // Fix the preview size using an invisible boundary
247  QGraphicsRectItem *boundary = m_scenePreview->addRect (QRect (0,
248  0,
249  cmdMediator.document().pixmap().width (),
250  cmdMediator.document().pixmap().height ()));
251  boundary->setVisible (false);
252 
253  m_scenePreview->addPixmap (cmdMediator.document().pixmap());
254 
255  updateControls();
256  enableOk (false); // Disable Ok button since there not yet any changes
257  updatePreview();
258 }
259 
260 double DlgSettingsPointMatch::radiusAlongDiagonal () const
261 {
262  double maxPointSize = m_modelPointMatchAfter->maxPointSize();
263 
264  return qSqrt (2.0) * maxPointSize / 2.0;
265 }
266 
268 {
269  if (!smallDialogs) {
270  setMinimumHeight (MINIMUM_HEIGHT);
271  }
272 }
273 
274 void DlgSettingsPointMatch::slotAcceptedPointColor (const QString &)
275 {
276  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotAcceptedPointColor";
277 
278  m_modelPointMatchAfter->setPaletteColorAccepted((ColorPalette) m_cmbAcceptedPointColor->currentData().toInt());
279 
280  updateControls();
281  updatePreview();
282 }
283 
284 void DlgSettingsPointMatch::slotCandidatePointColor (const QString &)
285 {
286  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotCandidatePointColor";
287 
288  m_modelPointMatchAfter->setPaletteColorCandidate((ColorPalette) m_cmbCandidatePointColor->currentData().toInt());
289  updateControls();
290  updatePreview();
291 }
292 
293 void DlgSettingsPointMatch::slotMaxPointSize (int maxPointSize)
294 {
295  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotMaxPointSize";
296 
297  m_modelPointMatchAfter->setMaxPointSize(maxPointSize);
298  updateControls();
299  updatePreview();
300 }
301 
302 void DlgSettingsPointMatch::slotMouseMove (QPointF pos)
303 {
304  // Move the box so it follows the mouse move, making sure to keep it entirely inside the view to
305  // prevent autoresizing by QGraphicsView
306  pos = boxPositionConstraint (pos);
307 
308  m_circle->setPos (pos);
309 }
310 
311 void DlgSettingsPointMatch::slotRejectedPointColor (const QString &)
312 {
313  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotRejectedPointColor";
314 
315  m_modelPointMatchAfter->setPaletteColorRejected((ColorPalette) m_cmbRejectedPointColor->currentData().toInt());
316  updateControls();
317  updatePreview();
318 }
319 
320 void DlgSettingsPointMatch::updateControls()
321 {
322  // All controls in this dialog are always fully validated so the ok button is always enabled (after the first change)
323  enableOk (true);
324 }
325 
326 void DlgSettingsPointMatch::updatePreview()
327 {
328  // Geometry parameters
329  double maxPointSize = m_modelPointMatchAfter->maxPointSize();
330 
331  double xLeft = -1.0 * maxPointSize / 2.0;
332  double yTop = -1.0 * maxPointSize / 2.0;
333 
334  // Update circle size
335  m_circle->setRect (xLeft,
336  yTop,
337  maxPointSize,
338  maxPointSize);
339 }
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:811
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:89
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.