(C++) QtExample35
February 24, 2017 · View on GitHub
(C++) QtExample35



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
Operating system(s) or programming environment(s)
Lubuntu 15.04 (vivid)
Qt Creator 3.1.1
- G++ 4.9.2
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(); }