Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) QtExample35

 

QtQt CreatorLubuntu

 

Qt example 35: arrows with an angle is a Qt example elaborates on Qt example 34: moveable, selectable and editable arrows, but has arrows with an angle in them instead.

Technical facts

 

Application type(s)

Operating system(s) or programming environment(s)

IDE(s):

Project type:

C++ standard:

Compiler(s):

Libraries used:

 

 

 

 

 

Qt project file: ./CppQtExample35/CppQtExample35.pro

 

exists(../../DesktopApplication.pri) {
  include(../../DesktopApplication.pri)
}
!exists(../../DesktopApplication.pri) {
  QT += core gui
  greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  TEMPLATE = app

  CONFIG(debug, debug|release) {
    message(Debug mode)
  }

  CONFIG(release, debug|release) {
    message(Release mode)
    DEFINES += NDEBUG NTRACE_BILDERBIKKEL
  }

  QMAKE_CXXFLAGS += -std=c++11 -Wall -Wextra

  unix {
    QMAKE_CXXFLAGS += -Werror
  }
}

exists(../../Libraries/Boost.pri) {
  include(../../Libraries/Boost.pri)
}
!exists(../../Libraries/Boost.pri) {
  win32 {
    INCLUDEPATH += \
      ../../../Projects/Libraries/boost_1_55_0
  }
}

SOURCES += \
    qtmain.cpp \
    qtarrowswidget.cpp \
    qtarrowitem.cpp

HEADERS += \
    qtarrowswidget.h \
    qtarrowitem.h

 

 

 

 

 

./CppQtExample35/qtarrowitem.h

 

#ifndef QTARROWITEM_H
#define QTARROWITEM_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <QGraphicsLineItem>
#pragma GCC diagnostic pop

struct QtArrowItem : public QGraphicsItem
{
  QtArrowItem(
    const double tail_x,
    const double tail_y,
    const bool tail,
    const double mid_x,
    const double mid_y,
    const bool head,
    const double head_x,
    const double head_y,
    QGraphicsItem *parent = 0);

  ///Respond to key presses
  void keyPressEvent(QKeyEvent *event);

  protected:
  ///The rectangle that containg the item, used for rough calculations like
  ///collision detection
  QRectF boundingRect() const;

  ///Respond to mouse press
  void mousePressEvent(QGraphicsSceneMouseEvent *event);

  ///Paint a QtTextPositionItem
  void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *);

  ///More precise shape compared to boundingRect
  ///In this example, it is redefined to ease selecting those thin lines
  QPainterPath shape() const;


  private:
  ///The extra width given to the line for easier clicking
  static const double m_click_easy_width;

  ///Show arrow at head
  bool m_head;

  double m_head_x;
  double m_head_y;

  double m_mid_x;
  double m_mid_y;

  ///Show arrow at tail
  bool m_tail;

  double m_tail_x;
  double m_tail_y;

  ///Obtain the angle in radians between two deltas
  ///12 o'clock is 0.0 * pi
  /// 3 o'clock is 0.5 * pi
  /// 6 o'clock is 1.0 * pi
  /// 9 o'clock is 1.5 * pi
  //From www.richelbilderbeek.nl/CppGetAngle.htm
  static double GetAngle(const double dx, const double dy);

};

#endif // QTARROWITEM_H

 

 

 

 

 

./CppQtExample35/qtarrowitem.cpp

 

#include <cassert>
#include <cmath>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/math/constants/constants.hpp>
#include <QGraphicsSceneMouseEvent>
#include <QKeyEvent>
#include <QPainter>
#include "qtarrowitem.h"
#pragma GCC diagnostic pop

const double QtArrowItem::m_click_easy_width = 10.0;

QtArrowItem::QtArrowItem(
  const double tail_x,
  const double tail_y,
  const bool tail,
  const double mid_x,
  const double mid_y,
  const bool head,
  const double head_x,
  const double head_y,
  QGraphicsItem *parent)
  : QGraphicsItem(parent),
    m_head(head),
    m_head_x(head_x),
    m_head_y(head_y),
    m_mid_x(mid_x),
    m_mid_y(mid_y),
    m_tail(tail),
    m_tail_x(tail_x),
    m_tail_y(tail_y)
{
  this->setFlag(QGraphicsItem::ItemIsMovable);
  this->setFlag(QGraphicsItem::ItemIsSelectable);
}

double QtArrowItem::GetAngle(const double dx, const double dy)
{
  return boost::math::constants::pi<double>() - (std::atan(dx/dy));
}

QRectF QtArrowItem::boundingRect() const
{
  return shape().boundingRect();
}

void QtArrowItem::keyPressEvent(QKeyEvent *event)
{
  switch (event->key())
  {
    case Qt::Key_F1:
    case Qt::Key_1:
    case Qt::Key_T:
    case Qt::Key_Minus:
      m_tail = !m_tail;
      this->update();
      break;
    case Qt::Key_F2:
    case Qt::Key_2:
    case Qt::Key_H:
    case Qt::Key_Plus:
      m_head = !m_head;
      this->update();
      break;
    default:
      break;
  }
}

void QtArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
  if (event->modifiers() & Qt::ShiftModifier)
  {
    if ((event->pos() - QPointF(m_tail_x,m_tail_y)).manhattanLength() < 10.0)
    {
      m_tail = !m_tail;
      this->update();
    }
    else if ((event->pos() - QPointF(m_head_x,m_head_y)).manhattanLength() < 10.0)
    {
      m_head = !m_head;
      this->update();
    }
  }
}

void QtArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
  painter->setRenderHint(QPainter::Antialiasing);
  if (this->isSelected())
  {
    const QColor color(255,0,0);
    QPen pen;
    pen.setColor(color);
    pen.setWidth(3);
    painter->setPen(pen);
    QBrush brush;
    brush.setColor(color);
    brush.setStyle(Qt::SolidPattern);
    painter->setBrush(brush);
  }
  else
  {
    const QColor color(0,0,0);
    QPen pen;
    pen.setColor(color);
    pen.setWidth(1);
    painter->setPen(pen);
    QBrush brush;
    brush.setColor(color);
    brush.setStyle(Qt::SolidPattern);
    painter->setBrush(brush);
  }
  painter->drawLine(
    m_tail_x,m_tail_y,
    m_mid_x ,m_mid_y);
  painter->drawLine(
    m_mid_x,m_mid_y,
    m_head_x,m_head_y);

  const double sz = 10.0; //pixels
  if (m_tail)
  {
    const double dx = m_mid_x - m_tail_x;
    const double dy = m_mid_y - m_tail_y;
    double angle = GetAngle(dx,dy);
    if (dy >= 0.0) angle = (1.0 * boost::math::constants::pi<double>()) + angle;
    const QPointF p0(m_tail_x,m_tail_y);
    const QPointF p1
      = p0 + QPointF(
         std::sin(angle + boost::math::constants::pi<double>() + (boost::math::constants::pi<double>() * 0.1)) * sz,
        -std::cos(angle + boost::math::constants::pi<double>() + (boost::math::constants::pi<double>() * 0.1)) * sz);
    const QPointF p2
      = p0 + QPointF(
         std::sin(angle + boost::math::constants::pi<double>() - (boost::math::constants::pi<double>() * 0.1)) * sz,
        -std::cos(angle + boost::math::constants::pi<double>() - (boost::math::constants::pi<double>() * 0.1)) * sz);
    painter->drawPolygon(QPolygonF() << p0 << p1 << p2);
  }
  if (m_head)
  {
    const double dx = m_head_x - m_mid_x;
    const double dy = m_head_y - m_mid_y;
    double angle = GetAngle(dx,dy);
    if (dy >= 0.0) angle = (1.0 * boost::math::constants::pi<double>()) + angle;
    const QPointF p0(m_head_x,m_head_y);
    const QPointF p1
      = p0 + QPointF(
         std::sin(angle +  0.0 + (boost::math::constants::pi<double>() * 0.1)) * sz,
        -std::cos(angle +  0.0 + (boost::math::constants::pi<double>() * 0.1)) * sz);
    const QPointF p2
      = p0 + QPointF(
         std::sin(angle +  0.0 - (boost::math::constants::pi<double>() * 0.1)) * sz,
        -std::cos(angle +  0.0 - (boost::math::constants::pi<double>() * 0.1)) * sz);

    painter->drawPolygon(QPolygonF() << p0 << p1 << p2);
  }
}

QPainterPath QtArrowItem::shape() const
{
  //Thanks to norobro for posting this code at
  //http://www.qtcentre.org/threads/49201-Increase-margin-for-detecting-tooltip-events-of-QGraphicsLineItem
  QPainterPath path;
  QPainterPathStroker stroker;
  path.moveTo(m_tail_x,m_tail_y);
  path.lineTo(m_mid_x,m_mid_y);
  path.lineTo(m_head_x,m_head_y);
  stroker.setWidth(m_click_easy_width);
  return stroker.createStroke(path);
}

 

 

 

 

 

./CppQtExample35/qtarrowswidget.h

 

#ifndef QTARROWSWIDGET_H
#define QTARROWSWIDGET_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <QGraphicsView>
#pragma GCC diagnostic pop

struct QtArrowsWidget : public QGraphicsView
{
  QtArrowsWidget();

  protected:
  ///Respond to a key press
  void keyPressEvent(QKeyEvent *event);

  private:
  ///The QGraphicsScene working on
  QGraphicsScene * const m_scene;
};

#endif // QTARROWSWIDGET_H

 

 

 

 

 

./CppQtExample35/qtarrowswidget.cpp

 

#include <cassert>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/math/constants/constants.hpp>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include "qtarrowitem.h"
#include "qtarrowswidget.h"
#pragma GCC diagnostic pop

QtArrowsWidget::QtArrowsWidget()
  : m_scene(new QGraphicsScene(this))
{

  this->setScene(m_scene);
  const int n_arrows = 16;
  for (int i=0; i!=n_arrows; ++i)
  {
    const double angle = 2.0 * boost::math::constants::pi<double>() * (static_cast<double>(i) / static_cast<double>(n_arrows));
    const double d_angle = (2.0 * boost::math::constants::pi<double>()) / 32.0;
    const double tail_x =  std::sin(angle) * 100.0;
    const double tail_y = -std::cos(angle) * 100.0;
    const bool tail = (std::rand() >> 4) % 2;

    const double mid_x =  std::sin(angle + d_angle) * 150.0;
    const double mid_y = -std::cos(angle + d_angle) * 150.0;

    const double head_x =  std::sin(angle) * 200.0;
    const double head_y = -std::cos(angle) * 200.0;
    const bool head = (std::rand() >> 4) % 2;

    QtArrowItem * item = new QtArrowItem(tail_x,tail_y,tail,mid_x,mid_y,head,head_x,head_y);
    m_scene->addItem(item);
  }
}

void QtArrowsWidget::keyPressEvent(QKeyEvent *event)
{
  const QList<QGraphicsItem *> v = m_scene->selectedItems();
  std::for_each(v.begin(),v.end(),
    [event](QGraphicsItem * item)
    {
      if (QtArrowItem * const arrow = dynamic_cast<QtArrowItem*>(item))
      {
        arrow->keyPressEvent(event);
      }
    }
  );
}

 

 

 

 

 

./CppQtExample35/qtmain.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <QApplication>
#include <QDesktopWidget>
#include "qtarrowswidget.h"
#pragma GCC diagnostic pop

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  QtArrowsWidget w;
  {
    //Resize the dialog and put it in the screen center
    w.setGeometry(0,0,600,400);
    const QRect screen = QApplication::desktop()->screenGeometry();
    w.move( screen.center() - w.rect().center() );
  }
  w.show();
  return a.exec();
}

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict

This page has been created by the tool CodeToHtml