Engauge Digitizer  2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DlgSettingsPointMatch.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 "CmdMediator.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 (nullptr),
34  m_viewPreview (nullptr),
35  m_circle (nullptr),
36  m_modelPointMatchBefore (nullptr),
37  m_modelPointMatchAfter (nullptr)
38 {
39  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::DlgSettingsPointMatch";
40 
41  QWidget *subPanel = createSubPanel ();
42  finishPanel (subPanel);
43 }
44 
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,
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  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(qFloor (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(static_cast<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(static_cast<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(static_cast<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 }
double maxPointSize() const
Get method for max point size.
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.
const int POINT_SIZE_MAX
#define LOG4CPP_INFO_S(logger)
Definition: convenience.h:18
QPixmap pixmap() const
Return the image that is being digitized.
Definition: Document.cpp:817
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.
const int POINT_SIZE_MIN
Command for DlgSettingsPointMatch.
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.
void setPaletteColorRejected(ColorPalette paletteColorRejected)
Set method for rejected color.
ColorPalette paletteColorCandidate() const
Get method for candidate color.
log4cpp::Category * mainCat
Definition: Logger.cpp:14
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.
const int MINIMUM_HEIGHT
Abstract base class for all Settings dialogs.
ColorPalette paletteColorRejected() const
Get method for rejected color.
virtual QWidget * createSubPanel()
Create dialog-specific panel to which base class will add Ok and Cancel buttons.
DlgSettingsPointMatch(MainWindow &mainWindow)
Single constructor.
ColorPalette paletteColorAccepted() const
Get method for accepted color.
MainWindow & mainWindow()
Get method for MainWindow.
virtual void createOptionalSaveDefault(QHBoxLayout *layout)
Let subclass define an optional Save As Default button.
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.
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT if defined(QT_NO_DEBUG) &amp;&amp; !defined(QT_FORCE_ASSERTS) define ENGAUGE...
Definition: EngaugeAssert.h:20