ctkDICOMModel.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. // Qt includes
  2. #include <QStringList>
  3. #include <QSqlDriver>
  4. #include <QSqlError>
  5. #include <QSqlQuery>
  6. #include <QSqlQueryModel>
  7. #include <QSqlRecord>
  8. #include <QTime>
  9. #include <QDebug>
  10. // ctkDICOM includes
  11. #include "ctkDICOMModel.h"
  12. struct Node;
  13. //------------------------------------------------------------------------------
  14. class ctkDICOMModelPrivate:public qCTKPrivate<ctkDICOMModel>
  15. {
  16. public:
  17. ctkDICOMModelPrivate();
  18. ~ctkDICOMModelPrivate();
  19. void init();
  20. enum IndexType{
  21. RootType,
  22. PatientType,
  23. StudyType,
  24. SeriesType,
  25. ImageType
  26. };
  27. void fetch(const QModelIndex& index, int limit);
  28. Node* createNode(int row, int column, const QModelIndex& parent)const;
  29. Node* nodeFromIndex(const QModelIndex& index)const;
  30. QVariant value(const QModelIndex& index, int row, int field)const;
  31. QString generateQuery(const QString& fields, const QString& table, const QString& conditions = QString())const;
  32. void updateQueries(Node* node)const;
  33. Node* RootNode;
  34. QSqlDatabase DataBase;
  35. QStringList Headers;
  36. QString Sort;
  37. };
  38. //------------------------------------------------------------------------------
  39. struct Node
  40. {
  41. ~Node()
  42. {
  43. foreach(Node* node, this->Children)
  44. {
  45. delete node;
  46. }
  47. this->Children.clear();
  48. }
  49. ctkDICOMModelPrivate::IndexType Type;
  50. Node* Parent;
  51. QVector<Node*> Children;
  52. int Row;
  53. int Column;
  54. QSqlQuery Query;
  55. QString UID;
  56. int RowCount;
  57. bool End;
  58. bool Fetching;
  59. };
  60. //------------------------------------------------------------------------------
  61. ctkDICOMModelPrivate::ctkDICOMModelPrivate()
  62. {
  63. this->RootNode = 0;
  64. }
  65. //------------------------------------------------------------------------------
  66. ctkDICOMModelPrivate::~ctkDICOMModelPrivate()
  67. {
  68. delete this->RootNode;
  69. this->RootNode = 0;
  70. }
  71. //------------------------------------------------------------------------------
  72. void ctkDICOMModelPrivate::init()
  73. {
  74. QCTK_P(ctkDICOMModel);
  75. this->Headers = QStringList() << "Name" << "Age" << "Scan" << "Date" << "Subject ID"
  76. << "Number" << "Institution" << "Referrer" << "Performer";
  77. }
  78. //------------------------------------------------------------------------------
  79. Node* ctkDICOMModelPrivate::nodeFromIndex(const QModelIndex& index)const
  80. {
  81. return index.isValid() ? reinterpret_cast<Node*>(index.internalPointer()) : this->RootNode;
  82. }
  83. //------------------------------------------------------------------------------
  84. Node* ctkDICOMModelPrivate::createNode(int row, int column, const QModelIndex& parent)const
  85. {
  86. QCTK_P(const ctkDICOMModel);
  87. Node* node = new Node;
  88. Node* nodeParent = 0;
  89. if (row == -1 || column == -1)
  90. {// root node
  91. node->Type = ctkDICOMModelPrivate::RootType;
  92. node->Parent = 0;
  93. }
  94. else
  95. {
  96. nodeParent = this->nodeFromIndex(parent);
  97. nodeParent->Children.push_back(node);
  98. node->Parent = (nodeParent == this->RootNode ? 0: nodeParent);
  99. node->Type = ctkDICOMModelPrivate::IndexType(nodeParent->Type + 1);
  100. }
  101. node->Row = row;
  102. node->Column = column;
  103. if (node->Type != ctkDICOMModelPrivate::RootType)
  104. {
  105. int field = nodeParent->Query.record().indexOf("UID");
  106. node->UID = this->value(parent, row, field).toString();
  107. }
  108. node->RowCount = 0;
  109. node->End = false;
  110. node->Fetching = false;
  111. this->updateQueries(node);
  112. return node;
  113. }
  114. //------------------------------------------------------------------------------
  115. QVariant ctkDICOMModelPrivate::value(const QModelIndex& parent, int row, int column) const
  116. {
  117. Node* node = this->nodeFromIndex(parent);
  118. if (row >= node->RowCount)
  119. {
  120. const_cast<ctkDICOMModelPrivate *>(this)->fetch(parent, row + 256);
  121. }
  122. if (!node->Query.seek(row))
  123. {
  124. qDebug() << node->Query.lastError();
  125. return QVariant();
  126. }
  127. return node->Query.value(column);
  128. }
  129. //------------------------------------------------------------------------------
  130. QString ctkDICOMModelPrivate::generateQuery(const QString& fields, const QString& table, const QString& conditions)const
  131. {
  132. QString res = QString("SELECT ") + fields + QString(" FROM ") + table;
  133. if (!conditions.isEmpty())
  134. {
  135. res += QString(" WHERE ") + conditions;
  136. }
  137. if (!this->Sort.isEmpty())
  138. {
  139. res += QString(" ORDER BY ") + this->Sort;
  140. }
  141. return res;
  142. }
  143. //------------------------------------------------------------------------------
  144. void ctkDICOMModelPrivate::updateQueries(Node* node)const
  145. {
  146. // are you kidding me, it should be virtualized here :-)
  147. QString query;
  148. switch(node->Type)
  149. {
  150. default:
  151. Q_ASSERT(node->Type == ctkDICOMModelPrivate::RootType);
  152. break;
  153. case ctkDICOMModelPrivate::RootType:
  154. //query = QString("SELECT FROM ");
  155. query = this->generateQuery("UID as UID, PatientsName as Name, PatientsAge as Age, PatientsBirthDate as Date, PatientID as \"Subject ID\"","Patients");
  156. break;
  157. case ctkDICOMModelPrivate::PatientType:
  158. //query = QString("SELECT FROM Studies WHERE PatientsUID='%1'").arg(node->UID);
  159. 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));
  160. break;
  161. case ctkDICOMModelPrivate::StudyType:
  162. //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);
  163. 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));
  164. break;
  165. case ctkDICOMModelPrivate::SeriesType:
  166. //query = QString("SELECT Filename as UID, Filename as Name, SeriesInstanceUID as Date FROM Images WHERE SeriesInstanceUID='%1'").arg(node->UID);
  167. query = this->generateQuery("Filename as UID, Filename as Name, SeriesInstanceUID as Date", "Images", QString("SeriesInstanceUID='%1'").arg(node->UID));
  168. break;
  169. case ctkDICOMModelPrivate::ImageType:
  170. break;
  171. }
  172. node->Query = QSqlQuery(query, this->DataBase);
  173. foreach(Node* child, node->Children)
  174. {
  175. this->updateQueries(child);
  176. }
  177. }
  178. //------------------------------------------------------------------------------
  179. void ctkDICOMModelPrivate::fetch(const QModelIndex& index, int limit)
  180. {
  181. QCTK_P(ctkDICOMModel);
  182. Node* node = this->nodeFromIndex(index);
  183. if (node->End || limit <= node->RowCount || node->Fetching/*|| bottom.column() == -1*/)
  184. {
  185. return;
  186. }
  187. node->Fetching = true;
  188. int newRowCount;
  189. const int oldRowCount = node->RowCount;
  190. // try to seek directly
  191. if (node->Query.seek(limit - 1))
  192. {
  193. newRowCount = limit;
  194. }
  195. else
  196. {
  197. newRowCount = qMax(oldRowCount, 1);
  198. if (node->Query.seek(newRowCount - 1))
  199. {
  200. while (node->Query.next())
  201. {
  202. ++newRowCount;
  203. }
  204. }
  205. else
  206. {
  207. // empty or invalid query
  208. newRowCount = 0;
  209. }
  210. node->End = true; // this is the end.
  211. }
  212. if (newRowCount > 0 && newRowCount > node->RowCount)
  213. {
  214. p->beginInsertRows(index, node->RowCount, newRowCount - 1);
  215. node->RowCount = newRowCount;
  216. node->Fetching = false;
  217. p->endInsertRows();
  218. }
  219. else
  220. {
  221. node->RowCount = newRowCount;
  222. node->Fetching = false;
  223. }
  224. }
  225. //------------------------------------------------------------------------------
  226. ctkDICOMModel::ctkDICOMModel(QObject* parent)
  227. {
  228. QCTK_INIT_PRIVATE(ctkDICOMModel);
  229. qctk_d()->init();
  230. }
  231. //------------------------------------------------------------------------------
  232. ctkDICOMModel::~ctkDICOMModel()
  233. {
  234. }
  235. //------------------------------------------------------------------------------
  236. bool ctkDICOMModel::canFetchMore ( const QModelIndex & parent ) const
  237. {
  238. QCTK_D(const ctkDICOMModel);
  239. Node* node = d->nodeFromIndex(parent);
  240. return !node->End;
  241. }
  242. //------------------------------------------------------------------------------
  243. int ctkDICOMModel::columnCount ( const QModelIndex & _parent ) const
  244. {
  245. QCTK_D(const ctkDICOMModel);
  246. Q_UNUSED(_parent);
  247. return d->Headers.size();
  248. }
  249. //------------------------------------------------------------------------------
  250. QVariant ctkDICOMModel::data ( const QModelIndex & index, int role ) const
  251. {
  252. QCTK_D(const ctkDICOMModel);
  253. if (role & ~(Qt::DisplayRole | Qt::EditRole))
  254. {
  255. return QVariant();
  256. }
  257. QModelIndex indexParent = this->parent(index);
  258. Node* node = d->nodeFromIndex(indexParent);
  259. Q_ASSERT(node->Row == indexParent.row());
  260. if (index.row() >= node->RowCount)
  261. {
  262. const_cast<ctkDICOMModelPrivate *>(d)->fetch(index, index.row());
  263. }
  264. /*
  265. if (!node->Query.seek(index.row()))
  266. {
  267. qDebug() << node->Query.lastError();
  268. return QVariant();
  269. }
  270. */
  271. int field = node->Query.record().indexOf(d->Headers[index.column()]);
  272. if (field < 0)
  273. {
  274. return QVariant();
  275. }
  276. return d->value(indexParent, index.row(), field);
  277. //return node->Query.value(field);
  278. }
  279. //------------------------------------------------------------------------------
  280. void ctkDICOMModel::fetchMore ( const QModelIndex & parent )
  281. {
  282. QCTK_D(ctkDICOMModel);
  283. Node* node = d->nodeFromIndex(parent);
  284. d->fetch(parent, qMax(node->RowCount, 0) + 256);
  285. }
  286. //------------------------------------------------------------------------------
  287. Qt::ItemFlags ctkDICOMModel::flags ( const QModelIndex & index ) const
  288. {
  289. return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  290. }
  291. //------------------------------------------------------------------------------
  292. bool ctkDICOMModel::hasChildren ( const QModelIndex & parent ) const
  293. {
  294. QCTK_D(const ctkDICOMModel);
  295. Node* node = d->nodeFromIndex(parent);
  296. return node->RowCount > 0 || (!node->End && node->Query.seek(0));
  297. }
  298. //------------------------------------------------------------------------------
  299. QVariant ctkDICOMModel::headerData(int section, Qt::Orientation orientation, int role)const
  300. {
  301. QCTK_D(const ctkDICOMModel);
  302. if (role & ~(Qt::DisplayRole | Qt::EditRole))
  303. {
  304. return QVariant();
  305. }
  306. if (orientation == Qt::Vertical)
  307. {
  308. return section;
  309. }
  310. Q_ASSERT(orientation == Qt::Horizontal);
  311. Q_ASSERT(section < d->Headers.size());
  312. return d->Headers[section];
  313. }
  314. //------------------------------------------------------------------------------
  315. QModelIndex ctkDICOMModel::index ( int row, int column, const QModelIndex & parent ) const
  316. {
  317. QCTK_D(const ctkDICOMModel);
  318. Node* parentNode = d->nodeFromIndex(parent);
  319. Node* node = 0;
  320. foreach(Node* tmpNode, parentNode->Children)
  321. {
  322. if (tmpNode->Row == row &&
  323. tmpNode->Column == column)
  324. {
  325. node = tmpNode;
  326. break;
  327. }
  328. }
  329. if (node == 0)
  330. {
  331. node = d->createNode(row, column, parent);
  332. }
  333. return this->createIndex(row, column, node);
  334. }
  335. //------------------------------------------------------------------------------
  336. QModelIndex ctkDICOMModel::parent ( const QModelIndex & index ) const
  337. {
  338. QCTK_D(const ctkDICOMModel);
  339. Node* node = d->nodeFromIndex(index);
  340. if (node == 0 || node->Parent == 0)
  341. {
  342. return QModelIndex();
  343. }
  344. return this->createIndex(node->Parent->Row, node->Parent->Column, node->Parent);
  345. }
  346. //------------------------------------------------------------------------------
  347. int ctkDICOMModel::rowCount ( const QModelIndex & parent ) const
  348. {
  349. QCTK_D(const ctkDICOMModel);
  350. Node* node = d->nodeFromIndex(parent);
  351. if (node->RowCount == 0 && node->End)
  352. {
  353. const_cast<ctkDICOMModelPrivate*>(d)->fetch(parent, 256);
  354. }
  355. return node->RowCount;
  356. }
  357. //------------------------------------------------------------------------------
  358. void ctkDICOMModel::setDatabase(const QSqlDatabase &db)
  359. {
  360. QCTK_D(ctkDICOMModel);
  361. this->beginResetModel();
  362. d->DataBase = db;
  363. delete d->RootNode;
  364. d->RootNode = 0;
  365. if (d->DataBase.tables().empty())
  366. {
  367. Q_ASSERT(d->DataBase.isOpen());
  368. return;
  369. }
  370. d->RootNode = d->createNode(-1, -1, QModelIndex());
  371. this->endResetModel();
  372. bool hasQuerySize = d->RootNode->Query.driver()->hasFeature(QSqlDriver::QuerySize);
  373. if (hasQuerySize && d->RootNode->Query.size() > 0)
  374. {
  375. int newRowCount= d->RootNode->Query.size();
  376. beginInsertRows(QModelIndex(), 0, qMax(0, newRowCount - 1));
  377. d->RootNode->RowCount = newRowCount;
  378. d->RootNode->End = true;
  379. endInsertRows();
  380. }
  381. else
  382. {
  383. d->RootNode->RowCount = 0;
  384. }
  385. d->fetch(QModelIndex(), 256);
  386. }
  387. //------------------------------------------------------------------------------
  388. void ctkDICOMModel::sort(int column, Qt::SortOrder order)
  389. {
  390. QCTK_D(ctkDICOMModel);
  391. emit layoutAboutToBeChanged();
  392. d->Sort = QString("\"%1\" %2")
  393. .arg(d->Headers[column])
  394. .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
  395. d->updateQueries(d->RootNode);
  396. emit layoutChanged();
  397. }
  398. //------------------------------------------------------------------------------
  399. bool ctkDICOMModel::setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role)
  400. {
  401. QCTK_D(ctkDICOMModel);
  402. if (role & ~(Qt::DisplayRole | Qt::EditRole))
  403. {
  404. return false;
  405. }
  406. if (orientation == Qt::Vertical)
  407. {
  408. return false;
  409. }
  410. if (value.toString() == d->Headers[section])
  411. {
  412. return false;
  413. }
  414. d->Headers[section] = value.toString();
  415. emit this->headerDataChanged(orientation, section, section);
  416. return true;
  417. }