ctkRangeWidget.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. All rights reserved.
  5. Distributed under a BSD License. See LICENSE.txt file.
  6. This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
  7. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the above copyright notice for more information.
  9. =========================================================================*/
  10. // Qt includes
  11. #include <QDebug>
  12. #include <QMouseEvent>
  13. // CTK includes
  14. #include "ctkRangeWidget.h"
  15. #include "ui_ctkRangeWidget.h"
  16. //-----------------------------------------------------------------------------
  17. class ctkRangeWidgetPrivate: public ctkPrivate<ctkRangeWidget>,
  18. public Ui_ctkRangeWidget
  19. {
  20. public:
  21. ctkRangeWidgetPrivate();
  22. void updateSpinBoxWidth();
  23. int synchronizedSpinBoxWidth()const;
  24. void synchronizeSiblingSpinBox(int newWidth);
  25. static bool equal(double v1, double v2);
  26. bool Tracking;
  27. bool Changing;
  28. double MinimumValueBeforeChange;
  29. double MaximumValueBeforeChange;
  30. bool AutoSpinBoxWidth;
  31. };
  32. // --------------------------------------------------------------------------
  33. bool ctkRangeWidgetPrivate::equal(double v1, double v2)
  34. {
  35. return qAbs(v1 - v2) < 0.0001;
  36. }
  37. // --------------------------------------------------------------------------
  38. ctkRangeWidgetPrivate::ctkRangeWidgetPrivate()
  39. {
  40. this->Tracking = true;
  41. this->Changing = false;
  42. this->MinimumValueBeforeChange = 0.;
  43. this->MaximumValueBeforeChange = 0.;
  44. this->AutoSpinBoxWidth = true;
  45. }
  46. // --------------------------------------------------------------------------
  47. void ctkRangeWidgetPrivate::updateSpinBoxWidth()
  48. {
  49. int spinBoxWidth = this->synchronizedSpinBoxWidth();
  50. if (this->AutoSpinBoxWidth)
  51. {
  52. this->MinimumSpinBox->setMinimumWidth(spinBoxWidth);
  53. this->MaximumSpinBox->setMinimumWidth(spinBoxWidth);
  54. }
  55. else
  56. {
  57. this->MinimumSpinBox->setMinimumWidth(0);
  58. this->MaximumSpinBox->setMinimumWidth(0);
  59. }
  60. this->synchronizeSiblingSpinBox(spinBoxWidth);
  61. }
  62. // --------------------------------------------------------------------------
  63. int ctkRangeWidgetPrivate::synchronizedSpinBoxWidth()const
  64. {
  65. CTK_P(const ctkRangeWidget);
  66. //Q_ASSERT(this->MinimumSpinBox->sizeHint() == this->MaximumSpinBox->sizeHint());
  67. int maxWidth = qMax(this->MinimumSpinBox->sizeHint().width(),
  68. this->MaximumSpinBox->sizeHint().width());
  69. if (!p->parent())
  70. {
  71. return maxWidth;
  72. }
  73. QList<ctkRangeWidget*> siblings =
  74. p->parent()->findChildren<ctkRangeWidget*>();
  75. foreach(ctkRangeWidget* sibling, siblings)
  76. {
  77. maxWidth = qMax(maxWidth, qMax(sibling->ctk_d()->MaximumSpinBox->sizeHint().width(),
  78. sibling->ctk_d()->MaximumSpinBox->sizeHint().width()));
  79. }
  80. return maxWidth;
  81. }
  82. // --------------------------------------------------------------------------
  83. void ctkRangeWidgetPrivate::synchronizeSiblingSpinBox(int width)
  84. {
  85. CTK_P(const ctkRangeWidget);
  86. QList<ctkRangeWidget*> siblings =
  87. p->parent()->findChildren<ctkRangeWidget*>();
  88. foreach(ctkRangeWidget* sibling, siblings)
  89. {
  90. if (sibling != p && sibling->isAutoSpinBoxWidth())
  91. {
  92. sibling->ctk_d()->MinimumSpinBox->setMinimumWidth(width);
  93. sibling->ctk_d()->MaximumSpinBox->setMinimumWidth(width);
  94. }
  95. }
  96. }
  97. // --------------------------------------------------------------------------
  98. ctkRangeWidget::ctkRangeWidget(QWidget* _parent) : Superclass(_parent)
  99. {
  100. CTK_INIT_PRIVATE(ctkRangeWidget);
  101. CTK_D(ctkRangeWidget);
  102. d->setupUi(this);
  103. d->MinimumSpinBox->setMinimum(d->Slider->minimum());
  104. d->MinimumSpinBox->setMaximum(d->Slider->maximum());
  105. d->MaximumSpinBox->setMinimum(d->Slider->minimum());
  106. d->MaximumSpinBox->setMaximum(d->Slider->maximum());
  107. d->MinimumSpinBox->setValue(d->Slider->minimumValue());
  108. d->MaximumSpinBox->setValue(d->Slider->maximumValue());
  109. this->connect(d->Slider, SIGNAL(minimumValueChanged(double)), d->MinimumSpinBox, SLOT(setValue(double)));
  110. this->connect(d->MinimumSpinBox, SIGNAL(valueChanged(double)), d->Slider, SLOT(setMinimumValue(double)));
  111. this->connect(d->Slider, SIGNAL(maximumValueChanged(double)), d->MaximumSpinBox, SLOT(setValue(double)));
  112. this->connect(d->MaximumSpinBox, SIGNAL(valueChanged(double)), d->Slider, SLOT(setMaximumValue(double)));
  113. this->connect(d->MinimumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(setMinimumToMaximumSpinBox(double)));
  114. this->connect(d->MaximumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(setMaximumToMinimumSpinBox(double)));
  115. this->connect(d->Slider, SIGNAL(sliderPressed()), this, SLOT(startChanging()));
  116. this->connect(d->Slider, SIGNAL(sliderReleased()), this, SLOT(stopChanging()));
  117. this->connect(d->Slider, SIGNAL(minimumValueChanged(double)), this, SLOT(changeMinimumValue(double)));
  118. this->connect(d->Slider, SIGNAL(maximumValueChanged(double)), this, SLOT(changeMaximumValue(double)));
  119. d->MinimumSpinBox->installEventFilter(this);
  120. d->MaximumSpinBox->installEventFilter(this);
  121. }
  122. // --------------------------------------------------------------------------
  123. double ctkRangeWidget::minimum()const
  124. {
  125. CTK_D(const ctkRangeWidget);
  126. Q_ASSERT(d->equal(d->MinimumSpinBox->minimum(),d->Slider->minimum()));
  127. return d->Slider->minimum();
  128. }
  129. // --------------------------------------------------------------------------
  130. double ctkRangeWidget::maximum()const
  131. {
  132. CTK_D(const ctkRangeWidget);
  133. Q_ASSERT(d->equal(d->MaximumSpinBox->maximum(), d->Slider->maximum()));
  134. return d->Slider->maximum();
  135. }
  136. // --------------------------------------------------------------------------
  137. void ctkRangeWidget::setMinimum(double min)
  138. {
  139. CTK_D(ctkRangeWidget);
  140. d->MinimumSpinBox->setMinimum(min);
  141. // SpinBox can truncate min (depending on decimals).
  142. // use Spinbox's min to set Slider's min
  143. d->Slider->setMinimum(d->MinimumSpinBox->minimum());
  144. Q_ASSERT(d->equal(d->MinimumSpinBox->minimum(),d->Slider->minimum()));
  145. d->updateSpinBoxWidth();
  146. }
  147. // --------------------------------------------------------------------------
  148. void ctkRangeWidget::setMaximum(double max)
  149. {
  150. CTK_D(ctkRangeWidget);
  151. d->MaximumSpinBox->setMaximum(max);
  152. // SpinBox can truncate max (depending on decimals).
  153. // use Spinbox's max to set Slider's max
  154. d->Slider->setMaximum(d->MaximumSpinBox->maximum());
  155. Q_ASSERT(d->equal(d->MaximumSpinBox->maximum(), d->Slider->maximum()));
  156. d->updateSpinBoxWidth();
  157. }
  158. // --------------------------------------------------------------------------
  159. void ctkRangeWidget::setRange(double min, double max)
  160. {
  161. CTK_D(ctkRangeWidget);
  162. d->MinimumSpinBox->setMinimum(qMin(min,max));
  163. d->MaximumSpinBox->setMaximum(qMax(min,max));
  164. // SpinBox can truncate the range (depending on decimals).
  165. // use Spinbox's range to set Slider's range
  166. d->Slider->setRange(d->MinimumSpinBox->minimum(), d->MaximumSpinBox->maximum());
  167. Q_ASSERT(d->equal(d->MinimumSpinBox->minimum(), d->Slider->minimum()));
  168. Q_ASSERT(d->equal(d->MaximumSpinBox->maximum(), d->Slider->maximum()));
  169. d->updateSpinBoxWidth();
  170. }
  171. /*
  172. // --------------------------------------------------------------------------
  173. double ctkRangeWidget::sliderPosition()const
  174. {
  175. return ctk_d()->Slider->sliderPosition();
  176. }
  177. // --------------------------------------------------------------------------
  178. void ctkRangeWidget::setSliderPosition(double position)
  179. {
  180. ctk_d()->Slider->setSliderPosition(position);
  181. }
  182. */
  183. /*
  184. // --------------------------------------------------------------------------
  185. double ctkRangeWidget::previousSliderPosition()
  186. {
  187. return ctk_d()->Slider->previousSliderPosition();
  188. }
  189. */
  190. // --------------------------------------------------------------------------
  191. double ctkRangeWidget::minimumValue()const
  192. {
  193. CTK_D(const ctkRangeWidget);
  194. Q_ASSERT(d->equal(d->Slider->minimumValue(), d->MinimumSpinBox->value()));
  195. return d->Changing ? d->MinimumValueBeforeChange : d->Slider->minimumValue();
  196. }
  197. // --------------------------------------------------------------------------
  198. double ctkRangeWidget::maximumValue()const
  199. {
  200. CTK_D(const ctkRangeWidget);
  201. Q_ASSERT(d->equal(d->Slider->maximumValue(), d->MaximumSpinBox->value()));
  202. return d->Changing ? d->MaximumValueBeforeChange : d->Slider->maximumValue();
  203. }
  204. // --------------------------------------------------------------------------
  205. void ctkRangeWidget::setMinimumValue(double _value)
  206. {
  207. CTK_D(ctkRangeWidget);
  208. // disable the tracking temporally to emit the
  209. // signal valueChanged if changeValue() is called
  210. bool isChanging = d->Changing;
  211. d->Changing = false;
  212. d->MinimumSpinBox->setValue(_value);
  213. // Why do we need to set the value to the slider ?
  214. //d->Slider->setValue(d->SpinBox->value());
  215. Q_ASSERT(d->equal(d->Slider->minimumValue(), d->MinimumSpinBox->value()));
  216. // restore the prop
  217. d->Changing = isChanging;
  218. }
  219. // --------------------------------------------------------------------------
  220. void ctkRangeWidget::setMaximumValue(double _value)
  221. {
  222. CTK_D(ctkRangeWidget);
  223. // disable the tracking temporally to emit the
  224. // signal valueChanged if changeValue() is called
  225. bool isChanging = d->Changing;
  226. d->Changing = false;
  227. d->MaximumSpinBox->setValue(_value);
  228. // Why do we need to set the value to the slider ?
  229. //d->Slider->setValue(d->SpinBox->value());
  230. Q_ASSERT(d->equal(d->Slider->maximumValue(), d->MaximumSpinBox->value()));
  231. // restore the prop
  232. d->Changing = isChanging;
  233. }
  234. // --------------------------------------------------------------------------
  235. void ctkRangeWidget::setMinimumToMaximumSpinBox(double minimum)
  236. {
  237. ctk_d()->MaximumSpinBox->setMinimum(minimum);
  238. }
  239. // --------------------------------------------------------------------------
  240. void ctkRangeWidget::setMaximumToMinimumSpinBox(double maximum)
  241. {
  242. ctk_d()->MinimumSpinBox->setMaximum(maximum);
  243. }
  244. // --------------------------------------------------------------------------
  245. void ctkRangeWidget::startChanging()
  246. {
  247. CTK_D(ctkRangeWidget);
  248. if (d->Tracking)
  249. {
  250. return;
  251. }
  252. d->Changing = true;
  253. d->MinimumValueBeforeChange = this->minimumValue();
  254. d->MaximumValueBeforeChange = this->maximumValue();
  255. }
  256. // --------------------------------------------------------------------------
  257. void ctkRangeWidget::stopChanging()
  258. {
  259. CTK_D(ctkRangeWidget);
  260. if (d->Tracking)
  261. {
  262. return;
  263. }
  264. d->Changing = false;
  265. if (qAbs(this->minimumValue() - d->MinimumValueBeforeChange) > (this->singleStep() * 0.000000001))
  266. {
  267. emit this->minimumValueChanged(this->minimumValue());
  268. }
  269. if (qAbs(this->maximumValue() - d->MaximumValueBeforeChange) > (this->singleStep() * 0.000000001))
  270. {
  271. emit this->maximumValueChanged(this->maximumValue());
  272. }
  273. }
  274. // --------------------------------------------------------------------------
  275. void ctkRangeWidget::changeMinimumValue(double newValue)
  276. {
  277. CTK_D(ctkRangeWidget);
  278. //if (d->Tracking)
  279. {
  280. emit this->minimumValueIsChanging(newValue);
  281. }
  282. if (!d->Changing)
  283. {
  284. emit this->minimumValueChanged(newValue);
  285. }
  286. }
  287. // --------------------------------------------------------------------------
  288. void ctkRangeWidget::changeMaximumValue(double newValue)
  289. {
  290. CTK_D(ctkRangeWidget);
  291. //if (d->Tracking)
  292. {
  293. emit this->maximumValueIsChanging(newValue);
  294. }
  295. if (!d->Changing)
  296. {
  297. emit this->maximumValueChanged(newValue);
  298. }
  299. }
  300. // --------------------------------------------------------------------------
  301. bool ctkRangeWidget::eventFilter(QObject *obj, QEvent *event)
  302. {
  303. if (event->type() == QEvent::MouseButtonPress)
  304. {
  305. QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
  306. if (mouseEvent->button() & Qt::LeftButton)
  307. {
  308. this->startChanging();
  309. }
  310. }
  311. else if (event->type() == QEvent::MouseButtonRelease)
  312. {
  313. QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
  314. if (mouseEvent->button() & Qt::LeftButton)
  315. {
  316. // here we might prevent ctkRangeWidget::stopChanging
  317. // from sending a valueChanged() event as the spinbox might
  318. // send a valueChanged() after eventFilter() is done.
  319. this->stopChanging();
  320. }
  321. }
  322. // standard event processing
  323. return this->Superclass::eventFilter(obj, event);
  324. }
  325. // --------------------------------------------------------------------------
  326. double ctkRangeWidget::singleStep()const
  327. {
  328. CTK_D(const ctkRangeWidget);
  329. Q_ASSERT(d->equal(d->Slider->singleStep(), d->MinimumSpinBox->singleStep()) &&
  330. d->equal(d->Slider->singleStep(), d->MaximumSpinBox->singleStep()));
  331. return d->Slider->singleStep();
  332. }
  333. // --------------------------------------------------------------------------
  334. void ctkRangeWidget::setSingleStep(double step)
  335. {
  336. CTK_D(ctkRangeWidget);
  337. d->MinimumSpinBox->setSingleStep(step);
  338. d->MaximumSpinBox->setSingleStep(step);
  339. d->Slider->setSingleStep(d->MinimumSpinBox->singleStep());
  340. Q_ASSERT(d->equal(d->Slider->singleStep(), d->MinimumSpinBox->singleStep()) &&
  341. d->equal(d->Slider->singleStep(), d->MaximumSpinBox->singleStep()));
  342. }
  343. // --------------------------------------------------------------------------
  344. int ctkRangeWidget::decimals()const
  345. {
  346. CTK_D(const ctkRangeWidget);
  347. Q_ASSERT(d->MinimumSpinBox->decimals() == d->MaximumSpinBox->decimals());
  348. return d->MinimumSpinBox->decimals();
  349. }
  350. // --------------------------------------------------------------------------
  351. void ctkRangeWidget::setDecimals(int newDecimals)
  352. {
  353. CTK_D(ctkRangeWidget);
  354. d->MinimumSpinBox->setDecimals(newDecimals);
  355. d->MaximumSpinBox->setDecimals(newDecimals);
  356. }
  357. // --------------------------------------------------------------------------
  358. QString ctkRangeWidget::prefix()const
  359. {
  360. CTK_D(const ctkRangeWidget);
  361. Q_ASSERT(d->MinimumSpinBox->prefix() == d->MaximumSpinBox->prefix());
  362. return d->MinimumSpinBox->prefix();
  363. }
  364. // --------------------------------------------------------------------------
  365. void ctkRangeWidget::setPrefix(const QString& newPrefix)
  366. {
  367. CTK_D(ctkRangeWidget);
  368. d->MinimumSpinBox->setPrefix(newPrefix);
  369. d->MaximumSpinBox->setPrefix(newPrefix);
  370. }
  371. // --------------------------------------------------------------------------
  372. QString ctkRangeWidget::suffix()const
  373. {
  374. CTK_D(const ctkRangeWidget);
  375. Q_ASSERT(d->MinimumSpinBox->suffix() == d->MaximumSpinBox->suffix());
  376. return d->MinimumSpinBox->suffix();
  377. }
  378. // --------------------------------------------------------------------------
  379. void ctkRangeWidget::setSuffix(const QString& newSuffix)
  380. {
  381. CTK_D(ctkRangeWidget);
  382. d->MinimumSpinBox->setSuffix(newSuffix);
  383. d->MaximumSpinBox->setSuffix(newSuffix);
  384. }
  385. // --------------------------------------------------------------------------
  386. double ctkRangeWidget::tickInterval()const
  387. {
  388. CTK_D(const ctkRangeWidget);
  389. return d->Slider->tickInterval();
  390. }
  391. // --------------------------------------------------------------------------
  392. void ctkRangeWidget::setTickInterval(double ti)
  393. {
  394. CTK_D(ctkRangeWidget);
  395. d->Slider->setTickInterval(ti);
  396. }
  397. // -------------------------------------------------------------------------
  398. void ctkRangeWidget::reset()
  399. {
  400. this->setMinimumValue(this->minimum());
  401. this->setMaximumValue(this->maximum());
  402. }
  403. // -------------------------------------------------------------------------
  404. void ctkRangeWidget::setSpinBoxAlignment(Qt::Alignment alignment)
  405. {
  406. CTK_D(ctkRangeWidget);
  407. d->MinimumSpinBox->setAlignment(alignment);
  408. d->MaximumSpinBox->setAlignment(alignment);
  409. }
  410. // -------------------------------------------------------------------------
  411. Qt::Alignment ctkRangeWidget::spinBoxAlignment()const
  412. {
  413. CTK_D(const ctkRangeWidget);
  414. Q_ASSERT(d->MinimumSpinBox->alignment() == d->MaximumSpinBox->alignment());
  415. return d->MinimumSpinBox->alignment();
  416. }
  417. // -------------------------------------------------------------------------
  418. void ctkRangeWidget::setTracking(bool enable)
  419. {
  420. CTK_D(ctkRangeWidget);
  421. d->Tracking = enable;
  422. }
  423. // -------------------------------------------------------------------------
  424. bool ctkRangeWidget::hasTracking()const
  425. {
  426. CTK_D(const ctkRangeWidget);
  427. return d->Tracking;
  428. }
  429. // -------------------------------------------------------------------------
  430. bool ctkRangeWidget::isAutoSpinBoxWidth()const
  431. {
  432. CTK_D(const ctkRangeWidget);
  433. return d->AutoSpinBoxWidth;
  434. }
  435. // -------------------------------------------------------------------------
  436. void ctkRangeWidget::setAutoSpinBoxWidth(bool autoWidth)
  437. {
  438. CTK_D(ctkRangeWidget);
  439. d->AutoSpinBoxWidth = autoWidth;
  440. d->updateSpinBoxWidth();
  441. }