Go back to Richel Bilderbeek's homepage.

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

 

 

 

 

 

(C++) QtPathArrowItem

 

QtQt CreatorLubuntu

 

QtPathArrowItem is a Qt QGraphicsItem class that displays an arrow that can have a multiple straight-line body.

Technical facts

 

 

 

 

 

 

./CppQtPathArrowItem/CppQtPathArrowItem.pri

 

INCLUDEPATH += \
    ../../Classes/CppQtPathArrowItem

SOURCES += \
    ../../Classes/CppQtPathArrowItem/qtpatharrowitem.cpp

HEADERS  += \
    ../../Classes/CppQtPathArrowItem/qtpatharrowitem.h

OTHER_FILES += \
    ../../Classes/CppQtPathArrowItem/Licence.txt

 

 

 

 

 

./CppQtPathArrowItem/qtpatharrowitem.h

 

//---------------------------------------------------------------------------
/*
QtPathArrowItem, an arrow QGraphicsItem with one or more midpoints
Copyright (C) 2012-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtPathArrowItem.htm
//---------------------------------------------------------------------------
#ifndef QTPATHARROWITEM_H
#define QTPATHARROWITEM_H

#include <string>
#include <vector>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/signals2.hpp>
#include <QGraphicsLineItem>
#include <QPen>
#pragma GCC diagnostic pop

namespace ribi {

///An arrow item that has one or more midpoints
struct QtPathArrowItem : public QGraphicsItem
{
  typedef QtPathArrowItem This;

  QtPathArrowItem(
    const QPointF& tail_pos,
    const bool tail,
    const std::vector<QPointF>& mid_pos,
    const bool head,
    const QPointF& head_pos,
    QGraphicsItem *parent = 0);

  virtual ~QtPathArrowItem() noexcept {}

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Is there an arrow at the 'to' point (x2,y2)?
  bool HasHead() const noexcept { return m_head; }

  ///Is there an arrow at the 'from' point (x1,y1)?
  bool HasTail() const noexcept { return m_tail; }

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

  ///Set the path the arrow has to take
  void SetArrowPath(
    const QPointF& tail_pos,
    const std::vector<QPointF>& mid_pos,
    const QPointF& head_pos
  );

  ///Set the pen used to show focus
  void SetFocusPen(const QPen& pen) noexcept { m_focus_pen = pen; }

  ///Set the regular pen used to draw the arrow
  void SetPen(const QPen& pen) noexcept { m_pen = pen; }

  ///Set if the arrow has a point at the head
  void SetHasHead(const bool has_head) noexcept { m_head = has_head; }

  ///Set if the arrow has a point at the tail
  void SetHasTail(const bool has_tail) noexcept { m_tail = has_tail; }

  ///Signal to request a scene update, because this item has moved/changed
  boost::signals2::signal<void (This*)> m_signal_item_requests_scene_update;

protected:
  QRectF boundingRect() const;
  void hoverEnterEvent(QGraphicsSceneHoverEvent *);
  virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
  virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
  void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *);
  QPainterPath shape() const;

private:

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

  ///The arrow used for indicating focus
  QPen m_focus_pen;

  ///Show arrow at head
  bool m_head;

  ///The position of the head
  QPointF m_head_pos;

  ///The positions between tail (lower indices) and tail (higher indices)
  std::vector<QPointF> m_mid_pos;

  ///The regular pen
  QPen m_pen;

  ///Show arrow at tail
  bool m_tail;

  ///The position of the head
  QPointF m_tail_pos;

  ///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);

};

} //~namespace ribi

#endif // QTPATHARROWITEM_H

 

 

 

 

 

./CppQtPathArrowItem/qtpatharrowitem.cpp

 

//---------------------------------------------------------------------------
/*
QtPathArrowItem, an arrow QGraphicsItem with one or more midpoints
Copyright (C) 2012-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtPathArrowItem.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "qtpatharrowitem.h"

#include <cassert>
#include <cmath>

#include <boost/math/constants/constants.hpp>

#include <QCursor>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QKeyEvent>
#include <QPainter>

#include "geometry.h"
#pragma GCC diagnostic pop

const double ribi::QtPathArrowItem::m_click_easy_width = 10.0;

ribi::QtPathArrowItem::QtPathArrowItem(
  const QPointF& tail_pos,
  const bool tail,
  const std::vector<QPointF>& mid_pos,
  const bool head,
  const QPointF& head_pos,
  QGraphicsItem *parent)
  : QGraphicsItem(parent),       //New since Qt5
    m_signal_item_requests_scene_update{},
    m_focus_pen(QPen(Qt::DashLine)),
    m_head(head),
    m_head_pos(head_pos),
    m_mid_pos(mid_pos),
    m_pen(QPen(QColor(0,0,0))),
    m_tail(tail),
    m_tail_pos(tail_pos)
{
  assert(m_mid_pos.size() >= 1
    && "There must be at least one midpos");
  this->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );

  //Allow mouse tracking
  this->setAcceptHoverEvents(true);
}

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

std::string ribi::QtPathArrowItem::GetVersion() noexcept
{
  return "1.0";
}

std::vector<std::string> ribi::QtPathArrowItem::GetVersionHistory() noexcept
{
  std::vector<std::string> v;
  v.push_back("2012-12-01: version 1.0: initial version");
  return v;
}

void ribi::QtPathArrowItem::hoverEnterEvent(QGraphicsSceneHoverEvent *)
{
  this->setCursor(QCursor(Qt::PointingHandCursor));
}

void ribi::QtPathArrowItem::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;
  }
  QGraphicsItem::keyPressEvent(event);
  m_signal_item_requests_scene_update(this);

}

void ribi::QtPathArrowItem::mouseMoveEvent(QGraphicsSceneMouseEvent*)
{
  m_signal_item_requests_scene_update(this);
  //QGraphicsLineItem::mouseMoveEvent(event);
}

void ribi::QtPathArrowItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
  if (event->modifiers() & Qt::ShiftModifier)
  {
    if ((event->pos() - m_tail_pos).manhattanLength() < 10.0)
    {
      m_tail = !m_tail;
      this->update();
    }
    else if ((event->pos() - m_head_pos).manhattanLength() < 10.0)
    {
      m_head = !m_head;
      this->update();
    }
  }
  QGraphicsItem::mousePressEvent(event);
}

void ribi::QtPathArrowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
  painter->setRenderHint(QPainter::Antialiasing);
  if (this->isSelected() || this->hasFocus())
  {
    painter->setPen(m_focus_pen);
  }
  else
  {
    painter->setPen(m_pen);
  }
  {
    //Draw the lines
    painter->drawLine(m_tail_pos,m_mid_pos[0]);
    const std::size_t max = m_mid_pos.size() - 1; //-1, because the line goes to the next index
    for (std::size_t i = 0; i!= max; ++i)
    {
      painter->drawLine(m_mid_pos[i],m_mid_pos[i+1]);
    }
    painter->drawLine(m_mid_pos[ m_mid_pos.size() - 1 ],m_head_pos);
  }


  const double sz = 10.0; //pixels
  if (m_tail)
  {
    const double pi{boost::math::constants::pi<double>()};
    const double dx{m_mid_pos[0].x() - m_tail_pos.x()};
    const double dy{m_mid_pos[0].y() - m_tail_pos.y()};
    double angle{Geometry().GetAngleClockScreen(dx,dy)};
    if (dy >= 0.0) angle = (1.0 * pi) + angle;
    //const QPointF m_tail_pos(m_tail_x,m_tail_y);
    const QPointF p1{
      m_tail_pos + QPointF(
         std::sin(angle + pi + (pi * 0.1)) * sz,
        -std::cos(angle + pi + (pi * 0.1)) * sz)
    };
    const QPointF p2{
      m_tail_pos + QPointF(
         std::sin(angle + pi - (pi * 0.1)) * sz,
        -std::cos(angle + pi - (pi * 0.1)) * sz)
    };
    painter->drawPolygon(QPolygonF() << m_tail_pos << p1 << p2);
  }
  if (m_head)
  {
    const double pi = boost::math::constants::pi<double>();
    const double dx = m_head_pos.x() - m_mid_pos[m_mid_pos.size() - 1].x();
    const double dy = m_head_pos.y() - m_mid_pos[m_mid_pos.size() - 1].y();
    double angle{Geometry().GetAngleClockScreen(dx,dy)};
    if (dy >= 0.0) angle = (1.0 * pi) + angle;
    const QPointF p1
      = m_head_pos + QPointF(
         std::sin(angle +  0.0 + (pi * 0.1)) * sz,
        -std::cos(angle +  0.0 + (pi * 0.1)) * sz);
    const QPointF p2
      = m_head_pos + QPointF(
         std::sin(angle +  0.0 - (pi * 0.1)) * sz,
        -std::cos(angle +  0.0 - (pi * 0.1)) * sz);

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

void ribi::QtPathArrowItem::SetArrowPath(
  const QPointF& tail_pos,
  const std::vector<QPointF>& mid_pos,
  const QPointF& head_pos)
{
  m_tail_pos = tail_pos;
  m_mid_pos = mid_pos;
  m_head_pos = head_pos;
  assert(m_mid_pos.size() >= 1 && "There must be at least one midpos");
  this->update();
}

QPainterPath ribi::QtPathArrowItem::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_pos);
  const std::size_t sz = m_mid_pos.size();
  for (std::size_t i = 0; i!= sz; ++i)
  {
    path.lineTo(m_mid_pos[i]);
  }
  path.lineTo(m_head_pos);

  stroker.setWidth(m_click_easy_width);
  return stroker.createStroke(path);
}

 

 

 

 

 

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