    <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:content="http://purl.org/rss/1.0/modules/content/">
     <channel>
        <title>ACCU  :: Writing Custom Widgets in Qt</title>
        <link>https://members.accu.org/index.php/journals/714</link>
        <description>Professionalism in Programming</description>
        <dc:language>en-us</dc:language> 
        <dc:creator>Administrator</dc:creator> 
        <admin:generatorAgent rdf:resource="http://www.xaraya.org" /> 
        <admin:errorReportsTo rdf:resource="mailto:webeditor@accu.org" />
       <sy:updatePeriod>hourly</sy:updatePeriod>
       <sy:updateFrequency>1</sy:updateFrequency>
       <docs>http://backend.userland.com/rss</docs>


        <h2>Journal Articles</h2>


<div class="xar-mod-head"><span class="xar-mod-title">CVu Journal Vol 16, #6 - Dec 2004 + Programming Topics</span></div>

<table border="0" cellpadding="1" cellspacing="0">
    <tbody>
    <tr>
        <td valign="top">
            Browse in :
       </td>
       <td valign="top">

                                            <a href="https://members.accu.org/index.php/journals/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c76/">Journals</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c77/">CVu</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c99/">166</a>
                    (12)
<br />

                                            <a href="https://members.accu.org/index.php/journals/">All</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c13/">Topics</a>

                     &gt;                         <a href="https://members.accu.org/index.php/journals/c65/">Programming</a>
                    (877)
<br />

                                            <a href="https://members.accu.org/index.php/journals/c99-65/">Any of these categories</a>

                    -                        <a href="https://members.accu.org/index.php/journals/c99+65/">All of these categories</a>
<br />
</td>
   </tr>
   </tbody>
</table>




<div class="xar-error">
   <p>
 <strong>Note:</strong> when you create a new publication type,
the articles module will automatically use the templates
<em>user-display-[publicationtype].xt</em>
and <em>user-summary-[publicationtype].xt</em>.
If those templates do not exist when you try to preview or display a new article,
you'll get this warning :-)  Please place your own templates in themes/<em>yourtheme</em>/modules/articles . The templates will get the extension .xt there. </p>
</div>
<div class="xar-norm xar-standard-box-padding">
   <h1><strong>Title:</strong>&nbsp;Writing Custom Widgets in Qt</h1>
<p><strong>Author:</strong>&nbsp;</p>
<p>
<strong>Date:</strong> 03 December 2004 13:16:10 +00:00 or Fri, 03 December 2004 13:16:10 +00:00</p>
<p><strong>Summary:</strong>&nbsp;<p>In the fourth installment of our series on cross-platform GUI programming with the Qt C++ toolkit, we are going to write a custom widget using Qt. The widget in question is a &quot;scribble&quot; widget (see Figure 1) - that is, the drawing area of a simple paint program</p></p>
<p><strong>Body:</strong>&nbsp;<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e18" id="d0e18"></a></h2>
</div>
<p>In the fourth installment of our series on cross-platform GUI
programming with the Qt C++ toolkit, we are going to write a custom
widget using Qt. The widget in question is a &quot;scribble&quot; widget (see
Figure 1) - that is, the drawing area of a simple paint program.
The user can draw by moving the mouse pointer while holding down
the left mouse button.</p>
<div class="figure"><a name="d0e22" id="d0e22"></a>
<div class="mediaobject c2"><img src=
"/var/uploads/journals/resources/blanchette-fig1.png" align="middle" alt=
"The Scribble Widget"></div>
<p class="title c3">Figure 1. The Scribble Widget</p>
</div>
<p>Writing a custom widget using Qt isn't much different from
writing an application's main window (C Vu Volume 16 No 3) or a
dialog (C Vu Volume 16 No 4). It also involves deriving from a Qt
base class, reimplementing some virtual functions, and connecting
signals to slots. The main difference is that we also need to
handle low-level events (also called &quot;messages&quot;) such as paint
events and mouse events to give the widget its look and feel.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e30" id="d0e30"></a>The Scribble
Class Definition</h2>
</div>
<p>We'll start by looking at the definition of the Scribble
class:</p>
<pre class="programlisting">
#ifndef SCRIBBLE_H
#define SCRIBBLE_H
#include &lt;qimage.h&gt;
#include &lt;qwidget.h&gt;

class Scribble : public QWidget {
public:
  Scribble(QWidget *parent = 0);
  QSize sizeHint() const;
  void setPixmap(const QPixmap &amp;pixmap);
  QPixmap pixmap() const { return m_pixmap; }
  void setPenColor(const QColor &amp;color);
  QColor penColor() const { return m_color; }
protected:
  void mousePressEvent(QMouseEvent *event);
  void mouseMoveEvent(QMouseEvent *event);
  void paintEvent(QPaintEvent *event);
private:
  QColor m_color;
  QPixmap m_pixmap;
  QPoint m_prevPos;
};
#endif
</pre>
<p>The <tt class="classname">Scribble</tt> class inherits from
<tt class="classname">QWidget</tt>, the base class for all widgets
and windows. <tt class="classname">Scribble</tt> provides public
access functions, three protected event handles, and some private
variables.</p>
<p>The <tt class="varname">m_color</tt> data member holds the
current pen colour. The <tt class="varname">m_pixmap</tt> member
holds the image that the user is drawing. The <tt class=
"varname">m_prevPos</tt> member will be explained later; just
ignore it for the moment.</p>
<p>The protected event handles are virtual functions inherited from
<tt class="classname">QWidget</tt> that are called whenever the
widget receives an event. Events are sent by the window system
whenever some condition occurs. For example, if the user presses a
key while the widget has the keyboard focus, the window system
dispatches a &quot;key press&quot; event that the widget can handle by
reimplementing <tt class=
"methodname">QWidget::keyPressEvent()</tt>. The <tt class=
"classname">Scribble</tt> widget is interested in three kinds of
event: &quot;mouse press&quot;, &quot;mouse move&quot; and &quot;paint&quot; events.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e70" id="d0e70"></a>The Scribble
Class Implementation</h2>
</div>
<p>We will now go through the implementation of the <tt class=
"classname">Scribble</tt> class, starting with the constructor:</p>
<pre class="programlisting">
Scribble::Scribble(QWidget *parent)
    : QWidget(parent) {
  m_color = black;
  m_pixmap.resize(480, 320);
  m_pixmap.fill(0xFFFFFF);
  setWFlags(WStaticContents);
}
</pre>
<p>The constructor takes a parent widget and passes it on to the
base class constructor. If parent is a null pointer, the widget is
a window in its own right; otherwise the widget is displayed within
the parent's area.</p>
<p>In the constructor body we initialize the <tt class=
"varname">m_color</tt> and <tt class="varname">m_pixmap</tt> data
members to default values. The pen colour is set to black; the
pixmap is initialized to size 480 &times; 320 and filled with white
(0xFFFFFF). Finally we set the <tt class=
"varname">WStaticContents</tt> flag on the widget, telling Qt that
the widget's content doesn't scale when the widget is resized, but
rather it stays rooted in the top-left corner. This simple trick
lets Qt optimize drawing and reduce flicker drastically.</p>
<pre class="programlisting">
QSize Scribble::sizeHint() const {
  return m_pixmap.size();
}
</pre>
<p>The <tt class="methodname">sizeHint()</tt> function is
reimplemented from <tt class="classname">QWidget</tt>. It should
return the ideal size of a widget. Layout managers take this into
account when assigning screen positions to widgets. Here we return
the size of the pixmap (480 &times; 320 by default) as the ideal
size for the widget.</p>
<pre class="programlisting">
void Scribble::setPixmap(const QPixmap
                                    &amp;pixmap) {
  m_pixmap = pixmap;
  update();
  updateGeometry();
}
</pre>
<p>The <tt class="methodname">setPixmap()</tt> function sets the
pixmap which the user can draw on. Notice that we call <tt class=
"methodname">update()</tt> and <tt class=
"methodname">updateGeometry()</tt> in addition to assigning the new
pixmap to <tt class="varname">m_pixmap</tt>. The call to <tt class=
"methodname">update()</tt> tells Qt to repaint the widget, ensuring
that the new pixmap is shown straight away. The call to <tt class=
"methodname">updateGeometry()</tt> tells the layout manager
responsible for this widget (if any) that the <tt class=
"methodname">sizeHint()</tt> might have changed.</p>
<pre class="programlisting">
void Scribble::setPenColor(const QColor
                                     &amp;color) {
  m_color = color;
}
</pre>
<p>The <tt class="methodname">setPenColor()</tt> function sets the
current pen colour. This time we don't need to call <tt class=
"methodname">update()</tt> because the operation doesn't affect the
screen rendering of the widget (it only affects pixels that the
user will draw in the future). We don't need to call <tt class=
"methodname">updateGeometry()</tt> either because <tt class=
"varname">m_color</tt> isn't used when computing the size hint.</p>
<pre class="programlisting">
void Scribble::mousePressEvent(QMouseEvent
                                     *event) {
  if(event-&gt;button() == LeftButton)
    m_prevPos = event-&gt;pos();
}
</pre>
<p>The <tt class="methodname">mousePressEvent()</tt> function is
called whenever the user presses a mouse button while the mouse
pointer is located on the widget. The event parameter gives
additional information, such as the button that was pressed
(<tt class="methodname">button()</tt>) and the screen position of
the mouse cursor when the button was pressed (<tt class=
"methodname">pos()</tt>). If the user pressed the left button, we
store the mouse position in <tt class="varname">m_prevPos</tt> for
later use.</p>
<pre class="programlisting">
void Scribble::mouseMoveEvent(QMouseEvent
                                     *event) {
  if(event-&gt;state() &amp; LeftButton) {
    QPainter painter(&amp;m_pixmap);
    painter.setPen(QPen(m_color, 3));
    painter.drawLine(m_prevPos, event-&gt;pos());

    QRect rect(m_prevPos, event-&gt;pos());
    rect = rect.normalize();
    update(rect.x() - 1, rect.y() - 1,
           rect.width() + 2,
           rect.height() + 2);

    m_prevPos = event-&gt;pos();
  }
}
</pre>
<p>The <tt class="methodname">mouseMoveEvent()</tt> function is
called continuously when the user moves the mouse pointer while
holding down a mouse button. The typical sequence of events is one
&quot;mouse press&quot; event when the user presses a button, then a series
of &quot;mouse move&quot; events that describe the path taken by the mouse
pointer, and finally a &quot;mouse release&quot; event when the user releases
the button.</p>
<p>We check if the left button is one of the buttons that are
currently pressed. If this is the case we update <tt class=
"varname">m_pixmap</tt> and repaint the widget using <tt class=
"methodname">update()</tt>.</p>
<p>We create a <tt class="classname">QPainter</tt> to draw on the
pixmap. We set the pen to have the correct colour (<tt class=
"varname">m_color</tt>) and a thickness of 3 pixels. Then we draw a
line from the previous mouse position (<tt class=
"varname">m_prevPos</tt>) to the new mouse position (<tt class=
"methodname">event-&gt;pos()</tt>).</p>
<p><tt class="classname">QPainter</tt> is the entrance door to Qt's
paint engine. It provides functions to draw all sorts of geometric
shapes (rectangles, circles, pie sections, Bezier curves, etc.) and
supports transformations such as rotating and scaling. A <tt class=
"classname">QPainter</tt> object can be used to draw on a pixmap, a
widget, a vector diagram or a printer.</p>
<p>Once we're done updating the pixmap we must update the on-screen
version. The reductionist approach would be to call <tt class=
"methodname">update()</tt> with no argument and be done with it;
this would tell Qt to redraw the entire widget area, a somewhat
expensive operation. Instead we compute the bounding rectangle for
the line segment we just drew and pass it to <tt class=
"methodname">update()</tt>.</p>
<p>At the end of the function, we update <tt class=
"varname">m_prevPos</tt> so that the next &quot;mouse move&quot; event will
prolong the line segment we just drew.</p>
<pre class="programlisting">
void Scribble::paintEvent(QPaintEvent *event) {
  QPainter painter(this);
  painter.drawPixmap(0, 0, m_pixmap);
}
</pre>
<p>The <tt class="methodname">paintEvent()</tt> function is called
whenever the widget must be repainted. This can occur if the widget
was temporarily obscured by another window and then made visible
again, or as a result of calling <tt class=
"methodname">update()</tt>. Here we simply draw the pixmap onto the
widget.</p>
<p>At this point you might wonder why we bother drawing on a pixmap
then transfer the pixmap onto the widget. Couldn't we draw directly
on the widget instead, eliminating the need for <tt class=
"varname">m_pixmap</tt>? The answer is no. This is because we can't
rely on the window system to keep a copy of the widget's pixels if
the window is obscured or minimized. A well-behaved widget must
implement <tt class="methodname">paintEvent()</tt> and be able to
redraw itself entirely at any moment.</p>
</div>
<div class="sect1" lang="en">
<div class="titlepage">
<h2><a name="d0e227" id="d0e227"></a>The
Application's Main Window</h2>
</div>
<p>We are done implementing the custom widget. To make it useful,
we need a window around it, with a &quot;Pen Color...&quot; button and a
&quot;Quit&quot; button. Here's the class definition:</p>
<pre class="programlisting">
#ifndef WINDOW_H
#define WINDOW_H

#include &lt;qwidget.h&gt;
class Scribble;

class Window : public QWidget {
  Q_OBJECT
public:
  Window(QWidget *parent = 0);
private slots:
  void choosePenColor();
private:
  Scribble *m_scribble;
};

#endif
</pre>
<p>We can call the class <tt class="classname">Window</tt> because
it will be the only window in the application. The class has one
slot, <tt class="methodname">choosePenColor()</tt>, which pops up a
colour dialog.</p>
<pre class="programlisting">
Window::Window(QWidget *parent)
    : QWidget(parent) {
  m_scribble = new Scribble(this);
  m_scribble-&gt;setSizePolicy(
                QSizePolicy::Expanding,
                QSizePolicy::Expanding);

  QPushButton *penColorButton =
        new QPushButton(tr(&quot;Pen Color...&quot;),
                        this);
  QPushButton *quitButton =
        new QPushButton(tr(&quot;Quit&quot;), this);

  connect(penColorButton, SIGNAL(clicked()),
          this, SLOT(choosePenColor()));
  connect(quitButton, SIGNAL(clicked()),
          this, SLOT(close()));

  QGridLayout *layout = new QGridLayout(this);
  layout-&gt;setMargin(10);
  layout-&gt;setSpacing(5);

  layout-&gt;addMultiCellWidget(m_scribble, 0, 2,
                             0, 0);
  layout-&gt;addWidget(penColorButton, 0, 1);
  layout-&gt;addWidget(quitButton, 1, 1);

  setCaption(tr(&quot;Scribble&quot;));
}
</pre>
<p>In the constructor we create three child widgets (the scribble
area and two push buttons), connect the &quot;Pen Color...&quot; button to
the <tt class="methodname">choosePenColor()</tt> slot, connect the
&quot;Quit&quot; button to the window's <tt class="methodname">close()</tt>
slot, and put the child widgets in a grid layout. Figure 2 shows
how the child widgets are laid out in the grid cells.</p>
<div class="figure"><a name="d0e252" id="d0e252"></a>
<div class="mediaobject c2"><img src=
"/var/uploads/journals/resources/blanchette-fig2.png" align="middle" alt=
"Grid Layout of Child Widgets"></div>
<p class="title c3">Figure 2. Grid Layout of Child Widgets</p>
</div>
<pre class="programlisting">
void Window::choosePenColor() {
  QColor color =
       QColorDialog::getColor(
                m_scribble-&gt;penColor(), this);
  if(color.isValid())
    m_scribble-&gt;setPenColor(color);
}
</pre>
<p>When the user clicks &quot;Pen Color...&quot;, we pop up a <tt class=
"classname">QColorDialog</tt> that allows the user to select a pen
colour. We pass the old pen colour to the dialog as the initial
value.</p>
<p>This is all the code we need in <tt class=
"classname">Window</tt>. To complete the application, we need a
<tt class="function">main()</tt> function:</p>
<pre class="programlisting">
int main(int argc,
         char *argv[]){
  QApplication
       app(argc, argv);
  Window win;
  app.setMainWidget(
                 &amp;win);
  win.show();
  return app.exec();
}
</pre>
<p>That's it! One of Qt's striking features is how easy it is to
create custom widgets. In fact all of Qt's built-in widgets (e.g.
<tt class="classname">QPushButton</tt> and <tt class=
"classname">QColorDialog</tt>) are implemented using the techniques
described in this article. While with other toolkits writing custom
widgets is considered an advanced topic, in Qt it is so easy that
it is taught straight away to beginners as an introduction to the
Qt way of thinking.</p>
<div class="figure"><a name="d0e283" id="d0e283"></a>
<div class="mediaobject c2"><img src=
"/var/uploads/journals/resources/blanchette-fig3.png" align="middle" alt=
"The Colour Dialog"></div>
<p class="title c3">Figure 3. The Colour Dialog</p>
</div>
</div>
</p>
<p><strong>Notes:</strong>&nbsp;</p>
<p><em>More fields may be available via dynamicdata ..</em></p>
</div>
</channel>
</rss>
