25 June 2012

Common Qt related queries

While using Qt, programmers tend to forget lot of small things, which they need to search again and again while coding. So I compiled some of these issues which I came across in this document.

->Creating modal dialogs in Qt
       CustomMessageBox customMessageBox(..., parent, Qt::Dialog);
       customMessageBox.setWindowModality(Qt::ApplicationModal);
       customMessageBox.exec();

-> Regular expression to check whether MAC address exists in a string
   <string>.contains(QRegExp("(([0-9A-Fa-f]{2}[-:]){5}[0-9A-Fa-f]{2})|(([0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})"))

-> Saving QPixmap as file. -

    QPixmap myImage(":/test.jpg");
     myImage.save("newTest.jpg");

Or
   QByteArray bytes;
   QBuffer buffer(&bytes);
   buffer.open(QIODevice::WriteOnly);
   pixmap.save(&buffer, "PNG");


-> Creating simple thread in Qt -
   Create a class inheriting QThread with a function void run().  The content of this function runs on a thread.
   class HelloThread: public QThread
    {
      Q_OBJECT
      private:
            void run();
    }

     void HelloThread::run()
    {
              qDebug()<<"Hello from worker thread" <<thread()->currentThreadId();
    }

    int main(int argc, char *argv[])
   {
         QCoreApplication app(argc, argv);
         HelloThread thread;
         thread.start();
         qDebug()<<"hello from GUI thread"<<app.thread()->currentThreadId();
         thread.wait();
         return 0;
    }

-> Difference between two QDateTime values
       use    QDateTime::daysTo() or secsTo().

-> Disabling a window
       mainWindow->setEnabled(false);
    If there are multiple windows, its better to disable them in pieces.

->  Redirection operator(<<, >>) overloading in a class
      Add a operator overloading friend function to the class as -
     friend std::ostream& operator<<(std::ostream &out, const MyClass &rhs);

     After defining this function, as -
     std::ostream& operator<<(std::ostream &out, const MyClass &rhs)
     {
           out<<rhs.name;
     }

   The class's objects can be used with << operator.

-> Deploying Qt application on windows
    Compile your project in release mode.
    Add resources and settings to your application as shown here.
    Then use dependency walker to check for the dll dependencies of your application. Copy these dlls into the folder of the executable.
    Finally use setup creating softwares like Inno Setup Compiler to package everything in a single executable.

->  View QTreeWidget as read only 
  treeWidget->editTriggers(QAbstractItemView::NoEditTriggers);

-> Change column width of QTreeWidget
   header()->resizeSection(int logicalIndex, int size);

-> Getting QGraphicsTextItem length 
   int length = my_graphics_text_item.toPlainText().size()


-> Find index of a value in QComboBox 
  int QComboBox::findText(const QString &text, Qt::MatchFlags flags = static_cast<Qt::MatchFlags>(Qt::MatchExactly | Qt::MatchCaseSensitive) is the function used to get index of the string value in QComboBox.

-> Implementing an Alarm functionality in Qt
// alarm.h
#include <QThread>
#include <QDateTime>
#include <QTime>
#include <QMutex>
#include <QDebug>

class Alarm : public QThread
{
    Q_OBJECT
public:
    Alarm(QTime time, QObject *parent = 0);
    void setEndTime(QTime time);
public slots:
    void stopAlarm();
signals:
    void alarmRing();
protected:
    void run();
private:
    QTime endTime;
    bool stopAlarmFlag;
    QMutex mutex;
};




 // alarm.cpp

#include "alarm.h"

Alarm::Alarm(QTime time, QObject *parent) : QThread(parent)
{
    endTime = time;
    stopAlarmFlag = false;
}

void Alarm::setEndTime(QTime time)
{
    mutex.lock();
    endTime = time;
    mutex.unlock();
}

void Alarm::run()
{
    while(1)
    {
        qDebug()<<"Run";
        if(QTime::currentTime()>=endTime)
        {
            emit alarmRing();
            return;
        }
        sleep(1);
    }

}

void Alarm::stopAlarm()
{
    this->exit();
}




-> Implementing a drag drop functionality between Qt widgets
   When the drag starts in a Qt widget, a new drag is created where mime data is fed into the event. The drop event in another widget reads these mime data. The code below show how to create a drag event with some mime data - 

In the views mouse event create a new drag object to contain the data you want moved,
QDrag* drag = new QDrag( this );
QByteArray ba;
QDataStream* data = new QDataStream(&ba, QIODevice::WriteOnly);
*data << m_slideIndex;
QMimeData* myMimeData = new QMimeData;
    myMimeData->setData("application/x-thumbnaildatastream", ba);
drag->setMimeData( myMimeData );
drag->setPixmap( thumb );
drag->setHotSpot( thumb.rect().center() );
if ( drag->exec() == Qt::IgnoreAction )
{
    qDebug() << "DRAG CANCELLED";
    m_dragging = false;
}
drag->deleteLater();
delete data;


-> Implementing a drag drop functionality between Qt widgets

  A simple display message
   QMessageBox msgBox;
   msgBox.setText("The document has been modified.");
   msgBox.exec();

  A document modified message - 
  QMessageBox msgBox;
  msgBox.setText("The document has been modified.");
  msgBox.setInformativeText("Do you want to save your changes?");
  msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
  msgBox.setDefaultButton(QMessageBox::Save);
  int ret = msgBox.exec();
  switch (ret) 
  {
   case QMessageBox::Save:
       // Save was clicked
       break;
   case QMessageBox::Discard:
       // Don't Save was clicked
       break;
   case QMessageBox::Cancel:
       // Cancel was clicked
       break;
   default:
       // should never be reached
       break;
  }

  A Warning message box -
int ret = QMessageBox::warning(this, tr("My Application"),
                                tr("The document has been modified.\n"
                                   "Do you want to save your changes?"),
                                QMessageBox::Save | QMessageBox::Discard
                                | QMessageBox::Cancel,
                                QMessageBox::Save);

  An error message box -
  QErrorMessage errorMessage;
  errorMessage.showMessage("Same resource can't be imported twice");
  errorMessage.exec();
  return;

Adding Signals and Slots to QGraphicsItem

Signals and slots are used for communication between objects. The signals and slots mechanism is a central feature of Qt and probably the part that differs most from the features provided by other frameworks. A signal is emitted when a particular event occurs. Qt's widgets have many predefined signals, but we can always subclass widgets to add our own signals to them. A slot is a function that is called in response to a particular signal. 


The QGraphicsItem class is the base class for all graphical items in a QGraphicsSceneIt provides a light-weight foundation for writing your own custom items. This includes defining the item's geometry, collision detection, its painting implementation and item interaction through its event handlers.


When we want to create a graphics item of our own, we can inherit from QGraphicsItem. QGraphicsItem is an abstract class. So two functions namely, 

virtual QRectF boundingRect() const;
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

These two functions have to be defined in the class which inherits from QGraphicsItem. But QGraphicsItem is not inherited from QObject, so we can't add signals or slots to this. So we need to inherit our class from QObject as well to access the signal slot functionality. QGraphicsObject is a predefined class which does the same thing but it is relatively slow as it has lot of other signals which you might not require. So, I prefer going for my own class which derives from both QGraphicsItem and QObject. The class below is a sample class to do so. 
 
class MyGraphicsObject : public QGraphicsItem, public QObject
{
     Q_OBJECT
     Q_INTERFACES(QGraphicsItem)
public:
     MyGraphicsObject(QGraphicsItem *parent = 0);

private:
     virtual QRectF boundingRect() const;
     virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

}; 



After defining this class, you can add signals/slots of your choices.