Feeds:
Posts
Comments

Archive for the ‘Knowledge Base’ Category

Background
You know that, we can’t create widgets in a thread but main thread. So what if we want to load a page in a thread? There are various solution to these problem. Here is one…

WebpageLoader: The page loader class.

class WebpageLoader : public QObject
{
  Q_OBJECT
public:
WebpageLoader(QObject *parent) :
    QObject(parent),mNetManager(new QNetworkAccessManager(this)),mCurrentUrlInd(0),mLastReply(0)
{
  connect(mNetManager,SIGNAL(finished(QNetworkReply*)),SLOT(loadFinished(QNetworkReply*)));
  // read urls from file
  readUrls();
  // timer will start after two minutes, so load the first link
  loadNextUrl();
  mTimerId = startTimer(PAGE_SWITCH_INTERVER);
}

~WebpageLoader()
{
  killTimer(mTimerId);

  if(mLastReply){
    // TODO or KNOWN ISSUE:
    // as the last reply is not aborted, hence QWaitCondition throws a message like
    // Destroyed while the thread is still waiting.
    //mLastReply->abort();
  }
}

public slots:
void loadFinished(QNetworkReply* reply)
{
  QString pageContent("");
  QUrl baseUrl;

  if(reply){           
    if(reply->error()){
      // if there is an error in reply, get it & show it      
      pageContent = QString("NetworkError (") +
                    QString::number(int(reply->error())) + QString("): ") + reply->errorString();
    }
    else{      
      // read the content
      pageContent = QString(reply->readAll());
      baseUrl = reply->request().url();
    }

    reply->deleteLater();
  }
  else{
    // Perhaps the problem is in internet connection
    pageContent = QString("<html><body><h3>There is problem in your internet connection. Check your connection by clicking <a href='www.google.com'>here</a>.</h3></body></html>");
  }

  // emit signal to indicates that current link's content has gotten.
  emit pageLoadDone(pageContent,baseUrl);
}

protected:
void timerEvent(QTimerEvent *e)
{
  loadNextUrl();
}

private:
void readUrls()
{
  mUrls.clear();
  QFile file(QCoreApplication::applicationDirPath() + "/links.txt");

  if(file.open(QFile::ReadOnly)){
    QTextStream stream(&file);
    QString str;

    while((str = QString(stream.readLine())).length() > 0){
      if(!str.startsWith(QString("http"))){
        str = QString("http://") + str;
      }

      mUrls.append(str);
    }

    file.close();
  }
}

void loadNextUrl()
{
  if(mUrls.count() > 0){
    // Construct request & post it to get content
    QNetworkRequest netRequest(QUrl(mUrls.at(mCurrentUrlInd++)));
    mLastReply =  mNetManager->get(netRequest);
    // update the links index to fetch next link
    mCurrentUrlInd = mCurrentUrlInd % mUrls.count();
  }
}

signals:
  void pageLoadDone(const QString& pageContent, const QUrl& baseUrl);

private:
  QNetworkAccessManager* mNetManager;
  QList<QString> mUrls;
  int mCurrentUrlInd;
  int mTimerId;
  QNetworkReply* mLastReply;
};

WebpageThread: The page load thread class

class WebpageThread : public QThread
{
Q_OBJECT
public:
WebpageThread::WebpageThread(QObject *parent) :
    QThread(parent),mWebpageLoader(0)
{
}

WebpageThread::~WebpageThread()
{
  // stop the event loop at last time, if it is not finished already
  while(isRunning()){
    quit();
    QThread::yieldCurrentThread();
  }
}

WebpageLoader* webpageLoader()const
{
  return mWebpageLoader;
}

protected:
// Just consider that run method is main method.
// So, to construct all the objects on this thread,
void WebpageThread::run()
{
  WebpageLoader webpageLoader;
  //now initialize the pointer of page loader so that out-side world can access it
  // for connecting with various signal to be notified on their threads.
  mWebpageLoader = & webpageLoader;
  // starts the event loop
  exec();
  // if event loop exits, just uninitialize it.
  mWebpageLoader = 0;
}

private:
  WebpageLoader* mWebpageLoader;
};

WebpageViewer: The viewer class

class WebpageViewer : public QWebView
{
Q_OBJECT
public:
WebpageViewer(QWidget *parent) :
    QWebView(parent)
{
  /* set the link delegation policy to DelegateAllLinks as we dont want
     to make our webview to show contents after clicking a link
  */
  page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);

  // Now create the thread on which the WebpageLoader lives
  WebpageThread* webpageThread= new WebpageThread(this);
  webpageThread->start();
  // wait for the thread to start running
  while(0 == webpageThread->webpageLoader());

  // Now, binds the necessary signals & corresponding slots.
  // Binds the pageLoadDone signal of WebpageLoader to handlePageLoadDone
connect(webpageThread->webpageLoader(),SIGNAL(pageLoadDone(QString,QUrl)),SLOT(handlePageLoadDone(QString,QUrl)));
  connect(qApp,SIGNAL(aboutToQuit()),webpageThread,SLOT(quit()));
}

private slots:
void handlePageLoadDone(const QString& pageContent,const QUrl& baseUrl)
{
  this->setHtml(pageContent,baseUrl);
}
};

Read Full Post »

Background
You know that, QDirModel is obsolete and Qt recommend to use QFileSystemModel. However, QFileSystemModel is not fast enough as windows explorer. Here I will create an explorer as fast as windows explorer.

Model Related Classes
FileSystemItem: This class represent an item (dir/file/folder) in the model. Here source listing of this class.

#include "filesystemmodel.h"

#include <QDir>
#include <QFileIconProvider>
#include <QDateTime>

#define SEPARATOR QString("/")

// This class represents a row in FileSystemModel
class FileSystemItem
{
public:
  FileSystemItem(const QFileInfo& fileInfo,FileSystemItem* parent = 0)
  {
    mParent = parent;
    mFileInfo = fileInfo;

    if(parent){
      parent->addChild(this);

      // NOTE: absoluteFilePath method of QFileInfo class cause the file system
      // query hence causes slower performance, We are going to keep the
      // absolutepath of an item. Absolute path means the absolute path of parent
      // plus a separator plus filename of this item.
      if(0 == parent->parent()){
        // for drives, there is no filename, so we used canonicalPath
        mAbsFilePath = fileInfo.canonicalPath();
      }
      else{
        mAbsFilePath = parent->absoluteFilePath() + SEPARATOR + fileInfo.fileName();
      }
    }
    else{
      // Path of root item, which is not visible;
      mAbsFilePath = "";
    }
  }

  ~FileSystemItem()
  {
    qDeleteAll(mChildren);
  }


  FileSystemItem* childAt(int position)
  {
    return mChildren.value(position,0);
  }

  int childCount() const
  {
    return mChildren.count();
  }

  // returns the position of this son among his siblings
  int childNumber() const
  {
    if (mParent){
          return mParent->mChildren.indexOf(const_cast<FileSystemItem*>(this));
        }

    return 0;
  }

  FileSystemItem* parent()
  {
    return mParent;
  }

  QString absoluteFilePath()const
  {
    return mAbsFilePath;
  }

  QString fileName()const
  {
    if(mParent){
      // for drives, there is no filename, so we used canonicalPath
      if(0 == mParent->parent()){
        return mFileInfo.canonicalPath();
      }
      else{
        return mFileInfo.fileName();
      }
    }

    return "";
  }

  QFileInfo fileInfo()const
  {
    return mFileInfo;
  }

  void addChild(FileSystemItem *child)
  {
    if(!mChildren.contains(child)){
      mChildren.append(child);
    }
  }

  // This is a recursive method which tries to match a path to a specifiq
  // FileSystemItem item which has the path;
  // Here startIndex is the position of the separator
  FileSystemItem* matchPath(const QStringList& path, int startIndex = 0)
  {
    foreach(FileSystemItem* child, mChildren){
      QString match = path.at(startIndex);

      if(child->fileName() == match){
        if(startIndex + 1 == path.count()){
          return child;
        }
        else{
          return child->matchPath(path,startIndex + 1);
        }
      }
    }

    return 0;
  }

private:
  FileSystemItem* mParent;
  QList<FileSystemItem*> mChildren;
  QFileInfo mFileInfo;
  QString mAbsFilePath;
};

// End of FileSystemItem class

FileSystemModel: This class is the Model itself.

class FileSystemModel : public QAbstractItemModel
{
Q_OBJECT

public:
  enum Column{NAME, SIZE, TYPE, DATE, LASTCOLUMN};
FileSystemModel(QObject* parent)
  : QAbstractItemModel(parent),mIconFactory(new QFileIconProvider())
{
  // Now it is time to fix the headers
  mHeaders << "Name"
           << "Size"
           << "Type"
           << "Date Modified";

  // Create the root item [NOTE: Root item is not visible, but is the parent
  // of all drives]
  mRootItem = new FileSystemItem(QFileInfo(),0);
  mCurrentPath = "";

  QFileInfoList drives = QDir::drives();

  foreach(QFileInfo drive, drives){
    new FileSystemItem(drive,mRootItem);
  }
}

~FileSystemModel()
{
  delete mRootItem;
  delete mIconFactory;
}

QVariant headerData(int section, Qt::Orientation orientation,
                               int role) const
{
  if(orientation == Qt::Horizontal){
    switch(role){
    // in case of DisplayRole, just return the header text
    case Qt::DisplayRole:
      return mHeaders.at(section);
      break;
    // in case of TextAlignmentRole, only SIZE column will be right align,
    // others will be left align
    case Qt::TextAlignmentRole:
      return int(SIZE) == section ? Qt::AlignRight : Qt::AlignLeft;
      break;
    }
  }

  return QVariant();
}

Qt::ItemFlags flags(const QModelIndex &index) const
{
  if (!index.isValid())
      return 0;

  // Our model is read only.
  return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

int columnCount(const QModelIndex & /* parent */) const
{
  return LASTCOLUMN; // here it is; 4.
}

int rowCount(const QModelIndex &parent) const
{
  FileSystemItem *parentItem = getItem(parent);
  return parentItem->childCount();
}

QVariant data(const QModelIndex &index, int role) const
{
  // invalid work, invalid return
  if (!index.isValid()){
    return QVariant();
  }

  // in case of TextAlignmentRole, only SIZE column will be right align,
  // others will be left align
  if(int(SIZE) == index.column() && Qt::TextAlignmentRole == role){
    return Qt::AlignRight;
  }

  // At present, I don't want pay attention other than DisplayRole & DecorationRole
  if (role != Qt::DisplayRole && role != Qt::DecorationRole){
      return QVariant();
    }

  FileSystemItem *item = getItem(index);

  if(!item){
    return QVariant();
  }

  // if the role is for decoration & column is zero, we send the item's icon
  if(role == Qt::DecorationRole && index.column() == int(NAME) ){
    return mIconFactory->icon(item->fileInfo());
  }

  QVariant data;
  Column col = Column(index.column());

  switch(col){
  case NAME:
    data = item->fileName();
    break;
  case SIZE:
    if(item->fileInfo().isDir()){
      data = ""; // we don't want to show zero.
    }
    else{
      data = item->fileInfo().size();
    }
    break;
  case TYPE:
    data = mIconFactory->type(item->fileInfo());
    break;
  case DATE:
    data = item->fileInfo().lastModified().toString(Qt::LocalDate);
    break;
  default:
    data = "";
    break;
  }

  return data;
}

QModelIndex index(int row, int column, const QModelIndex &parent) const
{
  // As the NAME column is a tree, we will only create index which parent is NAME column
  if (parent.isValid() && parent.column() != int(NAME))
      return QModelIndex();

  FileSystemItem *parentItem = getItem(parent);

  // if there is a parent index, we want to work
  if(parentItem){
    FileSystemItem *childItem = parentItem->childAt(row);

    if (childItem){
        return createIndex(row, column, childItem);
      }
  }

  return QModelIndex();
}

QModelIndex index(const QString& path, int column) const
{
  if(path.length() > 0){
    FileSystemItem *item = mRootItem->matchPath(path.split(SEPARATOR));

    if(item){
      return createIndex(item->childNumber(),column,item);
    }
  }

  return QModelIndex();
}

QModelIndex parent(const QModelIndex &index) const
{
  // invalid work, invalid return
  if(!index.isValid()){
      return QModelIndex();
    }

  FileSystemItem* childItem = getItem(index);

  // If there is no child, there is no index
  if(!childItem){
      return QModelIndex();
    }

  FileSystemItem *parentItem = childItem->parent();

  // if there is no parent or parent is invisible, there is no index
  if (!parentItem || parentItem == mRootItem){
      return QModelIndex();
    }

  return createIndex(parentItem->childNumber(), NAME, parentItem);
}

bool isDir(const QModelIndex &index)
{
  FileSystemItem *item = static_cast<FileSystemItem*>(index.internalPointer());

  if(item && item != mRootItem){
    return item->fileInfo().isDir();
  }

  return false;
}

// NOTE: absoluteFilePath method of QFileInfo class cause the file system
// query hence causes slower performance, We are going to keep the
// absolutepath of an item. Absolute path means the absolute path of parent
// plus a separator plus filename of this item.
QString absolutePath(const QModelIndex &index)
{
  FileSystemItem *item = static_cast<FileSystemItem*>(index.internalPointer());

  if(item && item != mRootItem){
    return item->absoluteFilePath();
  }

  return "";
}

QString currentPath()const
{
  return mCurrentPath;
}

QModelIndex setCurrentPath(const QString& path)
{
  mCurrentPath = path;

  // find the file system item
  FileSystemItem *item = mRootItem->matchPath(path.split(SEPARATOR));

  // if there is a item and item's child is zero, we are going to find the
  // entries in the directory
  if(item && item != mRootItem && item->childCount() == 0){
    populateItem(item);
  }

  return index(path);
}

private:
void populateItem(FileSystemItem *item)
{
  QDir dir(item->absoluteFilePath());
  QFileInfoList all = dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);

  // loop through all the item and construct the childs
  foreach(QFileInfo one, all){
    new FileSystemItem(one,item);
  }
}

FileSystemItem *getItem(const QModelIndex &index) const
{
  // just return the internal pointer we set at creating index if the index is valid
  if(index.isValid()) {
      FileSystemItem *item = static_cast<FileSystemItem*>(index.internalPointer());

      if(item) {
        return item;
      }
  }

  return mRootItem;
}
private:
  FileSystemItem* mRootItem;
  QString mCurrentPath;
  QStringList mHeaders;
  QFileIconProvider* mIconFactory;
};

FileSystemSortProxyModel: Sort/Proxy Model

class FileSystemSortProxyModel  : public QSortFilterProxyModel
{
Q_OBJECT
public:
FileSystemSortProxyModel(QObject *parent) :
    QSortFilterProxyModel(parent)
{
}
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const
{
  FileSystemModel* fsModel = dynamic_cast<FileSystemModel*>(sourceModel());

  // To keep the the folder always on the up, we need to do the followings
  // while the compare indice are folder & file exclusively.
  if(fsModel){
    if((fsModel->isDir(left) && !fsModel->isDir(right))){
      return sortOrder() == Qt::AscendingOrder;
    }
    else if( (!fsModel->isDir(left) && fsModel->isDir(right))){
      return sortOrder() == Qt::DescendingOrder;
    }
  }

  return QSortFilterProxyModel::lessThan(left,right);
}
};

View class
DirBrowser: Now the view class.

class DirBrowser  : public QTableView
{
Q_OBJECT
public: 
DirBrowser(QWidget *parent) :
    QTableView(parent)
{
  grabKeyboard();  

  /* IMPORTANT
     Construct the QFileSystemModel. This is relatively faster
     that QDirModel.

     ****** TODO ******
     However, QFileSystemModel is not fast enough as windows system explorer.
     We have to consider platform specifiq API in this case, like
     FindFirstFile or FindFirstFileEx with looping FindNextFile.
     In this case we have to completely make a View class to show contents on this.
     I have worked with this APIs at past, yet need some R&D's on this.
     In this case, there are challenges like finding the item's icon & item's
     text color.
  */
  //mFSModel = new QFileSystemModel(this);
  mFSModel = new FileSystemModel(this);
  //mFSModel->setRootPath(QDir::currentPath()); // This will populate the filesystem
  mSortModel = new FileSystemSortProxyModel(this);
  mSortModel->setSourceModel(mFSModel);

  this->setShowGrid(false);
  this->verticalHeader()->hide();
  this->setModel(mSortModel);
  this->setSortingEnabled(true);

  /* Now, binds the necessary signals & corresponding slots.
     One thing ro remember is that, we are the setting the signal
     ConnectionType to AutoConnection (default). So if a signal is emitted
     in another thread, yet the slot will run on invoked objects thread.
  */
  // Binds the doubleClicked signal to handleDirItemDoubleClicked slot of this class
  connect(this,SIGNAL(doubleClicked(QModelIndex)),SLOT(handleDirItemDoubleClicked(QModelIndex)));
}

private slots:
void handleDirItemDoubleClicked(const QModelIndex &index)
{
  if(mFSModel->isDir(mSortModel->mapToSource(index))){
    updateCurrentPath(index);
  }
}

protected:
void keyPressEvent(QKeyEvent *e)
{
  // if bakcspace key is pressed, view the dir browser to updates
  // parent folder
  if(e->key() == Qt::Key_Backspace){
    // If parent model-index is valid, up one level; Otherwise
    // back to topmost level
    if(this->rootIndex().parent().isValid()){
      updateCurrentPath(this->rootIndex().parent());
    }
    else{
      this->setRootIndex(mSortModel->mapFromSource(mFSModel->setCurrentPath("")));
    }
  }
}

private:
void updateCurrentPath(const QModelIndex &index)
{
  if(index.isValid()){
    // update the model for current path
    mFSModel->setCurrentPath(mFSModel->absolutePath(mSortModel->mapToSource(index)));
    // update the view as necessarily
    this->setRootIndex(mSortModel->mapFromSource(mFSModel->index(mFSModel->currentPath())));
  }
}
private:
  FileSystemModel* mFSModel;
  FileSystemSortProxyModel* mSortModel;
};

Read Full Post »

Problem
When you will draw text using QPainter’s drawText method, it will make you very slow (Qt experts say that this is due to high computation of glyph of font).

Workaround

  1. Construct a QPainterPath object
  2. Add your text by calling addText method of QPainterPath
  3. draw text using QPainter’s drawPath method

Read Full Post »

How to…?
Please structure your code base as following…

  1. Write a class inheriting descendants class of QAbstractScrollArea (As example QGraphicsView, QMdiArea, QPlainTextEdit, QScrollArea, QTextEdit, QColumnView, QHeaderView, QListView, QTableView, QTreeView etc.)
  2. In the constructor of your class call setViewportMargins and set the margins of left/top/right/bottom areas length.
  3. Create a QGridLayout and adds your custom Ruler/Scale in the layout.
  4. Set this layout calling setLayout

Example…
Once I needed to create horizontal/vertical ruler/scale in a QGraphicsView. I used this technique to do that…

setViewportMargins(RULER_BREADTH,RULER_BREADTH,0,0);
QGridLayout* gridLayout = new QGridLayout();
gridLayout->setSpacing(0);
gridLayout->setMargin(0);

mHorzRuler = new QDRuler(QDRuler::Horizontal);
mVertRuler = new QDRuler(QDRuler::Vertical);

QWidget* fake = new QWidget();
fake->setBackgroundRole(QPalette::Window);
fake->setFixedSize(RULER_BREADTH,RULER_BREADTH);
gridLayout->addWidget(fake,0,0);
gridLayout->addWidget(mHorzRuler,0,1);
gridLayout->addWidget(mVertRuler,1,0);
gridLayout->addWidget(this->viewport(),1,1);

this->setLayout(gridLayout);

QDRuler: The ruler class

#define RULER_BREADTH 20

class QDRuler : public QWidget
{
Q_OBJECT
Q_ENUMS(RulerType)
Q_PROPERTY(qreal origin READ origin WRITE setOrigin)
Q_PROPERTY(qreal rulerUnit READ rulerUnit WRITE setRulerUnit)
Q_PROPERTY(qreal rulerZoom READ rulerZoom WRITE setRulerZoom)
public:
  enum RulerType { Horizontal, Vertical };
QDRuler(QDRuler::RulerType rulerType, QWidget* parent) 
: QWidget(parent), mRulerType(rulerType), mOrigin(0.), mRulerUnit(1.),
  mRulerZoom(1.), mMouseTracking(false), mDrawText(false)
{
  setMouseTracking(true);
	QFont txtFont("Goudy Old Style", 5,20);
	txtFont.setStyleHint(QFont::TypeWriter,QFont::PreferOutline);
	setFont(txtFont);
}

QSize minimumSizeHint() const
{
  return QSize(RULER_BREADTH,RULER_BREADTH);
}

QDRuler::RulerType rulerType() const
{
  return mRulerType;
}

qreal origin() const
{
  return mOrigin;
}

qreal rulerUnit() const
{
  return mRulerUnit;
}

qreal rulerZoom() const
{
  return mRulerZoom;
}

public slots:

void setOrigin(const qreal origin)
{
  if (mOrigin != origin)
  {
    mOrigin = origin;
    update();
  }
}

void setRulerUnit(const qreal rulerUnit)
{
  if (mRulerUnit != rulerUnit)
  {
    mRulerUnit = rulerUnit;
    update();
  }
}

void setRulerZoom(const qreal rulerZoom)
{
  if (mRulerZoom != rulerZoom)
  {
    mRulerZoom = rulerZoom;
    update();
  }
}


void setCursorPos(const QPoint cursorPos)
{
  mCursorPos = this->mapFromGlobal(cursorPos);
  mCursorPos += QPoint(RULER_BREADTH,RULER_BREADTH);
  update();
}

void setMouseTrack(const bool track)
{
  if (mMouseTracking != track)
  {
    mMouseTracking = track;
    update();
  }
}

protected:
void mouseMoveEvent(QMouseEvent* event)
{  
  mCursorPos = event->pos();
  update();  
  QWidget::mouseMoveEvent(event);  
}

void paintEvent(QPaintEvent* event)
{
  QPainter painter(this);
	painter.setRenderHints(QPainter::TextAntialiasing | QPainter::HighQualityAntialiasing);
	QPen pen(Qt::black,0); // zero width pen is cosmetic pen
	//pen.setCosmetic(true);
	painter.setPen(pen);
  // We want to work with floating point, so we are considering
  // the rect as QRectF
  QRectF rulerRect = this->rect();

  // at first fill the rect
  //painter.fillRect(rulerRect,QColor(220,200,180));
  painter.fillRect(rulerRect,QColor(236,233,216));

  // drawing a scale of 25
  drawAScaleMeter(&painter,rulerRect,25,(Horizontal == mRulerType ? rulerRect.height()
		: rulerRect.width())/2);
  // drawing a scale of 50
  drawAScaleMeter(&painter,rulerRect,50,(Horizontal == mRulerType ? rulerRect.height()
		: rulerRect.width())/4);
  // drawing a scale of 100
  mDrawText = true;
  drawAScaleMeter(&painter,rulerRect,100,0);
  mDrawText = false;

  // drawing the current mouse position indicator
	painter.setOpacity(0.4);
  drawMousePosTick(&painter);
	painter.setOpacity(1.0);

  // drawing no man's land between the ruler & view
  QPointF starPt = Horizontal == mRulerType ? rulerRect.bottomLeft()
      : rulerRect.topRight();
  QPointF endPt = Horizontal == mRulerType ? rulerRect.bottomRight()
      : rulerRect.bottomRight();
  painter.setPen(QPen(Qt::black,2));
  painter.drawLine(starPt,endPt);
}

private:
void drawAScaleMeter(QPainter* painter, QRectF rulerRect, qreal scaleMeter, qreal startPositoin)
{
  // Flagging whether we are horizontal or vertical only to reduce
  // to cheching many times
  bool isHorzRuler = Horizontal == mRulerType;

  scaleMeter  = scaleMeter * mRulerUnit * mRulerZoom;

  // Ruler rectangle starting mark
  qreal rulerStartMark = isHorzRuler ? rulerRect.left() : rulerRect.top();
  // Ruler rectangle ending mark
  qreal rulerEndMark = isHorzRuler ? rulerRect.right() : rulerRect.bottom();

  // Condition A # If origin point is between the start & end mard,
  //we have to draw both from origin to left mark & origin to right mark.
  // Condition B # If origin point is left of the start mark, we have to draw
  // from origin to end mark.
  // Condition C # If origin point is right of the end mark, we have to draw
  // from origin to start mark.
  if (mOrigin >= rulerStartMark && mOrigin <= rulerEndMark)
  {		
    drawFromOriginTo(painter, rulerRect, mOrigin, rulerEndMark, 0, scaleMeter, startPositoin);
    drawFromOriginTo(painter, rulerRect, mOrigin, rulerStartMark, 0, -scaleMeter, startPositoin);
  }
  else if (mOrigin < rulerStartMark)
  {
		int tickNo = int((rulerStartMark - mOrigin) / scaleMeter);
		drawFromOriginTo(painter, rulerRect, mOrigin + scaleMeter * tickNo,
			rulerEndMark, tickNo, scaleMeter, startPositoin);
  }
  else if (mOrigin > rulerEndMark)
  {
		int tickNo = int((mOrigin - rulerEndMark) / scaleMeter);
    drawFromOriginTo(painter, rulerRect, mOrigin - scaleMeter * tickNo, 
			rulerStartMark, tickNo, -scaleMeter, startPositoin);
  }
}

void drawFromOriginTo(QPainter* painter, QRectF rulerRect, qreal startMark, qreal endMark, int startTickNo, qreal step, qreal startPosition)
{
  bool isHorzRuler = Horizontal == mRulerType;
  int iterate = 0;

  for (qreal current = startMark;
      (step < 0 ? current >= endMark : current <= endMark); current += step)
  {
    qreal x1 = isHorzRuler ? current : rulerRect.left() + startPosition;
    qreal y1 = isHorzRuler ? rulerRect.top() + startPosition : current;
    qreal x2 = isHorzRuler ? current : rulerRect.right();
    qreal y2 = isHorzRuler ? rulerRect.bottom() : current;
    painter->drawLine(QLineF(x1,y1,x2,y2));
    if (mDrawText)
    {
      QPainterPath txtPath;
			txtPath.addText(x1 + 1,y1 + (isHorzRuler ? 7 : -2),this->font(),QString::number(qAbs(int(step) * startTickNo++)));
      painter->drawPath(txtPath);
      iterate++;
    }
  }
}

void drawMousePosTick(QPainter* painter)
{
  if (mMouseTracking)
  {
    QPoint starPt = mCursorPos;
    QPoint endPt;
    if (Horizontal == mRulerType)
    {
      starPt.setY(this->rect().top());
      endPt.setX(starPt.x());
      endPt.setY(this->rect().bottom());
    }
    else
    {
      starPt.setX(this->rect().left());
      endPt.setX(this->rect().right());
      endPt.setY(starPt.y());
    }
    painter->drawLine(starPt,endPt);
  }
}
private:
  RulerType mRulerType;
  qreal mOrigin;
  qreal mRulerUnit;
  qreal mRulerZoom;
  QPoint mCursorPos;
  bool mMouseTracking;
  bool mDrawText;
};

Read Full Post »

Background
Qt Provides a framework for separation of Data and View that show the data. This framework a simplified version of MVC, where controller embedded into View. Event after this separation it possible to display the same data in several different views, and to implement new types of views, without changing the underlying data structures.

Model-View Architecture

Here model pulling data from the data source (files, databases or memory) & arrange the data according to a hierarchical structure. Views make the presentation of the data to user. The data editing is done through Delegate and again save it to Model. Model is responsible for saving data to data source.
Qt provided base classes for Model, View & Delegate are QAbstractItemModel, QAbstractItemView & QAbstractItemDelegate respectively. To use the existing models & views, it is very simple…

QFileSystemModel *pModel = new QFileSystemModel();
QTreeView *pTreeView = QTreeView();
pTreeView->setModel(pModel);

Lets gets on Models…
Models are responsible for pulling the data from data source. Arrange data following a hierarchical structure so that views/delegagte can get/set data (using an index called model index). Notify the View & Delegate while data has been modified. Actually there are three types of views & so as models — List, Table & Tree.

Model Types

To ensure that the representation of the data is kept separate from the way it is accessed, the concept of a model index is introduced.

Model Index

Qt Provided Ready-made Models

  1. List Model (QStringListModel)
        QStringList names;
        lst <<"Sujon" <<"Roton"<<"Rahim";
        QStringListModel *pModel = new QStringListModel(names,this);
        QListView *pView = new QListView(this);
        pView->setModel(pModel);
    
  2. Table Model (QSqlQueryModel, QSqlTableModel, QSqlRelationalTableModel)
       QSqlTableModel *pModel = new QSqlTableModel;
       pModel->setTable("employee");
       QTableView *pView = new QTableView;
       pView->setModel(pModel);
       pView->show();
    
  3. Tree Model: example given in the background section.

Creating a New Model
Think which type of model do you need? If you need List or Table type Model, consider to subclassing from QAbstractListModel or QAbstractTableModel. If it is tree type model, use QAbstractItemModel.

Subclassing List Model

  • Read-Only Models: rowCount() and data().
  • Editable Models: flags() and setData()
  • Resizing Models: insertRows() and removeRows()

Subclassing Table Model

  • Read-Only Models: rowCount(), columnCount() and data().
  • Editable Models: flags() and setData()
  • Resizing Models: insertRows(), removeRows(), insertColumn() and removeColumn()

Subclassing Tree Model

  • Read-Only Models: index(), parent(), rowCount(),columnCount(), and data(). If rowCount() is expensive, it is better to override hasChildren().
  • Editable Models: flags(), setData()
  • Resizing Models: insertRows(), removeRows(), insertColumn(), removeColumn()

Example of subclassing List Model
class declaration…

class StringListModel : public QAbstractListModel
{
Q_OBJECT
public:
    StringListModel(const QStringList &strings, QObject *parent = 0);

 // read only
    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role) const;

    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const;

// editable
    Qt::ItemFlags flags(const QModelIndex &index) const;
         bool setData(const QModelIndex &index, const QVariant &value,
                      int role = Qt::EditRole);
// resizable
    bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
    bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());

private:
    QStringList mStringList;
};

class definition…

StringListModel::StringListModel(const QStringList &strings,QObject *parent) :
    QAbstractListModel(parent),mStringList(strings)
{
}

int StringListModel::rowCount(const QModelIndex &parent) const
 {
     return mStringList.count();
 }

QVariant StringListModel::data(const QModelIndex &index, int role) const
 {
    if (!index.isValid())
        return QVariant();

    if (index.row() >= mStringList.size())
        return QVariant();

    if (role == Qt::DisplayRole || role == Qt::EditRole)
        return mStringList.at(index.row());
    else
        return QVariant();
 }

QVariant StringListModel::headerData(int section, Qt::Orientation orientation,
                                      int role) const
 {
     if (role != Qt::DisplayRole)
         return QVariant();

     if (orientation == Qt::Horizontal)
         return QString("Column %1").arg(section);
     else
         return QString("Row %1").arg(section);
 }

Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const
 {
     if (!index.isValid())
         return Qt::ItemIsEnabled;

     return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
 }

bool StringListModel::setData(const QModelIndex &index,
                              const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole) {

        mStringList.replace(index.row(), value.toString());
        emit dataChanged(index, index);
        return true;
    }
    return false;
}

bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent)
{
    beginInsertRows(QModelIndex(), position, position+rows-1);

    for (int row = 0; row < rows; ++row) {
        mStringList.insert(position, "");
    }

    endInsertRows();
    return true;
}

bool StringListModel::removeRows(int position, int rows, const QModelIndex &parent)
{
    beginRemoveRows(QModelIndex(), position, position+rows-1);

    for (int row = 0; row < rows; ++row) {
        mStringList.removeAt(position);
    }

    endRemoveRows();
    return true;
}

Example of subclassing Tree Model
class declaration…

// We will use TreeItem class to represent an item in our Tree model. That is this class //is form the data structure of our Model class.
class TreeItem
{
public:
    TreeItem(const QVector<QVariant> &data, TreeItem *parent = 0);
    ~TreeItem();

    TreeItem *child(int number);
    int childCount() const;
    int columnCount() const;
    QVariant data(int column) const;
    bool insertChildren(int position, int count, int columns);
    bool insertColumns(int position, int columns);
    TreeItem *parent();
    bool removeChildren(int position, int count);
    bool removeColumns(int position, int columns);
    int childNumber() const;
    bool setData(int column, const QVariant &value);

private:
    QList<TreeItem*> childItems;
    QVector<QVariant> itemData;
    TreeItem *parentItem;
};

// This is model class declaration
class TreeModel : public QAbstractItemModel
{
    Q_OBJECT

public:
    TreeModel(const QStringList &headers, const QString &data,
              QObject *parent = 0);
    ~TreeModel();

    QVariant data(const QModelIndex &index, int role) const;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const;

    QModelIndex index(int row, int column,
                      const QModelIndex &parent = QModelIndex()) const;
    QModelIndex parent(const QModelIndex &index) const;

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;

    Qt::ItemFlags flags(const QModelIndex &index) const;
    bool setData(const QModelIndex &index, const QVariant &value,
                 int role = Qt::EditRole);
    bool setHeaderData(int section, Qt::Orientation orientation,
                       const QVariant &value, int role = Qt::EditRole);

    bool insertColumns(int position, int columns,
                       const QModelIndex &parent = QModelIndex());
    bool removeColumns(int position, int columns,
                       const QModelIndex &parent = QModelIndex());
    bool insertRows(int position, int rows,
                    const QModelIndex &parent = QModelIndex());
    bool removeRows(int position, int rows,
                    const QModelIndex &parent = QModelIndex());

private:
    void setupModelData(const QStringList &lines, TreeItem *parent);
    TreeItem *getItem(const QModelIndex &index) const;

    TreeItem *rootItem;
};

And the definitions are…

// TreeItem class's definition

TreeItem::TreeItem(const QVector<QVariant> &data, TreeItem *parent)
{
    parentItem = parent;
    itemData = data;
}

TreeItem::~TreeItem()
{
    qDeleteAll(childItems);
}

TreeItem *TreeItem::child(int number)
{
    return childItems.value(number);
}

int TreeItem::childCount() const
{
    return childItems.count();
}

int TreeItem::childNumber() const
{
    if (parentItem)
        return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));

    return 0;
}

int TreeItem::columnCount() const
{
    return itemData.count();
}

QVariant TreeItem::data(int column) const
{
    return itemData.value(column);
}

bool TreeItem::insertChildren(int position, int count, int columns)
{
    if (position < 0 || position > childItems.size())
        return false;

    for (int row = 0; row < count; ++row) {
        QVector<QVariant> data(columns);
        TreeItem *item = new TreeItem(data, this);
        childItems.insert(position, item);
    }

    return true;
}

bool TreeItem::insertColumns(int position, int columns)
{
    if (position < 0 || position > itemData.size())
        return false;

    for (int column = 0; column < columns; ++column)
        itemData.insert(position, QVariant());

    foreach (TreeItem *child, childItems)
        child->insertColumns(position, columns);

    return true;
}

TreeItem *TreeItem::parent()
{
    return parentItem;
}

bool TreeItem::removeChildren(int position, int count)
{
    if (position < 0 || position + count > childItems.size())
        return false;

    for (int row = 0; row < count; ++row)
        delete childItems.takeAt(position);

    return true;
}

bool TreeItem::removeColumns(int position, int columns)
{
    if (position < 0 || position + columns > itemData.size())
        return false;

    for (int column = 0; column < columns; ++column)
        itemData.remove(position);

    foreach (TreeItem *child, childItems)
        child->removeColumns(position, columns);

    return true;
}

bool TreeItem::setData(int column, const QVariant &value)
{
    if (column < 0 || column >= itemData.size())
        return false;

    itemData[column] = value;
    return true;
}

// Model class's definition
TreeModel::TreeModel(const QStringList &headers, const QString &data,
                     QObject *parent)
    : QAbstractItemModel(parent)
{
    QVector<QVariant> rootData;
    foreach (QString header, headers)
        rootData << header;

    rootItem = new TreeItem(rootData);
    setupModelData(data.split(QString("\n")), rootItem);
}

TreeModel::~TreeModel()
{
    delete rootItem;
}

int TreeModel::columnCount(const QModelIndex & /* parent */) const
{
    return rootItem->columnCount();
}

QVariant TreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role != Qt::DisplayRole && role != Qt::EditRole)
        return QVariant();

    TreeItem *item = getItem(index);

    return item->data(index.column());
}

Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;

    return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

TreeItem *TreeModel::getItem(const QModelIndex &index) const
{
    if (index.isValid()) {
        TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
        if (item) return item;
    }
    return rootItem;
}

QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                               int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
        return rootItem->data(section);

    return QVariant();
}

//! [5]
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
    if (parent.isValid() && parent.column() != 0)
        return QModelIndex();

    TreeItem *parentItem = getItem(parent);

    TreeItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

bool TreeModel::insertColumns(int position, int columns, const QModelIndex &parent)
{
    bool success;

    beginInsertColumns(parent, position, position + columns - 1);
    success = rootItem->insertColumns(position, columns);
    endInsertColumns();

    return success;
}

bool TreeModel::insertRows(int position, int rows, const QModelIndex &parent)
{
    TreeItem *parentItem = getItem(parent);
    bool success;

    beginInsertRows(parent, position, position + rows - 1);
    success = parentItem->insertChildren(position, rows, rootItem->columnCount());
    endInsertRows();

    return success;
}

QModelIndex TreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    TreeItem *childItem = getItem(index);
    TreeItem *parentItem = childItem->parent();

    if (parentItem == rootItem)
        return QModelIndex();

    return createIndex(parentItem->childNumber(), 0, parentItem);
}

bool TreeModel::removeColumns(int position, int columns, const QModelIndex &parent)
{
    bool success;

    beginRemoveColumns(parent, position, position + columns - 1);
    success = rootItem->removeColumns(position, columns);
    endRemoveColumns();

    if (rootItem->columnCount() == 0)
        removeRows(0, rowCount());

    return success;
}

bool TreeModel::removeRows(int position, int rows, const QModelIndex &parent)
{
    TreeItem *parentItem = getItem(parent);
    bool success = true;

    beginRemoveRows(parent, position, position + rows - 1);
    success = parentItem->removeChildren(position, rows);
    endRemoveRows();

    return success;
}

int TreeModel::rowCount(const QModelIndex &parent) const
{
    TreeItem *parentItem = getItem(parent);

    return parentItem->childCount();
}

bool TreeModel::setData(const QModelIndex &index, const QVariant &value,
                        int role)
{
    if (role != Qt::EditRole)
        return false;

    TreeItem *item = getItem(index);
    bool result = item->setData(index.column(), value);

    if (result)
        emit dataChanged(index, index);

    return result;
}

bool TreeModel::setHeaderData(int section, Qt::Orientation orientation,
                              const QVariant &value, int role)
{
    if (role != Qt::EditRole || orientation != Qt::Horizontal)
        return false;

    bool result = rootItem->setData(section, value);

    if (result)
        emit headerDataChanged(orientation, section, section);

    return result;
}

Views
There are three types of views — List, Table & Tree. These are enough, but if you need a customize view, you can subclassing it.

Delegate
If you want to set just a control (widget), you may inherit QItemDelegate or QStyledItemDelegate and override createEditor() , setEditorData() and setModelData(). If you want to paint the control yourself, you need inherit QAbstractItemDelegate & also override paint(), sizeHint() in addition. Here is sample code that inheriting QItemDelegate.

// delegate codes
QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
    const QStyleOptionViewItem &opt/* option */,
    const QModelIndex &mi/* index */) const
{
    QSpinBox *editor = new QSpinBox(parent);
    QPoint range = GameSettings::me()->integerSettingRange(Game::Setting(mi.row()));
    editor->setMinimum(range.x());
    editor->setMaximum(range.y());

    return editor;
}

void SpinBoxDelegate::setEditorData(QWidget *editor,
                                    const QModelIndex &index) const
{
    //int value = index.model()->data(index, Qt::EditRole).toInt();

    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    spinBox->setValue(GameSettings::me()->setting(Game::Setting(index.row())).value<uint>());
}

void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                   const QModelIndex &index) const
{
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    spinBox->interpretText();
    int value = spinBox->value();

    model->setData(index, value, Qt::EditRole);
}

// Using that delegate
QTableView *pTableView = new QTableView();
SpinBoxDelegate *pSpinboxDelegate = new SpinBoxDelegate();
pTableView->setItemDelegate(pSpinboxDelegate);

Proxy Model
If you want to sorting & filtering on the data, you need to use QAbstractProxyModel (inherited from QAbstractItemModel) descendant classes like QSortFilterProxyModel. If you decided to inherit QAbstractProxyModel , you must override mapFromSource() & mapToSource(). Here is the sample code on sorting & filtering…

    TreeModel *pModel = new TreeModel();
    QSortFilterProxyModel *pProxyModel = new QSortFilterProxyModel();
    pProxyModel->setSourceModel(pModel);
    QTreeView *pView = new QTreeView();
    pView->setModel(pProxyModel);

// the following two lines will filter the column 1 for string "How to"
    pProxyModel->setFilterRegExp(QRegExp("How to", Qt::CaseInsensitive,
                                                 QRegExp::FixedString));
    pProxyModel->setFilterKeyColumn(1);

// the following line will make view sortable.
    pView->setSortingEnabled(true);

The proxy model stands between the actual model & view in the model-view architecture. So the same sorting for same model will share among multiple views & only the sorting & filtering algorithm get execution only one time.

Read Full Post »

Using QMutex Class
When you call lock() on a thread, other calls to lock() from other thread will block until the thread that owns the lock calls unlock(). This method is blocking way to owns a lock. The non-blocking way is to call tryLock(), which returns true if a lock owns, false otherwise. Let us see now how to use QMutex class. Consider the following code…

int data = 6;
 void method()
 {
     data *= 5;
     data /= 4;
 }

If the above method get called twice successively in a single thread, the value of the data is 8.
If the above method get called simultaneously from two thread, the following could happened…

// Thread 1 calls method()
 data *= 5;        // data is now 30

 // Thread 2 calls method().
 //
 // Most likely Thread 1 has been put to sleep by the operating
 // system to allow Thread 2 to run.
 data *= 5;        // data is now 150
 data /= 4;        // data is now 37

 // Thread 1 finishes executing.
 data /= 4;        // data is now 9, instead of 8

Now if we use a mutex, we can resolve it as follows …

 QMutex mutex;
 int data = 6;
 void method()
 {
     mutex.lock();
     data *= 5;
     data /= 4;
     mutex.unlock();
 }

So, we got that the QMutex class provides access serialization between threads.

Using QWaitCondition with QMutex
You know, there are some situation arises when a thread must wait to continue for some conditions to meet. Think a wait condition as it signaling always that the condition is true until a call to wait() while it signals that the condition is false. Again when a call to wakeOne() or wakeAll() is made, it signals that the condition is true. To state the situation lets consider a problem domain, Producer – Consumer Problem.

The producer writes data to the buffer until it reaches the end of the buffer, at which point it restarts from the beginning, overwriting existing data. The consumer thread reads the data as it is produced and writes it to standard error.
Wait conditions make it possible to have a higher level of concurrency than what is possible with mutexes alone. If accesses to the buffer were simply guarded by a QMutex, the consumer thread couldn’t access the buffer at the same time as the producer thread. Yet, there is no harm in having both threads working on different parts of the buffer at the same time.
Let’s start by reviewing the circular buffer and the associated synchronization tools:

 const int DataSize = 100000;
 const int BufferSize = 8192;
 char buffer[BufferSize];

 QWaitCondition bufferNotEmpty;
 QWaitCondition bufferNotFull;
 QMutex mutex;
 int numUsedBytes = 0;

DataSize is the amount of data that the producer will generate. To keep the example as simple as possible, we make it a constant. BufferSize is the size of the circular buffer. It is less than DataSize, meaning that at some point the producer will reach the end of the buffer and restart from the beginning.

To synchronize the producer and the consumer, we need two wait conditions and one mutex. The bufferNotEmpty condition is signalled when the producer has generated some data, telling the consumer that it can start reading it. The bufferNotFull condition is signalled when the consumer has read some data, telling the producer that it can generate more. The numUsedBytes is the number of bytes in the buffer that contain data.

Together, the wait conditions, the mutex, and the numUsedBytes counter ensure that the producer is never more than BufferSize bytes ahead of the consumer, and that the consumer never reads data that the consumer hasn’t generated yet.

Producer Class: Let’s review the code for the Producer class…

 class Producer : public QThread
 {
 public:
     void run();
 };

 void Producer::run()
 {
     qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));

     for (int i = 0; i < DataSize; ++i) {
         mutex.lock();
         if (numUsedBytes == BufferSize)
             bufferNotFull.wait(&mutex);
         mutex.unlock();

         buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];

         mutex.lock();
         ++numUsedBytes;
         bufferNotEmpty.wakeAll();
         mutex.unlock();
     }
 }

The producer generates DataSize bytes of data. Before it writes a byte to the circular buffer, it must first check whether the buffer is full (i.e., numUsedBytes equals BufferSize). If the buffer is full, the thread waits on the bufferNotFull condition.

At the end, the producer increments numUsedBytes and signalls that the condition bufferNotEmpty is true, since numUsedBytes is necessarily greater than 0.

We guard all accesses to the numUsedBytes variable with a mutex. In addition, the QWaitCondition::wait() function accepts a mutex as its argument. This mutex is unlocked before the thread is put to sleep and locked when the thread wakes up. Furthermore, the transition from the locked state to the wait state is atomic, to prevent race conditions from occurring.

Consumer Class: Let’s turn to the Consumer class…

 class Consumer : public QThread
 {
 public:
     void run();
 };

 void Consumer::run()
 {
     for (int i = 0; i < DataSize; ++i) {
         mutex.lock();
         if (numUsedBytes == 0)
             bufferNotEmpty.wait(&mutex);
         mutex.unlock();

         fprintf(stderr, "%c", buffer[i % BufferSize]);

         mutex.lock();
         --numUsedBytes;
         bufferNotFull.wakeAll();
         mutex.unlock();
     }
     fprintf(stderr, "\n");
 }

The code is very similar to the producer. Before we read the byte, we check whether the buffer is empty (numUsedBytes is 0) instead of whether it’s full and wait on the bufferNotEmpty condition if it’s empty. After we’ve read the byte, we decrement numUsedBytes (instead of incrementing it), and we signal the bufferNotFull condition (instead of the bufferNotEmpty condition).

Using the classes: In main(), we create the two threads and call QThread::wait() to ensure that both threads get time to finish before we exit:

int main(int argc, char *argv[])
 {
     QCoreApplication app(argc, argv);
     Producer producer;
     Consumer consumer;
     producer.start();
     consumer.start();
     producer.wait();
     consumer.wait();
     return 0;
 }

So what happens when we run the program? Initially, the producer thread is the only one that can do anything; the consumer is blocked waiting for the bufferNotEmpty condition to be signalled (numUsedBytes is 0). Once the producer has put one byte in the buffer, numUsedBytes is BufferSize – 1 and the bufferNotEmpty condition is signalled. At that point, two things can happen: Either the consumer thread takes over and reads that byte, or the consumer gets to produce a second byte.

Using QSemaphore
A semaphore is a generalization of a mutex. While a mutex can only be locked once, it’s possible to acquire a semaphore multiple times. Semaphores are typically used to guard a certain number of identical resources.

Semaphores support two fundamental operations, acquire() and release():

  • acquire(n) tries to acquire n resources. If there aren’t that many resources available, the call will block until this is the case.
  • release(n) releases n resources.

Using QSemaphore …

 QSemaphore sem(5);      // sem.available() == 5

 sem.acquire(3);         // sem.available() == 2
 sem.acquire(2);         // sem.available() == 0
 sem.release(5);         // sem.available() == 5
 sem.release(5);         // sem.available() == 10

 sem.tryAcquire(1);      // sem.available() == 9, returns true
 sem.tryAcquire(250);    // sem.available() == 9, returns false

Solving Producer-Consumer Problem with QSemaphore…

// globar variables
const int DataSize = 100000;
 const int BufferSize = 8192;
 char buffer[BufferSize];

 QSemaphore freeBytes(BufferSize);
 QSemaphore usedBytes;

// Producer Class
 class Producer : public QThread
 {
 public:
     void run();
 };

 void Producer::run()
 {
     qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
     for (int i = 0; i < DataSize; ++i) {
         freeBytes.acquire();
         buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
         usedBytes.release();
     }
 }

// Consumer Class
class Consumer : public QThread
 {
 public:
     void run();
 };

 void Consumer::run()
 {
     for (int i = 0; i < DataSize; ++i) {
         usedBytes.acquire();
         fprintf(stderr, "%c", buffer[i % BufferSize]);
         freeBytes.release();
     }
     fprintf(stderr, "\n");
 }

// using producer & consumer
 int main(int argc, char *argv[])
 {
     QCoreApplication app(argc, argv);
     Producer producer;
     Consumer consumer;
     producer.start();
     consumer.start();
     producer.wait();
     consumer.wait();
     return 0;
 }

Read Full Post »

How to Use?
Just inherit QThread class and override run method. Now while you call start slot through an instance of the inherited class, then while your overrided run get execution, another thread of control is begin with the starting point as run method. That is, run method execution life cycle (scope) run on another thread.

// Your inherited thread class
class MyThread : public QThread
 {
Q_OBJECT
 public:
     MyThread(QObject *parent):QObject(parent){}
     void run() // consider run() function as the main() function
     {
         // here you are running on another thread.
     }
 };

// Use your MyThread class
void createAThread()
{
MyThread *pThread = new MyThread(this);
pThread->start();
}

If you want to run an event loop on a thread, call exec method in the run method as follows.

class MyThread : public QThread
 {
Q_OBJECT
 public:
     MyThread(QObject *parent):QObject(parent){}
     void run()
     {
         // here you are running on another thread.
        // create your object here and connect their signal & slot
         exec(); // event loop began
     }
 };

Object Scope In Threaded System
Qt’s expectation is that an object which created in a thread, also be deleted from that thread. All the objects at the object-stack (child-parent tree) of an object will also be on that thread. You must delete all the objects at an object-stack on its own thread (you can get a thread on which an object lives by thread method and you can change an object’s thread by moveToThread method), or call deleteLater method for deferred deletion from another thread.
You must not use the thread object itself as parent for the objects that will be created in run method, because the thread object itself is created at another thread. Actually you need to follow the coding format that you use in main method (Main window is constructed on the stack without any parent and main window being the top most parent of all objects in main thread).
When you work on same data from multiple threads, use synchronization techniques (mutex, semaphore etc) to provide synchronization among threads.

Signals and Slots Across Threads
Qt supports these signal-slot connection types:

  • Auto Connection (default): The behavior is the same as the Direct Connection, if the emitter and receiver are in the same thread. The behavior is the same as the Queued Connection, if the emitter and receiver are in different threads.
  • Direct Connection: The slot is invoked immediately, when the signal is emitted. The slot is executed in the emitter’s thread, which is not necessarily the receiver’s thread.
  • Queued Connection: The slot is invoked when control returns to the event loop of the receiver’s thread. The slot is executed in the receiver’s thread.
  • Blocking Queued Connection: The slot is invoked as for the Queued Connection, except the current thread blocks until the slot returns. Note: Using this type to connect objects in the same thread will cause deadlock.
  • Unique Connection: The behavior is the same as the Auto Connection, but the connection is made only if it does not duplicate an existing connection. i.e., if the same signal is already connected to the same slot for the same pair of objects, then the connection is not made and connect() returns false.

The connection type can be specified by passing an additional argument to connect(). Be aware that using direct connections when the sender and receiver live in different threads is unsafe if an event loop is running in the receiver’s thread, for the same reason that calling any function on an object living in another thread is unsafe.

Read Full Post »

Using QTimer class
QTimer inherits QObject. The QTimer class provides a high-level programming interface for timers. It provides repetitive & single-shot timer.
For single-shot timer use singleShot static method of QTimer class.

class TimerUser: public QObject
{
    Q_OBJECT
public:
    TimerUser(QObject *parent = 0):QObject(parent)
    {
        QTimer::singleShot(1000,this,SLOT(timerMethod()));
    }
private slots:
    void timerMethod()
    {
        // Do your timer work
    }
};

To use repetitive timer, create an object of QTimer, connect its timeout signal & class start method of it.

class TimerUser: public QObject
{
Q_OBJECT
public:
TimerUser(QObject *parent = 0):QObject(parent)
{
QTimer *pTimer = new QTimer(this);
connect(pTimer,SIGNAL(timeout()),SLOT(timerMethod()));
pTimer-&gt;start(1000);
}
private slots:
void timerMethod()
{
// Do your timer work
}
};

As a special case, a QTimer with a timeout of 0 will time out as soon as all the events in the window system’s event queue have been processed. This can be used to do heavy work while providing a snappy user interface:

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(processOneThing()));
timer->start();

processOneThing() will from then on be called repeatedly. It should be written in such a way that it always returns quickly (typically after processing one data item) so that Qt can deliver events to widgets and stop the timer as soon as it has done all its work. This is the traditional way of implementing heavy work in GUI applications; multithreading is now becoming available on more and more platforms, and Qt expect that zero-millisecond QTimers will gradually be replaced by QThreads.

Using QBasicTimer
This is a fast, lightweight, and low-level class used by Qt internally. Qt recommend using the higher-level QTimer class rather than this class if you want to use timers in your applications.
The QBasicTimer class provides timer events for QObject inherited classes. That is, it calls timerEvent method of QObject inherited classes.

class TimerUser: public QObject
{
Q_OBJECT
public:
TimerUser(QObject *parent = 0):QObject(parent)
{
mTimer.start(1000,this);
}
protected:
void timerEvent(QTimerEvent *pEvent)
{
// Do your timer work
}

private:
QBasicTimer mTimer;
};

Use timer id from pEvent object to determine which timer posted this event and do work accordingly.

Read Full Post »

Qt provides a very sophisticated property system which provides by compiler vendor (using keyword __property or [property]). But that depends on that compiler. But Qt provides similar system with compiler independent & platform independent.

How to Declare a Property?
Q_PROPERTY(type name
READ getFunction
[WRITE setFunction]
[RESET resetFunction]
[NOTIFY notifySignal]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])

Here:

  • type: is the data type that this property will set & get
  • name: is the property’s name
  • getFunction, setFunction & resetFunction: are the get set & reset function of this property respectively.
  • notifySignal:
  • will be emitted while the property value is changed.

  • DESIGNABLE true/false (default true): indicates whether this property is usable on the property sheet at Qt Designer.
  • SCRIPTABLE true/false (default true): indicates whether this property should be accessible by a scripting engine (default true). Instead of true or false, you can specify a boolean member function
  • STORED true/false (default true): indicates whether this property will be used for a fixed data field or will use a shared data field.
  • USER true/false (default false): indicates whether this property will be changed directly by user from gui at runtime.
  • The presence of the CONSTANT attibute indicates that the property value is constant. So setFunction& resetFunction will not exist.
  • FINAL attribute indicates that no inherited class will override this property.

As example…
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)

And class structure is…

class PropertyExample : QObject
{
    Q_OBJECT
    Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
public:
    PropertyExample(QObject *parent):QObject(parent){}
    // get method
    QCursor cursor()const{return mCursor;}
    // set method
    void setCursor(const QCursor &cursor){mCursor = cursor;}
    // reset method
    void unsetCursor(){mCursor = QCursor;}
private:
    QCursor mCursor;
};

Reading and Writing Properties with the Meta-Object System
We will use setProperty & property methods of QObject for setting & getting of a property value through QObject pointer of a class. Note that this way is slower than using directly setFunction & getFunction .
Example…

QPushButton *button = new QPushButton;
 QObject *object = button;
 button->setDown(true);
 object->setProperty("down", true);

Handling Custom Types
If the type we are using for a property is a enum type, register it to meta-object-system using Q_ENUM macro. If it is a ORed or ANDed value of enum type use Q_FLAGS macro. If it is a cumtom class, register it using Q_DECLARE_METATYPE

Dynamic Property
QObject::setProperty() can also be used to add new properties to an instance of a class at runtime. When it is called with a name and a value, if a property with the given name exists in the QObject, and if the given value is compatible with the property’s type, the value is stored in the property, and true is returned. If the value is not compatible with the property’s type, the property is not changed, and false is returned. But if the property with the given name doesn’t exist in the QObject (i.e., if it wasn’t declared with Q_PROPERTY(), a new property with the given name and value is automatically added to the QObject, but false is still returned. This means that a return of false can’t be used to determine whether a particular property was actually set, unless you know in advance that the property already exists in the QObject.

Note that dynamic properties are added on a per instance basis, i.e., they are added to QObject, not QMetaObject. A property can be removed from an instance by passing the property name and an invalid QVariant value to QObject::setProperty(). The default constructor for QVariant constructs an invalid QVariant.

Dynamic properties can be queried with QObject::property(), just like properties declared at compile time with Q_PROPERTY().

Conclusion
This property mechanism is used with property animation, state-machine framework.

Read Full Post »

It is very easy to work with preference settings in Qt.

Loading Prefernece
Suppose you prefer to keep your main windows size and position synchronized with the last time you closed your main window.

// Call the following method in your main windows constructor
void loadPreference()
{
QSettings preference;
this.setGeometry(preference.value("Geometry/MainWindow",this->geometry()).value<QRect>());
}

You just need to construct an object of type QSetting and use it’svalue method to obtain the save value. The first parameter is a unique name. It is better to follow a protocol when naming this unique parameter ( I used Property/Object protocol). The second parameter is default value. This ma be used only for the first time of your application running.

Saving Prefernece
Suppose you prefer to keep your main windows size and position saved so that you can synchronized when next time you will view your main window.

// Call the following method in your main windows closed event
void savePreference()
{
QSettings preference;
preference.setValue("Geometry/MainWindow",this->geometry()));
}

It is better to set your application name and your organization name while you construct QApplication object by setApplicationName and setOrganizationName. This information will be used by QSettings object to create your preference settings file.

Qt Care Us Much
Event Qt provides some handy methods which make your work easy. As example QMainWindow provides saveGeometry and saveState methods to save your main windows geometry and states of toolbars & dock widgets.

// Use the code while your main window is loading
QSettings settings;
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());

// Use the code while your main window is closing
QSettings settings;
settings.setValue("geometry", saveGeometry());
settings.setValue("windowState", saveState());

Read Full Post »

Older Posts »