ctkVTKMagnifyViewTest2.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QApplication>
  16. #include <QCursor>
  17. #include <QHBoxLayout>
  18. #include <QIcon>
  19. #include <QSignalSpy>
  20. #include <QStyle>
  21. #include <QTimer>
  22. // CTK includes
  23. #include "ctkVTKMagnifyView.h"
  24. #include "ctkCommandLineParser.h"
  25. #include "ctkVTKSliceView.h"
  26. #include "ctkWidgetsUtils.h"
  27. // VTK includes
  28. #include <vtkImageReader2Factory.h>
  29. #include <vtkImageReader2.h>
  30. #include <vtkImageData.h>
  31. #include <vtkImageGaussianSmooth.h>
  32. #include <vtkSmartPointer.h>
  33. #include <vtkVersion.h>
  34. // STD includes
  35. #include <cstdlib>
  36. #include <iostream>
  37. //-----------------------------------------------------------------------------
  38. bool imageCompare(ctkVTKMagnifyView * magnify, QString baselineDirectory,
  39. QString baselineFilename)
  40. {
  41. QImage output = ctk::grabWidget(magnify);
  42. QImage baseline(baselineDirectory + "/" + baselineFilename);
  43. return output == baseline;
  44. }
  45. //-----------------------------------------------------------------------------
  46. // (Used to create baselines, not during testing).
  47. void imageSave(ctkVTKMagnifyView * magnify, QString baselineDirectory,
  48. QString baselineFilename)
  49. {
  50. QImage output = ctk::grabWidget(magnify);
  51. output.save(baselineDirectory + "/" + baselineFilename);
  52. }
  53. //-----------------------------------------------------------------------------
  54. bool runBaselineTest(int time, QApplication& app, ctkVTKMagnifyView * magnify,
  55. QWidget * underWidget, bool shouldBeUnder,
  56. QString baselineDirectory, QString testName,
  57. QString testNumber, QString errorMessage)
  58. {
  59. QTimer::singleShot(time, &app, SLOT(quit()));
  60. if (app.exec() == EXIT_FAILURE)
  61. {
  62. std::cerr << "ctkVTKMagnifyView exec failed when "
  63. << qPrintable(errorMessage) << std::endl;
  64. return false;
  65. }
  66. if (underWidget->underMouse() != shouldBeUnder)
  67. {
  68. std::cerr << "ctkMagnifyView mouse position failed when "
  69. << qPrintable(errorMessage) << std::endl;
  70. return false;
  71. }
  72. QString baselineFilename
  73. = "ctkVTKMagnifyViewTest2" + testNumber + testName + ".png";
  74. if (!imageCompare(magnify, baselineDirectory, baselineFilename))
  75. {
  76. std::cerr << "ctkVTKMagnifyView baseline comparison failed when "
  77. << qPrintable(errorMessage) << "." << std::endl;
  78. return false;
  79. }
  80. return true;
  81. }
  82. //-----------------------------------------------------------------------------
  83. int ctkVTKMagnifyViewTest2(int argc, char * argv [] )
  84. {
  85. QApplication app(argc, argv);
  86. // Command line parser
  87. ctkCommandLineParser parser;
  88. parser.addArgument("", "-D", QVariant::String);
  89. parser.addArgument("", "-V", QVariant::String);
  90. parser.addArgument("", "-I", QVariant::String);
  91. parser.addArgument("", "-T", QVariant::String);
  92. parser.addArgument("", "-S", QVariant::String);
  93. parser.addArgument("", "-M", QVariant::String);
  94. bool ok = false;
  95. QHash<QString, QVariant> parsedArgs = parser.parseArguments(app.arguments(), &ok);
  96. if (!ok)
  97. {
  98. std::cerr << qPrintable(parser.errorString()) << std::endl;
  99. return EXIT_FAILURE;
  100. }
  101. QString dataDirectory = parsedArgs["-D"].toString();
  102. QString baselineDirectory = parsedArgs["-V"].toString();
  103. QString testType = parsedArgs["-T"].toString();
  104. bool interactive = parsedArgs["-I"].toBool();
  105. int size = parsedArgs["-S"].toInt();
  106. double magnification = parsedArgs["-M"].toDouble();
  107. // Create the parent widget
  108. QWidget parentWidget;
  109. QHBoxLayout layout(&parentWidget);
  110. // Magnify widget parameters (we want an odd widget size and odd bullsEye)
  111. bool showCrosshair = true;
  112. QPen crosshairPen(Qt::yellow);
  113. crosshairPen.setJoinStyle(Qt::MiterJoin);
  114. ctkCrosshairLabel::CrosshairType crosshairType
  115. = ctkCrosshairLabel::BullsEyeCrosshair;
  116. double bullsEyeWidth = magnification + 2;
  117. QColor marginColor = Qt::magenta;
  118. bool observeRenderWindowEvents = false;
  119. int updateInterval = 0;
  120. // Create the magnify widget
  121. ctkVTKMagnifyView * magnify = new ctkVTKMagnifyView(&parentWidget);
  122. magnify->setMinimumSize(size,size);
  123. magnify->setMaximumSize(size,size);
  124. magnify->setShowCrosshair(showCrosshair);
  125. magnify->setCrosshairPen(crosshairPen);
  126. magnify->setCrosshairType(crosshairType);
  127. magnify->setBullsEyeWidth(bullsEyeWidth);
  128. magnify->setMarginColor(marginColor);
  129. magnify->setMagnification(magnification);
  130. magnify->setObserveRenderWindowEvents(observeRenderWindowEvents);
  131. magnify->setUpdateInterval(updateInterval);
  132. layout.addWidget(magnify);
  133. // Test magnify widget parameters
  134. if (magnify->showCrosshair() != showCrosshair)
  135. {
  136. std::cerr << "ctkVTKMagnifyView:setShowCrosshair failed. "
  137. << magnify->showCrosshair() << std::endl;
  138. return EXIT_FAILURE;
  139. }
  140. if (magnify->crosshairPen() != crosshairPen)
  141. {
  142. std::cerr << "ctkVTKMagnifyView:setCrosshairPen failed. "
  143. << qPrintable(magnify->crosshairPen().color().name()) << std::endl;
  144. return EXIT_FAILURE;
  145. }
  146. if (magnify->crosshairType() != crosshairType)
  147. {
  148. std::cerr << "ctkVTKMagnifyView:setCrosshairType failed. "
  149. << magnify->crosshairType() << std::endl;
  150. return EXIT_FAILURE;
  151. }
  152. if (magnify->bullsEyeWidth() != bullsEyeWidth)
  153. {
  154. std::cerr << "ctkVTKMagnifyView:setBullsEyeWidth failed. "
  155. << magnify->bullsEyeWidth() << std::endl;
  156. return EXIT_FAILURE;
  157. }
  158. if (magnify->marginColor() != marginColor)
  159. {
  160. std::cerr << "ctkVTKMagnifyView:setMarginColor failed. "
  161. << qPrintable(magnify->marginColor().name()) << std::endl;
  162. return EXIT_FAILURE;
  163. }
  164. if (magnify->magnification() != magnification)
  165. {
  166. std::cerr << "ctkVTKMagnifyView:setMagnification failed. "
  167. << magnify->magnification() << std::endl;
  168. return EXIT_FAILURE;
  169. }
  170. if (magnify->observeRenderWindowEvents() != observeRenderWindowEvents)
  171. {
  172. std::cerr << "ctkVTKMagnifyView:setObserveRenderWindowEvents failed. "
  173. << magnify->observeRenderWindowEvents() << std::endl;
  174. }
  175. if (magnify->updateInterval() != updateInterval)
  176. {
  177. std::cerr << "ctkVTKMagnifyView:setUpdateInterval failed. "
  178. << magnify->updateInterval() << std::endl;
  179. return EXIT_FAILURE;
  180. }
  181. // The remainder is interactive, so abort now if command line args specify otherwise
  182. if (interactive)
  183. {
  184. return EXIT_SUCCESS;
  185. }
  186. // Add observed ctkVTKSliceViews (there are three, and the first two are observed)
  187. QList<ctkVTKSliceView *> allSliceViews;
  188. int numSliceViews = 3;
  189. for (int i = 0; i < numSliceViews; i++)
  190. {
  191. allSliceViews.append(new ctkVTKSliceView(&parentWidget));
  192. layout.addWidget(allSliceViews[i]);
  193. }
  194. // Observe the first two widgets
  195. magnify->observe(allSliceViews[0]->VTKWidget());
  196. magnify->observe(allSliceViews[1]->VTKWidget());
  197. if (!magnify->isObserved(allSliceViews[0]->VTKWidget()) ||
  198. !magnify->isObserved(allSliceViews[1]->VTKWidget()) ||
  199. magnify->isObserved(allSliceViews[2]->VTKWidget()) ||
  200. magnify->numberObserved() != 2)
  201. {
  202. std::cerr << "ctkVTKMagnifyView:observe(ctkVTKOpenGLNativeWidget*) failed. "
  203. << "Number observed = " << magnify->numberObserved() << std::endl;
  204. return EXIT_FAILURE;
  205. }
  206. QString imageFilename = dataDirectory + "/" + "computerIcon.png";
  207. // Instanciate the reader factory
  208. vtkSmartPointer<vtkImageReader2Factory> imageFactory =
  209. vtkSmartPointer<vtkImageReader2Factory>::New();
  210. // Instanciate an image reader
  211. vtkSmartPointer<vtkImageReader2> imageReader;
  212. imageReader.TakeReference(imageFactory->CreateImageReader2(imageFilename.toLatin1()));
  213. if (!imageReader)
  214. {
  215. std::cerr << "Failed to instanciate image reader using: "
  216. << qPrintable(imageFilename) << std::endl;
  217. return EXIT_FAILURE;
  218. }
  219. // Read image
  220. imageReader->SetFileName(imageFilename.toLatin1());
  221. imageReader->Update();
  222. #if (VTK_MAJOR_VERSION <= 5)
  223. vtkImageData* image = imageReader->GetOutput();
  224. #else
  225. vtkAlgorithmOutput* imagePort = imageReader->GetOutputPort();
  226. #endif
  227. // Setup the slice views
  228. for (int i = 0; i < numSliceViews; i++)
  229. {
  230. allSliceViews[i]->setRenderEnabled(true);
  231. allSliceViews[i]->setMinimumSize(350,350);
  232. #if (VTK_MAJOR_VERSION <= 5)
  233. allSliceViews[i]->setImageData(image);
  234. #else
  235. allSliceViews[i]->setImageDataConnection(imagePort);
  236. #endif
  237. allSliceViews[i]->setHighlightedBoxColor(Qt::yellow);
  238. allSliceViews[i]->scheduleRender();
  239. }
  240. int time = 200;
  241. // Get crosshair points of interest, used in the following tests
  242. parentWidget.move(0,0);
  243. parentWidget.show();
  244. QTimer::singleShot(time, &app, SLOT(quit()));
  245. if (app.exec() == EXIT_FAILURE)
  246. {
  247. std::cerr << "ctkVTKMagnifyView:show failed the first time." << std::endl;
  248. return EXIT_FAILURE;
  249. }
  250. QPoint insideSlice0 = allSliceViews[0]->mapToGlobal(
  251. QPoint(allSliceViews[0]->width()/2 + 100, allSliceViews[0]->height()/2 + 100));
  252. QPoint outside = parentWidget.mapToGlobal(
  253. QPoint(parentWidget.width(), parentWidget.height()));
  254. QPoint insideSlice1 = allSliceViews[1]->mapToGlobal(
  255. QPoint(allSliceViews[1]->width()/2 - 50, allSliceViews[1]->height() - 50));
  256. QPoint insideSlice1edge = allSliceViews[1]->mapToGlobal(
  257. QPoint(allSliceViews[1]->width() - 5, allSliceViews[1]->height() - 100));
  258. QPoint insideSlice2 = allSliceViews[2]->mapToGlobal(
  259. QPoint(allSliceViews[2]->width()/2, allSliceViews[2]->height()/2));
  260. QPoint insideSlice0bottomRightCorner = allSliceViews[0]->mapToGlobal(
  261. QPoint(allSliceViews[0]->width()-1, allSliceViews[0]->height()-1));
  262. QPoint insideSlice0topLeftCorner = allSliceViews[0]->mapToGlobal(
  263. QPoint(0,0));
  264. parentWidget.hide();
  265. // Make sure the magnify widget magnifies right away when shown with the crosshair inside
  266. // an observed ctkVTKOpenGLNativeWidget
  267. QCursor::setPos(insideSlice0);
  268. parentWidget.show();
  269. if (!runBaselineTest(time, app, magnify, allSliceViews[0], true,
  270. baselineDirectory, testType, "a",
  271. "magnify widget first shown with crosshair inside observed widget"))
  272. {
  273. return EXIT_FAILURE;
  274. }
  275. parentWidget.hide();
  276. // Make sure the magnify widget shows blank right away when shown with the crosshair
  277. // outside the observed ctkVTKOpenGLNativeWidget(s)
  278. QCursor::setPos(outside);
  279. parentWidget.show();
  280. if (!runBaselineTest(time, app, magnify, &parentWidget, false,
  281. baselineDirectory, testType, "b",
  282. "magnify widget first shown with crosshair outside observed widget"))
  283. {
  284. return EXIT_FAILURE;
  285. }
  286. // Test magnification after move to allSliceViews[1]
  287. QCursor::setPos(insideSlice1);
  288. if (!runBaselineTest(time, app, magnify, allSliceViews[1], true,
  289. baselineDirectory, testType, "c",
  290. "crosshair moved inside 2nd observed widget the first time"))
  291. {
  292. return EXIT_FAILURE;
  293. }
  294. // Test magnification after move within allSliceViews[1] close to border
  295. QCursor::setPos(insideSlice1edge);
  296. if (!runBaselineTest(time, app, magnify, allSliceViews[1], true,
  297. baselineDirectory, testType, "d",
  298. "crosshair moved inside 2nd observed widget the second time"))
  299. {
  300. return EXIT_FAILURE;
  301. }
  302. // Test magnification after move outside an observed widget (should be blank)
  303. QCursor::setPos(insideSlice2);
  304. if (!runBaselineTest(time, app, magnify, allSliceViews[2], true,
  305. baselineDirectory, testType, "e",
  306. "crosshair moved inside unobserved widget"))
  307. {
  308. return EXIT_FAILURE;
  309. }
  310. // Test magnification after move back inside an observed widget (at extreme bottom
  311. // right corner)
  312. QCursor::setPos(insideSlice0bottomRightCorner);
  313. if (!runBaselineTest(time, app, magnify, allSliceViews[0], true,
  314. baselineDirectory, testType, "f",
  315. "crosshair moved to bottom-right corner of observed widget"))
  316. {
  317. return EXIT_FAILURE;
  318. }
  319. // Test magnification after move back inside an observed widget (at extreme top left
  320. // corner)
  321. QCursor::setPos(insideSlice0topLeftCorner);
  322. if (!runBaselineTest(time, app, magnify, allSliceViews[0], true,
  323. baselineDirectory, testType, "g",
  324. "crosshair moved to top-left corner of observed widget"))
  325. {
  326. return EXIT_FAILURE;
  327. }
  328. // Go back inside the widget
  329. QCursor::setPos(insideSlice0);
  330. if (!runBaselineTest(time, app, magnify, allSliceViews[0], true,
  331. baselineDirectory, testType, "a",
  332. "crosshair moved again inside observed widget"))
  333. {
  334. return EXIT_FAILURE;
  335. }
  336. // Test enabling observing render window events (trigger a render window event by
  337. // changing the image data)
  338. observeRenderWindowEvents = true;
  339. magnify->setObserveRenderWindowEvents(observeRenderWindowEvents);
  340. if (magnify->observeRenderWindowEvents() != observeRenderWindowEvents)
  341. {
  342. std::cerr << "ctkVTKMagnifyView:setObserveRenderWindowEvents failed. "
  343. << magnify->observeRenderWindowEvents() << std::endl;
  344. return EXIT_FAILURE;
  345. }
  346. vtkImageGaussianSmooth * gaussian = vtkImageGaussianSmooth::New();
  347. #if (VTK_MAJOR_VERSION <= 5)
  348. gaussian->SetInput(image);
  349. #else
  350. gaussian->SetInputConnection(imagePort);
  351. #endif
  352. gaussian->SetRadiusFactors(5,5);
  353. gaussian->Update();
  354. #if (VTK_MAJOR_VERSION <= 5)
  355. allSliceViews[0]->setImageData(gaussian->GetOutput());
  356. #else
  357. allSliceViews[0]->setImageDataConnection(gaussian->GetOutputPort());
  358. #endif
  359. allSliceViews[0]->scheduleRender();
  360. if (!runBaselineTest(time, app, magnify, allSliceViews[0], true,
  361. baselineDirectory, testType, "h",
  362. "after Gaussian blur when observing render window events"))
  363. {
  364. return EXIT_FAILURE;
  365. }
  366. // Test disabling observing render window events (trigger a render window event by
  367. // changing the image data)
  368. observeRenderWindowEvents = false;
  369. magnify->setObserveRenderWindowEvents(observeRenderWindowEvents);
  370. if (magnify->observeRenderWindowEvents() != observeRenderWindowEvents)
  371. {
  372. std::cerr << "ctkVTKMagnifyView:setObserveRenderWindowEvents failed. "
  373. << magnify->observeRenderWindowEvents() << std::endl;
  374. return EXIT_FAILURE;
  375. }
  376. #if (VTK_MAJOR_VERSION <= 5)
  377. gaussian->SetInput(image);
  378. #else
  379. gaussian->SetInputConnection(imageReader->GetOutputPort());
  380. #endif
  381. gaussian->SetRadiusFactors(0,0);
  382. gaussian->Update();
  383. #if (VTK_MAJOR_VERSION <= 5)
  384. allSliceViews[0]->setImageData(gaussian->GetOutput());
  385. #else
  386. allSliceViews[0]->setImageDataConnection(gaussian->GetOutputPort());
  387. #endif
  388. allSliceViews[0]->scheduleRender();
  389. if (!runBaselineTest(time, app, magnify, allSliceViews[0], true,
  390. baselineDirectory, testType, "h",
  391. "after Gaussian blur when not observing render window events"))
  392. {
  393. return EXIT_FAILURE;
  394. }
  395. // Test changing the update interval
  396. magnify->setUpdateInterval(time * 2);
  397. magnify->setObserveRenderWindowEvents(true);
  398. #if (VTK_MAJOR_VERSION <= 5)
  399. allSliceViews[0]->setImageData(image);
  400. #else
  401. allSliceViews[0]->setImageDataConnection(imagePort);
  402. #endif
  403. allSliceViews[0]->scheduleRender();
  404. QCursor::setPos(insideSlice0bottomRightCorner);
  405. // It should be waiting to update here
  406. if (!runBaselineTest(time, app, magnify, allSliceViews[0], true,
  407. baselineDirectory, testType, "h",
  408. "after changing update interval: updated too quickly"))
  409. {
  410. return EXIT_FAILURE;
  411. }
  412. // It should have updated by now
  413. if (!runBaselineTest(time + 50, app, magnify, allSliceViews[0], true,
  414. baselineDirectory, testType, "f",
  415. "after changing update interval: didn't update after waiting"))
  416. {
  417. return EXIT_FAILURE;
  418. }
  419. gaussian->Delete();
  420. return EXIT_SUCCESS;
  421. }