Skip to content

Latest commit

 

History

History
199 lines (116 loc) · 29.7 KB

CppQtExample34.md

File metadata and controls

199 lines (116 loc) · 29.7 KB

 

 

 

 

 

 

QtQt CreatorLubuntu

 

Qt example 34: moveable, selectable and editable arrows is a Qt example to create some arrows that are (easily) selectable, moveable and editable. Arrows can be edited by mouse press and a keyboard press: whil holding shift, click the end of an arrow to have an arrowhead added or removed. When having selected an arrow, press 1,F1,the minus sign of 't' to have the tail arrowhead added or removed. Or, when having selected an arrow, press 2,F2,the plus sign of 'h' to have the head arrowhead added or removed.

 

What I liked, is that it is possible to select multiple items while holding CTRL (which is standard GUI behavior) and edit these all at the same time using a key press!

Technical facts

 

Application type(s)

Operating system(s) or programming environment(s)

IDE(s):

Project type:

C++ standard:

Compiler(s):

Libraries used:

  • Qt Qt: version 5.4.1 (32 bit)
  • STL STL: GNU ISO C++ Library, version 4.9.2

 

 

 

 

 

Qt project file: ./CppQtExample34/CppQtExample34.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

 

 

 

 

 

./CppQtExample34/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 QGraphicsLineItem {   QtArrowItem(     const double x1,     const double y1,     const bool tail,     const double x2,     const double y2,     const bool head,     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;   ///Show arrow at tail   bool m_tail;   ///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

 

 

 

 

 

./CppQtExample34/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 x1,   const double y1,   const bool tail,   const double x2,   const double y2,   const bool head,   QGraphicsItem *parent)   : QGraphicsLineItem(x1,y1,x2,y2,parent),     m_head(head),     m_tail(tail) {   this->setFlag(QGraphicsItem::ItemIsMovable);   this->setFlag(QGraphicsItem::ItemIsSelectable);   assert(this->line().p1() == QPointF(x1,y1));   assert(this->line().p2() == QPointF(x2,y2)); } 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() - this->line().p1()).manhattanLength() < 10.0)     {       m_tail = !m_tail;       this->update();     }     else if ((event->pos() - this->line().p2()).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(this->line());   //The angle from tail to head   double angle = GetAngle(line().dx(),line().dy());   if (line().dy() >= 0.0) angle = (1.0 * boost::math::constants::pi<double>()) + angle;   const double sz = 10.0; //pixels   if (m_tail)   {     const QPointF p0 = this->line().p1();     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 QPointF p0 = this->line().p2();     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(line().p1());   path.lineTo(line().p2());   stroker.setWidth(m_click_easy_width);   return stroker.createStroke(path); }

 

 

 

 

 

./CppQtExample34/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

 

 

 

 

 

./CppQtExample34/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 x1 =  std::sin(angle) * 100.0;     const double y1 = -std::cos(angle) * 100.0;     const bool tail = (std::rand() >> 4) % 2;     const double x2 =  std::sin(angle) * 200.0;     const double y2 = -std::cos(angle) * 200.0;     const bool head = (std::rand() >> 4) % 2;     QtArrowItem * item = new QtArrowItem(x1,y1,tail,x2,y2,head);     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);       }     }   ); }

 

 

 

 

 

./CppQtExample34/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(); }