ctkDICOMModel.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  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 <QStringList>
  12. #include <QSqlDriver>
  13. #include <QSqlError>
  14. #include <QSqlQuery>
  15. #include <QSqlQueryModel>
  16. #include <QSqlRecord>
  17. #include <QTime>
  18. #include <QDebug>
  19. // ctkDICOM includes
  20. #include "ctkDICOMModel.h"
  21. struct Node;
  22. //------------------------------------------------------------------------------
  23. class ctkDICOMModelPrivate:public ctkPrivate<ctkDICOMModel>
  24. {
  25. public:
  26. ctkDICOMModelPrivate();
  27. virtual ~ctkDICOMModelPrivate();
  28. void init();
  29. enum IndexType{
  30. RootType,
  31. PatientType,
  32. StudyType,
  33. SeriesType,
  34. ImageType
  35. };
  36. void fetch(const QModelIndex& indexValue, int limit);
  37. Node* createNode(int row, const QModelIndex& parentValue)const;
  38. Node* nodeFromIndex(const QModelIndex& indexValue)const;
  39. //QModelIndexList indexListFromNode(const Node* node)const;
  40. //QModelIndexList modelIndexList(Node* node = 0)const;
  41. //int childrenCount(Node* node = 0)const;
  42. // move it in the Node struct
  43. QVariant value(Node* parentValue, int row, int field)const;
  44. QVariant value(const QModelIndex& indexValue, int row, int field)const;
  45. QString generateQuery(const QString& fields, const QString& table, const QString& conditions = QString())const;
  46. void updateQueries(Node* node)const;
  47. Node* RootNode;
  48. QSqlDatabase DataBase;
  49. QStringList Headers;
  50. QString Sort;
  51. };
  52. //------------------------------------------------------------------------------
  53. // 1 node per row
  54. struct Node
  55. {
  56. ~Node()
  57. {
  58. foreach(Node* node, this->Children)
  59. {
  60. delete node;
  61. }
  62. this->Children.clear();
  63. }
  64. ctkDICOMModelPrivate::IndexType Type;
  65. Node* Parent;
  66. QVector<Node*> Children;
  67. int Row;
  68. QSqlQuery Query;
  69. QString UID;
  70. int RowCount;
  71. bool AtEnd;
  72. bool Fetching;
  73. };
  74. //------------------------------------------------------------------------------
  75. ctkDICOMModelPrivate::ctkDICOMModelPrivate()
  76. {
  77. this->RootNode = 0;
  78. }
  79. //------------------------------------------------------------------------------
  80. ctkDICOMModelPrivate::~ctkDICOMModelPrivate()
  81. {
  82. delete this->RootNode;
  83. this->RootNode = 0;
  84. }
  85. //------------------------------------------------------------------------------
  86. void ctkDICOMModelPrivate::init()
  87. {
  88. this->Headers = QStringList() << "Name" << "Age" << "Scan" << "Date" << "Subject ID"
  89. << "Number" << "Institution" << "Referrer" << "Performer";
  90. }
  91. //------------------------------------------------------------------------------
  92. Node* ctkDICOMModelPrivate::nodeFromIndex(const QModelIndex& indexValue)const
  93. {
  94. return indexValue.isValid() ? reinterpret_cast<Node*>(indexValue.internalPointer()) : this->RootNode;
  95. }
  96. /*
  97. //------------------------------------------------------------------------------
  98. QModelIndexList ctkDICOMModelPrivate::indexListFromNode(const Node* node)const
  99. {
  100. CTK_P(const ctkDICOMModel);
  101. Q_ASSERT(node);
  102. QModelIndexList indexList;
  103. Node* parentNode = node->Parent;
  104. if (parentNode == 0)
  105. {
  106. return indexList;
  107. }
  108. int field = parentNode->Query.record().indexOf("UID");
  109. int row = -1;
  110. for (row = 0; row < parentNode->RowCount; ++row)
  111. {
  112. QString uid = this->value(parentNode, row, field).toString();
  113. if (uid == node->UID)
  114. {
  115. break;
  116. }
  117. }
  118. if (row >= parentNode->RowCount)
  119. {
  120. return indexList;
  121. }
  122. for (int column = 0 ; column < this->Headers.size(); ++column)
  123. {
  124. indexList.append(p->createIndex(row, column, parentNode));
  125. }
  126. return indexList;
  127. }
  128. //------------------------------------------------------------------------------
  129. QModelIndexList ctkDICOMModelPrivate::modelIndexList(Node* node)const
  130. {
  131. QModelIndexList list;
  132. if (node == 0)
  133. {
  134. node = this->RootNode;
  135. }
  136. foreach(Node* child, node->Children)
  137. {
  138. list.append(this->indexListFromNode(child));
  139. }
  140. foreach(Node* child, node->Children)
  141. {
  142. list.append(this->modelIndexList(child));
  143. }
  144. return list;
  145. }
  146. //------------------------------------------------------------------------------
  147. int ctkDICOMModelPrivate::childrenCount(Node* node)const
  148. {
  149. int count = 0;
  150. if (node == 0)
  151. {
  152. node = this->RootNode;
  153. }
  154. count += node->Children.size();
  155. foreach(Node* child, node->Children)
  156. {
  157. count += this->childrenCount(child);
  158. }
  159. return count;
  160. }
  161. */
  162. //------------------------------------------------------------------------------
  163. Node* ctkDICOMModelPrivate::createNode(int row, const QModelIndex& parentValue)const
  164. {
  165. Node* node = new Node;
  166. Node* nodeParent = 0;
  167. if (row == -1)
  168. {// root node
  169. node->Type = ctkDICOMModelPrivate::RootType;
  170. node->Parent = 0;
  171. }
  172. else
  173. {
  174. nodeParent = this->nodeFromIndex(parentValue);
  175. nodeParent->Children.push_back(node);
  176. node->Parent = nodeParent;
  177. node->Type = ctkDICOMModelPrivate::IndexType(nodeParent->Type + 1);
  178. }
  179. node->Row = row;
  180. if (node->Type != ctkDICOMModelPrivate::RootType)
  181. {
  182. int field = nodeParent->Query.record().indexOf("UID");
  183. node->UID = this->value(parentValue, row, field).toString();
  184. }
  185. node->RowCount = 0;
  186. node->AtEnd = false;
  187. node->Fetching = false;
  188. this->updateQueries(node);
  189. return node;
  190. }
  191. //------------------------------------------------------------------------------
  192. QVariant ctkDICOMModelPrivate::value(const QModelIndex& parentValue, int row, int column) const
  193. {
  194. Node* node = this->nodeFromIndex(parentValue);
  195. if (row >= node->RowCount)
  196. {
  197. const_cast<ctkDICOMModelPrivate *>(this)->fetch(parentValue, row + 256);
  198. }
  199. return this->value(node, row, column);
  200. }
  201. //------------------------------------------------------------------------------
  202. QVariant ctkDICOMModelPrivate::value(Node* parentValue, int row, int column) const
  203. {
  204. Q_ASSERT(row < parentValue->RowCount);
  205. if (!parentValue->Query.seek(row))
  206. {
  207. qDebug() << parentValue->Query.lastError();
  208. Q_ASSERT(parentValue->Query.seek(row));
  209. return QVariant();
  210. }
  211. QVariant res = parentValue->Query.value(column);
  212. Q_ASSERT(res.isValid());
  213. return res;
  214. }
  215. //------------------------------------------------------------------------------
  216. QString ctkDICOMModelPrivate::generateQuery(const QString& fields, const QString& table, const QString& conditions)const
  217. {
  218. QString res = QString("SELECT ") + fields + QString(" FROM ") + table;
  219. if (!conditions.isEmpty())
  220. {
  221. res += QString(" WHERE ") + conditions;
  222. }
  223. if (!this->Sort.isEmpty())
  224. {
  225. res += QString(" ORDER BY ") + this->Sort;
  226. }
  227. return res;
  228. }
  229. //------------------------------------------------------------------------------
  230. void ctkDICOMModelPrivate::updateQueries(Node* node)const
  231. {
  232. // are you kidding me, it should be virtualized here :-)
  233. QString query;
  234. switch(node->Type)
  235. {
  236. default:
  237. Q_ASSERT(node->Type == ctkDICOMModelPrivate::RootType);
  238. break;
  239. case ctkDICOMModelPrivate::RootType:
  240. //query = QString("SELECT FROM ");
  241. query = this->generateQuery("UID as UID, PatientsName as Name, PatientsAge as Age, PatientsBirthDate as Date, PatientID as \"Subject ID\"","Patients");
  242. break;
  243. case ctkDICOMModelPrivate::PatientType:
  244. //query = QString("SELECT FROM Studies WHERE PatientsUID='%1'").arg(node->UID);
  245. query = this->generateQuery("StudyInstanceUID as UID, StudyDescription as Name, ModalitiesInStudy as Scan, StudyDate as Date, AccessionNumber as Number, ReferringPhysician as Institution, ReferringPhysician as Referrer, PerformingPysiciansName as Performer", "Studies",QString("PatientsUID='%1'").arg(node->UID));
  246. break;
  247. case ctkDICOMModelPrivate::StudyType:
  248. //query = QString("SELECT SeriesInstanceUID as UID, SeriesDescription as Name, BodyPartExamined as Scan, SeriesDate as Date, AcquisitionNumber as Number FROM Series WHERE StudyInstanceUID='%1'").arg(node->UID);
  249. query = this->generateQuery("SeriesInstanceUID as UID, SeriesDescription as Name, BodyPartExamined as Scan, SeriesDate as Date, AcquisitionNumber as Number","Series",QString("StudyInstanceUID='%1'").arg(node->UID));
  250. break;
  251. case ctkDICOMModelPrivate::SeriesType:
  252. //query = QString("SELECT Filename as UID, Filename as Name, SeriesInstanceUID as Date FROM Images WHERE SeriesInstanceUID='%1'").arg(node->UID);
  253. query = this->generateQuery("Filename as UID, Filename as Name, SeriesInstanceUID as Date", "Images", QString("SeriesInstanceUID='%1'").arg(node->UID));
  254. break;
  255. case ctkDICOMModelPrivate::ImageType:
  256. break;
  257. }
  258. node->Query = QSqlQuery(query, this->DataBase);
  259. foreach(Node* child, node->Children)
  260. {
  261. this->updateQueries(child);
  262. }
  263. }
  264. //------------------------------------------------------------------------------
  265. void ctkDICOMModelPrivate::fetch(const QModelIndex& indexValue, int limit)
  266. {
  267. CTK_P(ctkDICOMModel);
  268. Node* node = this->nodeFromIndex(indexValue);
  269. if (node->AtEnd || limit <= node->RowCount || node->Fetching/*|| bottom.column() == -1*/)
  270. {
  271. return;
  272. }
  273. node->Fetching = true;
  274. int newRowCount;
  275. const int oldRowCount = node->RowCount;
  276. // try to seek directly
  277. if (node->Query.seek(limit - 1))
  278. {
  279. newRowCount = limit;
  280. }
  281. else
  282. {
  283. newRowCount = qMax(oldRowCount, 1);
  284. if (node->Query.seek(newRowCount - 1))
  285. {
  286. while (node->Query.next())
  287. {
  288. ++newRowCount;
  289. }
  290. }
  291. else
  292. {
  293. // empty or invalid query
  294. newRowCount = 0;
  295. }
  296. node->AtEnd = true; // this is the end.
  297. }
  298. if (newRowCount > 0 && newRowCount > node->RowCount)
  299. {
  300. p->beginInsertRows(indexValue, node->RowCount, newRowCount - 1);
  301. node->RowCount = newRowCount;
  302. node->Fetching = false;
  303. p->endInsertRows();
  304. }
  305. else
  306. {
  307. node->RowCount = newRowCount;
  308. node->Fetching = false;
  309. }
  310. }
  311. //------------------------------------------------------------------------------
  312. ctkDICOMModel::ctkDICOMModel(QObject* parentValue)
  313. {
  314. CTK_INIT_PRIVATE(ctkDICOMModel);
  315. ctk_d()->init();
  316. }
  317. //------------------------------------------------------------------------------
  318. ctkDICOMModel::~ctkDICOMModel()
  319. {
  320. }
  321. //------------------------------------------------------------------------------
  322. bool ctkDICOMModel::canFetchMore ( const QModelIndex & parentValue ) const
  323. {
  324. CTK_D(const ctkDICOMModel);
  325. Node* node = d->nodeFromIndex(parentValue);
  326. return !node->AtEnd;
  327. }
  328. //------------------------------------------------------------------------------
  329. int ctkDICOMModel::columnCount ( const QModelIndex & _parent ) const
  330. {
  331. CTK_D(const ctkDICOMModel);
  332. Q_UNUSED(_parent);
  333. return d->RootNode != 0 ? d->Headers.size() : 0;
  334. }
  335. //------------------------------------------------------------------------------
  336. QVariant ctkDICOMModel::data ( const QModelIndex & indexValue, int role ) const
  337. {
  338. CTK_D(const ctkDICOMModel);
  339. if (role & ~(Qt::DisplayRole | Qt::EditRole))
  340. {
  341. return QVariant();
  342. }
  343. QModelIndex indexParent = this->parent(indexValue);
  344. Node* parentNode = d->nodeFromIndex(indexParent);
  345. if (indexValue.row() >= parentNode->RowCount)
  346. {
  347. const_cast<ctkDICOMModelPrivate *>(d)->fetch(indexValue, indexValue.row());
  348. }
  349. /*
  350. if (!node->Query.seek(indexValue.row()))
  351. {
  352. qDebug() << node->Query.lastError();
  353. return QVariant();
  354. }
  355. */
  356. int field = parentNode->Query.record().indexOf(d->Headers[indexValue.column()]);
  357. if (field < 0)
  358. {
  359. return QString();
  360. }
  361. return d->value(indexParent, indexValue.row(), field);
  362. //return node->Query.value(field);
  363. }
  364. //------------------------------------------------------------------------------
  365. void ctkDICOMModel::fetchMore ( const QModelIndex & parentValue )
  366. {
  367. CTK_D(ctkDICOMModel);
  368. Node* node = d->nodeFromIndex(parentValue);
  369. d->fetch(parentValue, qMax(node->RowCount, 0) + 256);
  370. }
  371. //------------------------------------------------------------------------------
  372. Qt::ItemFlags ctkDICOMModel::flags ( const QModelIndex & indexValue ) const
  373. {
  374. return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  375. }
  376. //------------------------------------------------------------------------------
  377. bool ctkDICOMModel::hasChildren ( const QModelIndex & parentValue ) const
  378. {
  379. CTK_D(const ctkDICOMModel);
  380. if (parentValue.column() > 0)
  381. {
  382. return false;
  383. }
  384. Node* node = d->nodeFromIndex(parentValue);
  385. if (!node)
  386. {
  387. return false;
  388. }
  389. if (node->RowCount == 0 && !node->AtEnd)
  390. {
  391. //const_cast<qCTKDCMTKModelPrivate*>(d)->fetch(parentValue, 1);
  392. return node->Query.seek(0);
  393. }
  394. return node->RowCount > 0;
  395. }
  396. //------------------------------------------------------------------------------
  397. QVariant ctkDICOMModel::headerData(int section, Qt::Orientation orientation, int role)const
  398. {
  399. CTK_D(const ctkDICOMModel);
  400. if (role & ~(Qt::DisplayRole | Qt::EditRole))
  401. {
  402. return QVariant();
  403. }
  404. if (orientation == Qt::Vertical)
  405. {
  406. return section;
  407. }
  408. Q_ASSERT(orientation == Qt::Horizontal);
  409. Q_ASSERT(section < d->Headers.size());
  410. return d->Headers[section];
  411. }
  412. //------------------------------------------------------------------------------
  413. QModelIndex ctkDICOMModel::index ( int row, int column, const QModelIndex & parentValue ) const
  414. {
  415. CTK_D(const ctkDICOMModel);
  416. if (d->RootNode == 0 || parentValue.column() > 0)
  417. {
  418. return QModelIndex();
  419. }
  420. Node* parentNode = d->nodeFromIndex(parentValue);
  421. int field = parentNode->Query.record().indexOf("UID");
  422. QString uid = d->value(parentValue, row, field).toString();
  423. Node* node = 0;
  424. foreach(Node* tmpNode, parentNode->Children)
  425. {
  426. if (tmpNode->UID == uid)
  427. {
  428. node = tmpNode;
  429. break;
  430. }
  431. }
  432. if (node == 0)
  433. {
  434. node = d->createNode(row, parentValue);
  435. }
  436. return this->createIndex(row, column, node);
  437. }
  438. //------------------------------------------------------------------------------
  439. QModelIndex ctkDICOMModel::parent ( const QModelIndex & indexValue ) const
  440. {
  441. CTK_D(const ctkDICOMModel);
  442. Node* node = d->nodeFromIndex(indexValue);
  443. Q_ASSERT(node);
  444. Node* parentNode = node->Parent;
  445. if (parentNode == 0)
  446. {// node is root
  447. return QModelIndex();
  448. }
  449. return parentNode == d->RootNode ? QModelIndex() : this->createIndex(parentNode->Row, 0, parentNode);
  450. /* need to recalculate the parent row
  451. Node* greatParentNode = parentNode->Parent;
  452. if (greatParentNode == 0)
  453. {
  454. return QModelIndex();
  455. }
  456. int field = greatParentNode->Query.record().indexOf("UID");
  457. int row = -1;
  458. for (row = 0; row < greatParentNode->RowCount; ++row)
  459. {
  460. QString uid = d->value(greatParentNode, row, field).toString();
  461. if (uid == parentNode->UID)
  462. {
  463. break;
  464. }
  465. }
  466. Q_ASSERT(row < greatParentNode->RowCount);
  467. return this->createIndex(row, 0, parentNode);
  468. */
  469. }
  470. //------------------------------------------------------------------------------
  471. int ctkDICOMModel::rowCount ( const QModelIndex & parentValue ) const
  472. {
  473. CTK_D(const ctkDICOMModel);
  474. if (d->RootNode == 0 || parentValue.column() > 0)
  475. {
  476. return 0;
  477. }
  478. Node* node = d->nodeFromIndex(parentValue);
  479. Q_ASSERT(node);
  480. if (node->RowCount == 0 && !node->AtEnd)
  481. {
  482. //const_cast<ctkDICOMModelPrivate*>(d)->fetch(parentValue, 256);
  483. }
  484. return node->RowCount;
  485. }
  486. //------------------------------------------------------------------------------
  487. void ctkDICOMModel::setDatabase(const QSqlDatabase &db)
  488. {
  489. CTK_D(ctkDICOMModel);
  490. this->beginResetModel();
  491. d->DataBase = db;
  492. delete d->RootNode;
  493. d->RootNode = 0;
  494. if (d->DataBase.tables().empty())
  495. {
  496. //Q_ASSERT(d->DataBase.isOpen());
  497. this->endResetModel();
  498. return;
  499. }
  500. d->RootNode = d->createNode(-1, QModelIndex());
  501. this->endResetModel();
  502. // TODO, use hasQuerySize everywhere, not only in setDataBase()
  503. bool hasQuerySize = d->RootNode->Query.driver()->hasFeature(QSqlDriver::QuerySize);
  504. if (hasQuerySize && d->RootNode->Query.size() > 0)
  505. {
  506. int newRowCount= d->RootNode->Query.size();
  507. beginInsertRows(QModelIndex(), 0, qMax(0, newRowCount - 1));
  508. d->RootNode->RowCount = newRowCount;
  509. d->RootNode->AtEnd = true;
  510. endInsertRows();
  511. }
  512. d->fetch(QModelIndex(), 256);
  513. }
  514. //------------------------------------------------------------------------------
  515. void ctkDICOMModel::sort(int column, Qt::SortOrder order)
  516. {
  517. CTK_D(ctkDICOMModel);
  518. /* The following would work if there is no fetch involved.
  519. ORDER BY doesn't just apply on the fetched item. By sorting
  520. new items can show up in the model, and we need to be more
  521. careful
  522. emit layoutAboutToBeChanged();
  523. QModelIndexList oldIndexList = d->modelIndexList();
  524. d->Sort = QString("\"%1\" %2")
  525. .arg(d->Headers[column])
  526. .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
  527. d->updateQueries(d->RootNode);
  528. QModelIndexList newIndexList = d->modelIndexList();
  529. Q_ASSERT(oldIndexList.count() == newIndexList.count());
  530. this->changePersistentIndexList(oldIndexList, newIndexList);
  531. emit layoutChanged();
  532. */
  533. this->beginResetModel();
  534. delete d->RootNode;
  535. d->RootNode = 0;
  536. d->Sort = QString("\"%1\" %2")
  537. .arg(d->Headers[column])
  538. .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
  539. d->RootNode = d->createNode(-1, QModelIndex());
  540. this->endResetModel();
  541. }
  542. //------------------------------------------------------------------------------
  543. bool ctkDICOMModel::setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role)
  544. {
  545. CTK_D(ctkDICOMModel);
  546. if (role & ~(Qt::DisplayRole | Qt::EditRole))
  547. {
  548. return false;
  549. }
  550. if (orientation == Qt::Vertical)
  551. {
  552. return false;
  553. }
  554. if (value.toString() == d->Headers[section])
  555. {
  556. return false;
  557. }
  558. d->Headers[section] = value.toString();
  559. emit this->headerDataChanged(orientation, section, section);
  560. return true;
  561. }