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   // @bug: this expression is not "valid", DisplayRole and EditRole are not bitmasks
00448   if (role & ~(Qt::DisplayRole | Qt::EditRole))
00449     {
00450     return QVariant();
00451     }
00452   if (orientation == Qt::Vertical)
00453     {
00454     return section;
00455     }
00456   Q_ASSERT(orientation == Qt::Horizontal);
00457   Q_ASSERT(section < d->Headers.size());
00458   return d->Headers[section];
00459 }
00460 
00461 //------------------------------------------------------------------------------
00462 QModelIndex ctkDICOMModel::index ( int row, int column, const QModelIndex & parentValue ) const
00463 {
00464   CTK_D(const ctkDICOMModel);
00465   if (d->RootNode == 0 || parentValue.column() > 0)
00466     {
00467     return QModelIndex();
00468     }
00469   Node* parentNode = d->nodeFromIndex(parentValue);
00470   int field = parentNode->Query.record().indexOf("UID");
00471   QString uid = d->value(parentValue, row, field).toString();
00472   Node* node = 0;
00473   foreach(Node* tmpNode, parentNode->Children)
00474     {
00475     if (tmpNode->UID == uid)
00476       {
00477       node = tmpNode;
00478       break;
00479       }
00480     }
00481   if (node == 0)
00482     {
00483     node = d->createNode(row, parentValue);
00484     }
00485   return this->createIndex(row, column, node);
00486 }
00487 
00488 //------------------------------------------------------------------------------
00489 QModelIndex ctkDICOMModel::parent ( const QModelIndex & indexValue ) const
00490 {
00491   CTK_D(const ctkDICOMModel);
00492   Node* node = d->nodeFromIndex(indexValue);
00493   Q_ASSERT(node);
00494   Node* parentNode = node->Parent;
00495   if (parentNode == 0)
00496     {// node is root
00497     return QModelIndex();
00498     }
00499   return parentNode == d->RootNode ? QModelIndex() : this->createIndex(parentNode->Row, 0, parentNode);
00500   /* need to recalculate the parent row
00501   Node* greatParentNode = parentNode->Parent;
00502   if (greatParentNode == 0)
00503     {
00504     return QModelIndex();
00505     }
00506   int field = greatParentNode->Query.record().indexOf("UID");
00507   int row = -1;
00508   for (row = 0; row < greatParentNode->RowCount; ++row)
00509     {
00510     QString uid = d->value(greatParentNode, row, field).toString();
00511     if (uid == parentNode->UID)
00512       {
00513       break;
00514       }
00515     }
00516   Q_ASSERT(row < greatParentNode->RowCount);
00517   return this->createIndex(row, 0, parentNode);
00518   */
00519 }
00520 
00521 //------------------------------------------------------------------------------
00522 int ctkDICOMModel::rowCount ( const QModelIndex & parentValue ) const
00523 {
00524   CTK_D(const ctkDICOMModel);
00525   if (d->RootNode == 0 || parentValue.column() > 0)
00526     {
00527     return 0;
00528     }
00529   Node* node = d->nodeFromIndex(parentValue);
00530   Q_ASSERT(node);
00531   if (node->RowCount == 0 && !node->AtEnd)
00532     {
00533     //const_cast<ctkDICOMModelPrivate*>(d)->fetch(parentValue, 256);
00534     }
00535   return node->RowCount;
00536 }
00537 
00538 //------------------------------------------------------------------------------
00539 void ctkDICOMModel::setDatabase(const QSqlDatabase &db)
00540 {
00541   CTK_D(ctkDICOMModel);
00542 
00543   this->beginResetModel();
00544   d->DataBase = db;
00545   
00546   delete d->RootNode;
00547   d->RootNode = 0;
00548 
00549   if (d->DataBase.tables().empty())
00550     {
00551     //Q_ASSERT(d->DataBase.isOpen());
00552     this->endResetModel();
00553     return;
00554     }
00555     
00556   d->RootNode = d->createNode(-1, QModelIndex());
00557   
00558   this->endResetModel();
00559 
00560   // TODO, use hasQuerySize everywhere, not only in setDataBase()
00561   bool hasQuerySize = d->RootNode->Query.driver()->hasFeature(QSqlDriver::QuerySize);
00562   if (hasQuerySize && d->RootNode->Query.size() > 0) 
00563     {
00564     int newRowCount= d->RootNode->Query.size();
00565     beginInsertRows(QModelIndex(), 0, qMax(0, newRowCount - 1));
00566     d->RootNode->RowCount = newRowCount;
00567     d->RootNode->AtEnd = true;
00568     endInsertRows();
00569     }
00570   d->fetch(QModelIndex(), 256);
00571 }
00572 
00573 //------------------------------------------------------------------------------
00574 void ctkDICOMModel::sort(int column, Qt::SortOrder order)
00575 {
00576   CTK_D(ctkDICOMModel);
00577   /* The following would work if there is no fetch involved.
00578      ORDER BY doesn't just apply on the fetched item. By sorting
00579      new items can show up in the model, and we need to be more
00580      careful
00581   emit layoutAboutToBeChanged();
00582   QModelIndexList oldIndexList = d->modelIndexList();
00583   d->Sort = QString("\"%1\" %2")
00584     .arg(d->Headers[column])
00585     .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
00586   d->updateQueries(d->RootNode);
00587   QModelIndexList newIndexList = d->modelIndexList();
00588   Q_ASSERT(oldIndexList.count() == newIndexList.count());
00589   this->changePersistentIndexList(oldIndexList, newIndexList);
00590   emit layoutChanged();
00591   */
00592   this->beginResetModel();
00593   delete d->RootNode;
00594   d->RootNode = 0;
00595   d->Sort = QString("\"%1\" %2")
00596     .arg(d->Headers[column])
00597     .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
00598   d->RootNode = d->createNode(-1, QModelIndex());
00599   
00600   this->endResetModel();
00601 }
00602 
00603 //------------------------------------------------------------------------------
00604 bool ctkDICOMModel::setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role)
00605 {
00606   CTK_D(ctkDICOMModel);
00607   if (role & ~(Qt::DisplayRole | Qt::EditRole))
00608     {
00609     return false;
00610     }
00611   if (orientation == Qt::Vertical)
00612     {
00613     return false;
00614     }
00615   if (value.toString() == d->Headers[section])
00616     {
00617     return false;
00618     }
00619   d->Headers[section] = value.toString();
00620   emit this->headerDataChanged(orientation, section, section);
00621   return true;
00622 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated on 21 May 2010 for CTK by  doxygen 1.6.1