ctkDICOMModel.cpp

Go to the documentation of this file.
00001 /*=========================================================================
00002 
00003   Library:   CTK
00004  
00005   Copyright (c) 2010  Kitware Inc.
00006 
00007   Licensed under the Apache License, Version 2.0 (the "License");
00008   you may not use this file except in compliance with the License.
00009   You may obtain a copy of the License at
00010 
00011       http://www.commontk.org/LICENSE
00012 
00013   Unless required by applicable law or agreed to in writing, software
00014   distributed under the License is distributed on an "AS IS" BASIS,
00015   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00016   See the License for the specific language governing permissions and
00017   limitations under the License.
00018  
00019 =========================================================================*/
00020 
00021 // Qt includes
00022 #include <QStringList>
00023 #include <QSqlDriver>
00024 #include <QSqlError>
00025 #include <QSqlQuery>
00026 #include <QSqlQueryModel>
00027 #include <QSqlRecord>
00028 
00029 #include <QTime>
00030 #include <QDebug>
00031 
00032 // ctkDICOM includes
00033 #include "ctkDICOMModel.h"
00034 
00035 struct Node;
00036 
00037 //------------------------------------------------------------------------------
00038 class ctkDICOMModelPrivate:public ctkPrivate<ctkDICOMModel>
00039 {
00040 public:
00041   ctkDICOMModelPrivate();
00042   virtual ~ctkDICOMModelPrivate();
00043   void init();
00044 
00045   enum IndexType{
00046     RootType,
00047     PatientType,
00048     StudyType,
00049     SeriesType,
00050     ImageType
00051   };
00052  
00053   void fetch(const QModelIndex& indexValue, int limit);
00054   Node* createNode(int row, const QModelIndex& parentValue)const;
00055   Node* nodeFromIndex(const QModelIndex& indexValue)const;
00056   //QModelIndexList indexListFromNode(const Node* node)const;
00057   //QModelIndexList modelIndexList(Node* node = 0)const;
00058   //int childrenCount(Node* node = 0)const;
00059   // move it in the Node struct
00060   QVariant value(Node* parentValue, int row, int field)const;
00061   QVariant value(const QModelIndex& indexValue, int row, int field)const;
00062   QString  generateQuery(const QString& fields, const QString& table, const QString& conditions = QString())const;
00063   void updateQueries(Node* node)const;
00064 
00065   Node*        RootNode;
00066   QSqlDatabase DataBase;
00067   QStringList  Headers;
00068   QString      Sort;
00069 };
00070 
00071 //------------------------------------------------------------------------------
00072 // 1 node per row
00073 struct Node
00074 {
00075   ~Node()
00076     {
00077     foreach(Node* node, this->Children)
00078       {
00079       delete node;
00080       }
00081     this->Children.clear();
00082     }
00083   ctkDICOMModelPrivate::IndexType Type;
00084   Node*     Parent;
00085   QVector<Node*> Children;
00086   int       Row;
00087   QSqlQuery Query;
00088   QString   UID;
00089   int       RowCount;
00090   bool      AtEnd;
00091   bool      Fetching;
00092 };
00093 
00094 //------------------------------------------------------------------------------
00095 ctkDICOMModelPrivate::ctkDICOMModelPrivate()
00096 {
00097   this->RootNode     = 0;
00098 }
00099 
00100 //------------------------------------------------------------------------------
00101 ctkDICOMModelPrivate::~ctkDICOMModelPrivate()
00102 {
00103   delete this->RootNode;
00104   this->RootNode = 0;
00105 }
00106 
00107 //------------------------------------------------------------------------------
00108 void ctkDICOMModelPrivate::init()
00109 {
00110   this->Headers = QStringList() << "Name" << "Age" << "Scan" << "Date" << "Subject ID"
00111                   << "Number" << "Institution" << "Referrer" << "Performer";
00112 }
00113 
00114 //------------------------------------------------------------------------------
00115 Node* ctkDICOMModelPrivate::nodeFromIndex(const QModelIndex& indexValue)const
00116 {
00117   return indexValue.isValid() ? reinterpret_cast<Node*>(indexValue.internalPointer()) : this->RootNode;
00118 }
00119 
00120 /*
00121 //------------------------------------------------------------------------------
00122 QModelIndexList ctkDICOMModelPrivate::indexListFromNode(const Node* node)const
00123 {
00124   CTK_P(const ctkDICOMModel);
00125   Q_ASSERT(node);
00126   QModelIndexList indexList;
00127   
00128   Node* parentNode = node->Parent;
00129   if (parentNode == 0)
00130     {
00131     return indexList;
00132     }
00133   int field = parentNode->Query.record().indexOf("UID");
00134   int row = -1;
00135   for (row = 0; row < parentNode->RowCount; ++row)
00136     {
00137     QString uid = this->value(parentNode, row, field).toString();
00138     if (uid == node->UID)
00139       {
00140       break;
00141       }
00142     }
00143   if (row >= parentNode->RowCount)
00144     {
00145     return indexList;
00146     }
00147   for (int column = 0 ; column < this->Headers.size(); ++column)
00148     {
00149     indexList.append(p->createIndex(row, column, parentNode));
00150     }
00151   return indexList;
00152 }
00153 
00154 //------------------------------------------------------------------------------
00155 QModelIndexList ctkDICOMModelPrivate::modelIndexList(Node* node)const
00156 {
00157   QModelIndexList list;
00158   if (node == 0)
00159     {
00160     node = this->RootNode;
00161     }
00162   foreach(Node* child, node->Children)
00163     {
00164     list.append(this->indexListFromNode(child));
00165     }
00166   foreach(Node* child, node->Children)
00167     {
00168     list.append(this->modelIndexList(child));
00169     }
00170   return list;
00171 }
00172 
00173 //------------------------------------------------------------------------------
00174 int ctkDICOMModelPrivate::childrenCount(Node* node)const
00175 {
00176   int count = 0;
00177   if (node == 0)
00178     {
00179     node = this->RootNode;
00180     }
00181   count += node->Children.size();
00182   foreach(Node* child, node->Children)
00183     {
00184     count += this->childrenCount(child);
00185     }
00186   return count;
00187 }
00188 */
00189 //------------------------------------------------------------------------------
00190 Node* ctkDICOMModelPrivate::createNode(int row, const QModelIndex& parentValue)const
00191 {
00192   Node* node = new Node;
00193   Node* nodeParent = 0;
00194   if (row == -1)
00195     {// root node
00196     node->Type = ctkDICOMModelPrivate::RootType;
00197     node->Parent = 0;
00198     }
00199   else
00200     {
00201     nodeParent = this->nodeFromIndex(parentValue); 
00202     nodeParent->Children.push_back(node);
00203     node->Parent = nodeParent;
00204     node->Type = ctkDICOMModelPrivate::IndexType(nodeParent->Type + 1);
00205     }
00206   node->Row = row;
00207   if (node->Type != ctkDICOMModelPrivate::RootType)
00208     {
00209     int field = nodeParent->Query.record().indexOf("UID");
00210     node->UID = this->value(parentValue, row, field).toString();
00211     }
00212   
00213   node->RowCount = 0;
00214   node->AtEnd = false;
00215   node->Fetching = false;
00216 
00217   this->updateQueries(node);
00218   
00219   return node;
00220 }
00221 
00222 //------------------------------------------------------------------------------
00223 QVariant ctkDICOMModelPrivate::value(const QModelIndex& parentValue, int row, int column) const
00224 {
00225   Node* node = this->nodeFromIndex(parentValue);
00226   if (row >= node->RowCount)
00227     {      
00228     const_cast<ctkDICOMModelPrivate *>(this)->fetch(parentValue, row + 256);
00229     }
00230   return this->value(node, row, column);
00231 }
00232 
00233 //------------------------------------------------------------------------------
00234 QVariant ctkDICOMModelPrivate::value(Node* parentValue, int row, int column) const
00235 {
00236   Q_ASSERT(row < parentValue->RowCount);
00237 
00238   if (!parentValue->Query.seek(row)) 
00239     {
00240     qDebug() << parentValue->Query.lastError();
00241     Q_ASSERT(parentValue->Query.seek(row));
00242     return QVariant();
00243     }
00244   QVariant res = parentValue->Query.value(column);
00245   Q_ASSERT(res.isValid());
00246   return res;
00247 }
00248 
00249 //------------------------------------------------------------------------------
00250 QString ctkDICOMModelPrivate::generateQuery(const QString& fields, const QString& table, const QString& conditions)const
00251 {
00252   QString res = QString("SELECT ") + fields + QString(" FROM ") + table;
00253   if (!conditions.isEmpty())
00254     {
00255     res += QString(" WHERE ") + conditions;
00256     }
00257   if (!this->Sort.isEmpty())
00258     {
00259     res += QString(" ORDER BY ") + this->Sort;
00260     }
00261   return res;
00262 }
00263 
00264 //------------------------------------------------------------------------------
00265 void ctkDICOMModelPrivate::updateQueries(Node* node)const
00266 {
00267   // are you kidding me, it should be virtualized here :-)
00268   QString query;
00269   switch(node->Type)
00270     {
00271     default:
00272       Q_ASSERT(node->Type == ctkDICOMModelPrivate::RootType);
00273       break;
00274     case ctkDICOMModelPrivate::RootType:
00275       //query = QString("SELECT  FROM ");
00276       query = this->generateQuery("UID as UID, PatientsName as Name, PatientsAge as Age, PatientsBirthDate as Date, PatientID as \"Subject ID\"","Patients");
00277       break;
00278     case ctkDICOMModelPrivate::PatientType:
00279       //query = QString("SELECT  FROM Studies WHERE PatientsUID='%1'").arg(node->UID);
00280       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));
00281       break;
00282     case ctkDICOMModelPrivate::StudyType:
00283       //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);
00284       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));
00285       break;
00286     case ctkDICOMModelPrivate::SeriesType:
00287       //query = QString("SELECT Filename as UID, Filename as Name, SeriesInstanceUID as Date FROM Images WHERE SeriesInstanceUID='%1'").arg(node->UID);
00288       query = this->generateQuery("Filename as UID, Filename as Name, SeriesInstanceUID as Date", "Images", QString("SeriesInstanceUID='%1'").arg(node->UID));
00289       break;
00290     case ctkDICOMModelPrivate::ImageType:
00291       break;
00292     }
00293   node->Query = QSqlQuery(query, this->DataBase);
00294   foreach(Node* child, node->Children)
00295     {
00296     this->updateQueries(child);
00297     }
00298 }
00299 
00300 //------------------------------------------------------------------------------
00301 void ctkDICOMModelPrivate::fetch(const QModelIndex& indexValue, int limit)
00302 {
00303   CTK_P(ctkDICOMModel);
00304   Node* node = this->nodeFromIndex(indexValue);
00305   if (node->AtEnd || limit <= node->RowCount || node->Fetching/*|| bottom.column() == -1*/)
00306     {
00307     return;
00308     }
00309   node->Fetching = true;
00310 
00311   int newRowCount;
00312   const int oldRowCount = node->RowCount;
00313 
00314   // try to seek directly
00315   if (node->Query.seek(limit - 1)) 
00316     {
00317     newRowCount = limit;
00318     } 
00319   else 
00320     {
00321     newRowCount = qMax(oldRowCount, 1);
00322     if (node->Query.seek(newRowCount - 1)) 
00323       {
00324       while (node->Query.next())
00325         {
00326         ++newRowCount;
00327         }
00328       } 
00329     else 
00330       {
00331       // empty or invalid query
00332       newRowCount = 0;
00333       }
00334     node->AtEnd = true; // this is the end.
00335     }
00336   if (newRowCount > 0 && newRowCount > node->RowCount) 
00337     {
00338     p->beginInsertRows(indexValue, node->RowCount, newRowCount - 1);
00339     node->RowCount = newRowCount;
00340     node->Fetching = false;
00341     p->endInsertRows();
00342     } 
00343   else 
00344     {
00345     node->RowCount = newRowCount;
00346     node->Fetching = false;
00347     }
00348 }
00349 
00350 //------------------------------------------------------------------------------
00351 ctkDICOMModel::ctkDICOMModel(QObject* parentValue)
00352 {
00353   CTK_INIT_PRIVATE(ctkDICOMModel);
00354   ctk_d()->init();
00355 }
00356 
00357 //------------------------------------------------------------------------------
00358 ctkDICOMModel::~ctkDICOMModel()
00359 {
00360 }
00361 
00362 //------------------------------------------------------------------------------
00363 bool ctkDICOMModel::canFetchMore ( const QModelIndex & parentValue ) const
00364 {
00365   CTK_D(const ctkDICOMModel);
00366   Node* node = d->nodeFromIndex(parentValue);
00367   return !node->AtEnd;
00368 }
00369 
00370 //------------------------------------------------------------------------------
00371 int ctkDICOMModel::columnCount ( const QModelIndex & _parent ) const
00372 {
00373   CTK_D(const ctkDICOMModel);
00374   Q_UNUSED(_parent);
00375   return d->RootNode != 0 ? d->Headers.size() : 0;
00376 }
00377 
00378 //------------------------------------------------------------------------------
00379 QVariant ctkDICOMModel::data ( const QModelIndex & indexValue, int role ) const
00380 {
00381   CTK_D(const ctkDICOMModel);
00382   if (role & ~(Qt::DisplayRole | Qt::EditRole))
00383     {
00384     return QVariant();
00385     }
00386   QModelIndex indexParent = this->parent(indexValue);
00387   Node* parentNode = d->nodeFromIndex(indexParent);
00388   if (indexValue.row() >= parentNode->RowCount)
00389     {      
00390     const_cast<ctkDICOMModelPrivate *>(d)->fetch(indexValue, indexValue.row());
00391     }
00392 /*
00393   if (!node->Query.seek(indexValue.row())) 
00394     {
00395     qDebug() << node->Query.lastError();
00396     return QVariant();
00397     }
00398     */
00399   int field = parentNode->Query.record().indexOf(d->Headers[indexValue.column()]);
00400   if (field < 0)
00401     {
00402     return QString();
00403     }
00404   return d->value(indexParent, indexValue.row(), field);
00405   //return node->Query.value(field);
00406 }
00407 
00408 //------------------------------------------------------------------------------
00409 void ctkDICOMModel::fetchMore ( const QModelIndex & parentValue )
00410 {
00411   CTK_D(ctkDICOMModel);
00412   Node* node = d->nodeFromIndex(parentValue);
00413   d->fetch(parentValue, qMax(node->RowCount, 0) + 256);
00414 }
00415 
00416 //------------------------------------------------------------------------------
00417 Qt::ItemFlags ctkDICOMModel::flags ( const QModelIndex & indexValue ) const
00418 {
00419   return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
00420 }
00421 
00422 //------------------------------------------------------------------------------
00423 bool ctkDICOMModel::hasChildren ( const QModelIndex & parentValue ) const
00424 {
00425   CTK_D(const ctkDICOMModel);
00426   if (parentValue.column() > 0)
00427     {
00428     return false;
00429     }
00430   Node* node = d->nodeFromIndex(parentValue);
00431   if (!node)
00432     {
00433     return false;
00434     }
00435   if (node->RowCount == 0 && !node->AtEnd)
00436     {
00437     //const_cast<qCTKDCMTKModelPrivate*>(d)->fetch(parentValue, 1);
00438     return node->Query.seek(0);
00439     }
00440   return node->RowCount > 0;
00441 }
00442 
00443 //------------------------------------------------------------------------------
00444 QVariant ctkDICOMModel::headerData(int section, Qt::Orientation orientation, int role)const
00445 {
00446   CTK_D(const ctkDICOMModel);
00447   if (role & ~(Qt::DisplayRole | Qt::EditRole))
00448     {
00449     return QVariant();
00450     }
00451   if (orientation == Qt::Vertical)
00452     {
00453     return section;
00454     }
00455   Q_ASSERT(orientation == Qt::Horizontal);
00456   Q_ASSERT(section < d->Headers.size());
00457   return d->Headers[section];
00458 }
00459 
00460 //------------------------------------------------------------------------------
00461 QModelIndex ctkDICOMModel::index ( int row, int column, const QModelIndex & parentValue ) const
00462 {
00463   CTK_D(const ctkDICOMModel);
00464   if (d->RootNode == 0 || parentValue.column() > 0)
00465     {
00466     return QModelIndex();
00467     }
00468   Node* parentNode = d->nodeFromIndex(parentValue);
00469   int field = parentNode->Query.record().indexOf("UID");
00470   QString uid = d->value(parentValue, row, field).toString();
00471   Node* node = 0;
00472   foreach(Node* tmpNode, parentNode->Children)
00473     {
00474     if (tmpNode->UID == uid)
00475       {
00476       node = tmpNode;
00477       break;
00478       }
00479     }
00480   if (node == 0)
00481     {
00482     node = d->createNode(row, parentValue);
00483     }
00484   return this->createIndex(row, column, node);
00485 }
00486 
00487 //------------------------------------------------------------------------------
00488 QModelIndex ctkDICOMModel::parent ( const QModelIndex & indexValue ) const
00489 {
00490   CTK_D(const ctkDICOMModel);
00491   Node* node = d->nodeFromIndex(indexValue);
00492   Q_ASSERT(node);
00493   Node* parentNode = node->Parent;
00494   if (parentNode == 0)
00495     {// node is root
00496     return QModelIndex();
00497     }
00498   return parentNode == d->RootNode ? QModelIndex() : this->createIndex(parentNode->Row, 0, parentNode);
00499   /* need to recalculate the parent row
00500   Node* greatParentNode = parentNode->Parent;
00501   if (greatParentNode == 0)
00502     {
00503     return QModelIndex();
00504     }
00505   int field = greatParentNode->Query.record().indexOf("UID");
00506   int row = -1;
00507   for (row = 0; row < greatParentNode->RowCount; ++row)
00508     {
00509     QString uid = d->value(greatParentNode, row, field).toString();
00510     if (uid == parentNode->UID)
00511       {
00512       break;
00513       }
00514     }
00515   Q_ASSERT(row < greatParentNode->RowCount);
00516   return this->createIndex(row, 0, parentNode);
00517   */
00518 }
00519 
00520 //------------------------------------------------------------------------------
00521 int ctkDICOMModel::rowCount ( const QModelIndex & parentValue ) const
00522 {
00523   CTK_D(const ctkDICOMModel);
00524   if (d->RootNode == 0 || parentValue.column() > 0)
00525     {
00526     return 0;
00527     }
00528   Node* node = d->nodeFromIndex(parentValue);
00529   Q_ASSERT(node);
00530   if (node->RowCount == 0 && !node->AtEnd)
00531     {
00532     //const_cast<ctkDICOMModelPrivate*>(d)->fetch(parentValue, 256);
00533     }
00534   return node->RowCount;
00535 }
00536 
00537 //------------------------------------------------------------------------------
00538 void ctkDICOMModel::setDatabase(const QSqlDatabase &db)
00539 {
00540   CTK_D(ctkDICOMModel);
00541 
00542   this->beginResetModel();
00543   d->DataBase = db;
00544   
00545   delete d->RootNode;
00546   d->RootNode = 0;
00547 
00548   if (d->DataBase.tables().empty())
00549     {
00550     //Q_ASSERT(d->DataBase.isOpen());
00551     this->endResetModel();
00552     return;
00553     }
00554     
00555   d->RootNode = d->createNode(-1, QModelIndex());
00556   
00557   this->endResetModel();
00558 
00559   // TODO, use hasQuerySize everywhere, not only in setDataBase()
00560   bool hasQuerySize = d->RootNode->Query.driver()->hasFeature(QSqlDriver::QuerySize);
00561   if (hasQuerySize && d->RootNode->Query.size() > 0) 
00562     {
00563     int newRowCount= d->RootNode->Query.size();
00564     beginInsertRows(QModelIndex(), 0, qMax(0, newRowCount - 1));
00565     d->RootNode->RowCount = newRowCount;
00566     d->RootNode->AtEnd = true;
00567     endInsertRows();
00568     }
00569   d->fetch(QModelIndex(), 256);
00570 }
00571 
00572 //------------------------------------------------------------------------------
00573 void ctkDICOMModel::sort(int column, Qt::SortOrder order)
00574 {
00575   CTK_D(ctkDICOMModel);
00576   /* The following would work if there is no fetch involved.
00577      ORDER BY doesn't just apply on the fetched item. By sorting
00578      new items can show up in the model, and we need to be more
00579      careful
00580   emit layoutAboutToBeChanged();
00581   QModelIndexList oldIndexList = d->modelIndexList();
00582   d->Sort = QString("\"%1\" %2")
00583     .arg(d->Headers[column])
00584     .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
00585   d->updateQueries(d->RootNode);
00586   QModelIndexList newIndexList = d->modelIndexList();
00587   Q_ASSERT(oldIndexList.count() == newIndexList.count());
00588   this->changePersistentIndexList(oldIndexList, newIndexList);
00589   emit layoutChanged();
00590   */
00591   this->beginResetModel();
00592   delete d->RootNode;
00593   d->RootNode = 0;
00594   d->Sort = QString("\"%1\" %2")
00595     .arg(d->Headers[column])
00596     .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
00597   d->RootNode = d->createNode(-1, QModelIndex());
00598   
00599   this->endResetModel();
00600 }
00601 
00602 //------------------------------------------------------------------------------
00603 bool ctkDICOMModel::setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role)
00604 {
00605   CTK_D(ctkDICOMModel);
00606   if (role & ~(Qt::DisplayRole | Qt::EditRole))
00607     {
00608     return false;
00609     }
00610   if (orientation == Qt::Vertical)
00611     {
00612     return false;
00613     }
00614   if (value.toString() == d->Headers[section])
00615     {
00616     return false;
00617     }
00618   d->Headers[section] = value.toString();
00619   emit this->headerDataChanged(orientation, section, section);
00620   return true;
00621 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines