杭州网站建设多少钱,wordpress全站采集,代理平台哪个好,北京网站开发品牌文章目录 QT图形视图系统介绍开始搭建MainWindow框架设置scene的属性缩放功能的添加加上标尺 QT图形视图系统
介绍
详细的介绍可以看QT的官方助手#xff0c;那里面介绍的详细且明白#xff0c;需要一定的英语基础#xff0c;我这里直接使用一个开源项目来介绍QGraphicsVi… 文章目录 QT图形视图系统介绍开始搭建MainWindow框架设置scene的属性缩放功能的添加加上标尺 QT图形视图系统
介绍
详细的介绍可以看QT的官方助手那里面介绍的详细且明白需要一定的英语基础我这里直接使用一个开源项目来介绍QGraphicsView、QGraphicsScene的使用。
先提供一个项目的图片 先来一个简单的例子这个例子是介绍了一下QGraphicsView 和 QGraphicsScene的关系并且如何在View中展示Scene
#include QApplication
#include QGraphicsScene
#include QGraphicsViewint main(int argc, char **argv)
{QApplication app(argc, argv);QGraphicsScene scene;scene.addText(Hello, QGraphicsView);QGraphicsView view(scene);view.show();return app.exec();
}上面的是最基本的QGraphicsView 中显示QGraphicsScene 并且打印Hello, QGraphicsView在界面上的例子。由此我们可以看到scene对象需要被view对象管理之后再显示出来。
接下来我们将重写QGraphicsView 来实现我们自己要的效果。
开始搭建MainWindow框架
使用mainwindowz作为整个项目的外部界面框架并且将自己的view放在mainwindow中
mainwindow 之后的代码我会将头文件代码和cpp代码放在一个代码块中请注意区分
// mainwindow.h
#ifndef GRAPHICSVIEWQ_MAINWINDOW_H
#define GRAPHICSVIEWQ_MAINWINDOW_H#include QMainWindow
class GraphicsView;
class MainWindow : public QMainWindow
{Q_OBJECT
public:explicit MainWindow(QWidget *parent 0);~MainWindow() override;protected:private:GraphicsView *graphics_view_;
};#endif //GRAPHICSVIEWQ_MAINWINDOW_H
// mainwindow.cpp#include QHBoxLayout
#include mainwindow.h
#include graphicsview.hMainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{setMouseTracking(true);resize(1600, 1000);graphics_view_ new GraphicsView(this);graphics_view_-setObjectName(QString::fromUtf8(graphicsView));graphics_view_-setTransformationAnchor(QGraphicsView::AnchorUnderMouse);graphics_view_-setResizeAnchor(QGraphicsView::AnchorUnderMouse);QWidget *centralWidget new QWidget(this);centralWidget-setObjectName(QString::fromUtf8(centralwidget));QHBoxLayout *horizontalLayout new QHBoxLayout(centralWidget);horizontalLayout-setSpacing(0);horizontalLayout-setObjectName(QString::fromUtf8(horizontalLayout));horizontalLayout-setContentsMargins(3, 3, 3, 3);horizontalLayout-addWidget(graphics_view_);setCentralWidget(centralWidget);QGraphicsScene *scene new QGraphicsScene();scene-addText(Hello, MainWindow);graphics_view_-setScene(scene);
}MainWindow::~MainWindow()
{}graphicsview
// graphicsview.h
#ifndef GRAPHICSVIEWQ_GRAPHICSVIEW_H
#define GRAPHICSVIEWQ_GRAPHICSVIEW_H
#include QGraphicsView
#include QWidgetclass GraphicsView : public QGraphicsView
{Q_OBJECT
public:explicit GraphicsView(QWidget *parent nullptr);explicit GraphicsView(QGraphicsScene *scene, QWidget *parent nullptr);~GraphicsView() override;protected:private:
};
#endif //GRAPHICSVIEWQ_GRAPHICSVIEW_H
// graphicsview.cpp
#include graphicsview.h
GraphicsView::GraphicsView(QWidget *parent): QGraphicsView(parent)
{}GraphicsView::GraphicsView(QGraphicsScene *scene, QWidget *parent): QGraphicsView(scene, parent)
{}GraphicsView::~GraphicsView()
{}这个时候我们展示mainwindow的时候是能正常看到 hello mainwindow的时候我们离我们的目标又进一步了。
设置scene的属性
接下来给我们的view在构造的时候加一些属性并且删除掉mainwindow中的scene
void GraphicsView::setBaseAttribute()
{// 设置场景QGraphicsScene *scene new QGraphicsScene(this);scene-addText(Hello, MainWindow);setScene(scene);// 设置接收场景交互setInteractive(true);// 接收Drop事件setAcceptDrops(true);// 接收鼠标移动事件setMouseTracking(true);// CacheNone 所有的绘画都是直接在视窗上完成的.// 背景被缓存,这影响自定义背景和基于backgroundBrush属性的背景.当这个标志被启用,QGraphicsView将分配一个像素图与viewport的完整尺寸.setCacheMode(CacheBackground);// 渲染时QGraphicsView在渲染背景或前景以及渲染每个项目时保护画家状态(参见QPainter::save())。这允许你让画工处于一个改变的状态(例如你可以调用QPainter::setPen()或QPainter::setBrush()而不需要在绘画后恢复状态)。但是如果项目始终恢复状态则应该启用此标志以防止QGraphicsView做同样的事情。setOptimizationFlag(DontSavePainterState);// 禁用QGraphicsView对曝光区域的抗锯齿自动调整。setOptimizationFlag(DontAdjustForAntialiasing);// QGraphicsView将通过分析需要重绘的区域来尝试找到最佳的更新模式。setViewportUpdateMode(SmartViewportUpdate);// 一个橡皮筋会出现。鼠标拖动将设置橡皮筋的几何形状并选择橡皮筋覆盖的所有项目。非交互式视图禁用此模式。setDragMode(RubberBandDrag);// 设置支持鼠标右键弹出菜单setContextMenuPolicy(Qt::DefaultContextMenu);// 设置横向和纵向滚动条常开setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);// 设置黑色背景setStyleSheet(QGraphicsView { background: #000000 });scene-setSceneRect(-1000, -1000, 2000, 2000);// 流出添加标尺的空间setViewportMargins(24, 0, 0, 24);
}
这个时候我们再运行的时候可以看到整个背景就编程黑色的了。并且出现了滚动条
缩放功能的添加
接下来我们给界面添加缩放功能
首先我们需要注释掉黑色背景方便我们查看文字的变化 并且添加以下代码以便放大缩小的时候更好的跟随鼠标
// 设置抗锯齿
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
// 设置放大缩小的时候跟随鼠标
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
setResizeAnchor(QGraphicsView::AnchorUnderMouse);接下来我们添加缩放函数同时我们重写鼠标事件
void GraphicsView::zoomIn()
{if(transform().m11() 1000.0) return;scale(zoomFactor, zoomFactor);
}void GraphicsView::zoomOut()
{if(transform().m11() 1.0) return;scale(1.0 / zoomFactor, 1.0 / zoomFactor);
}void GraphicsView::wheelEvent(QWheelEvent *event)
{const auto delta event-angleDelta().y();const auto pos event-position().toPoint();static auto sbUpdate [delta, this, scale 3](QScrollBar* sb) {// TODO 如果是多个view的话 会不会出问题sb-setValue(sb-value() - delta);};if (event-buttons() Qt::RightButton) {if (abs(delta) 120) {setInteractive(false);if (delta 0)zoomIn();elsezoomOut();setInteractive(true);}} else {switch (event-modifiers()) {case Qt::ControlModifier:if (abs(delta) 120) {setInteractive(false);if (delta 0)zoomIn();elsezoomOut();setInteractive(true);}break;case Qt::ShiftModifier:if (!event-angleDelta().x())sbUpdate(QAbstractScrollArea::horizontalScrollBar());break;case Qt::NoModifier:if (!event-angleDelta().x())sbUpdate(QAbstractScrollArea::verticalScrollBar());break;default:break;}}emit sig_mouseMove(mapToScene(pos));// QGraphicsView::wheelEvent(event);
}通过鼠标我们可以看到对应的变化我这里添加了混合按钮操作ctrl是缩放shift是移动横轴我这里就不贴效果图了你们按照此步骤加函数即可自己去尝试效果去吧。
我们还需要回到最初始的大小这个时候我们需要添加回到100%比例的函数。并且添加一个键盘事件按下空格的时候则回到100%的状态。这里可以在初始化的时候直接给设置成百分百
QSizeF GraphicsView::getRealSize()
{static QSizeF size;if (!size.isEmpty())return size;if (size.isEmpty()) FIXME 当前界面的物理尺寸size QGuiApplication::screens()[0]-physicalSize();return size;
}void GraphicsView::zoomTo100()
{根据物理尺寸设置大小 因为后面我们会引入尺子因此这里设置为根据物理尺寸设置double x 1.0, y 1.0;const double m11 QGraphicsView::transform().m11(), m22 QGraphicsView::transform().m22();const double dx QGraphicsView::transform().dx(), dy QGraphicsView::transform().dy();const QSizeF size(getRealSize()); // size in mmconst QRect scrGeometry(QApplication::primaryScreen()-geometry()); // size in pixx qAbs(1.0 / m11 / (size.height() / scrGeometry.height()));y qAbs(1.0 / m22 / (size.width() / scrGeometry.width()));std::cout dx dy std::endl;scale(x, y); 恢复到初始状态(位移状态未记录)
// QMatrix q;
// q.setMatrix(1,this-matrix().m12(),this-matrix().m21(),1,this-matrix().dx(),this-matrix().dy());
// this-setMatrix(q,false);
}void GraphicsView::keyPressEvent(QKeyEvent *event)
{switch (event-key()) {case Qt::Key_Space:zoomTo100();break;case Qt::Key_F:zoomFit();break;default:break;}QGraphicsView::keyPressEvent(event);
}void GraphicsView::zoomFit()
{fitInView(scene()-itemsBoundingRect(), false);
}void GraphicsView::fitInView(QRectF dstRect, bool withBorders)
{if (dstRect.isNull())return;if (withBorders)dstRect QMarginsF(dstRect.width() / 5, dstRect.height() / 5, dstRect.width() / 5, dstRect.height() / 5); // 5 mmQGraphicsView::fitInView(dstRect, Qt::KeepAspectRatio);
}加上标尺
接下来我们来给我们的视图加上左边和下面的标尺
先上一张图片 ruler
#ifndef GRAPHICSVIEWLEARN_RULER_H
#define GRAPHICSVIEWLEARN_RULER_H#include QWidget
#include QPenclass Ruler final : public QWidget
{Q_OBJECT
public:enum { Width 24};explicit Ruler(Qt::Orientation rulerType, QWidget* parent);void drawAScaleMeter(QPainter* painter, QRectF rulerRect, double scaleMeter, double startPosition);// 绘制刻度线void drawFromOriginTo(QPainter* painter, QRectF rect, double startMark, double endMark, int startTickNo, double step, double startPosition);protected:void paintEvent(QPaintEvent* event) override;void drawMousePosTick(QPainter* painter);private:Qt::Orientation orientation_;double grid_step_ {1.0};double origin_ {};double ruler_unit_ {1.0};double ruler_zoom_ {1.0};double tick_koef_ {1.0};QPoint cursor_pos_;QPen meter_pen_;bool draw_text_ {};
};#endif //GRAPHICSVIEWLEARN_RULER_H#include ruler.h
#include QPainterRuler::Ruler(Qt::Orientation rulerType, QWidget *parent): QWidget(parent), orientation_ { rulerType }
{setMouseTracking(true);setStyleSheet(QWidget{ background:black; });
}void Ruler::paintEvent(QPaintEvent *event)
{Q_UNUSED(event)QPainter painter(this);painter.setRenderHints(QPainter::TextAntialiasing);painter.setPen(QPen(Qt::darkGray, 0.0)); // 零宽度笔是装饰笔QRectF rulerRect(rect()); // 需要QRectF// 首先填充矩形painter.fillRect(rulerRect, QColor().rgb());if (qFuzzyIsNull(ruler_zoom_))return;// fixme 这个地方需要修改成带单位转换的grid_step_ pow(10.0, ceil(log10(8.0 / ruler_zoom_)));// ViewSettings::instance().gridStep(rulerZoom_);// 绘制小刻度if ((grid_step_ * ruler_zoom_) 35) {tick_koef_ 0.1;draw_text_ true;}meter_pen_ QPen(Qt::darkGray, 0.0);drawAScaleMeter(painter, rulerRect, grid_step_ * 1, static_castdouble(Ruler::Width) * 0.6);draw_text_ false;// 绘制中间刻度if ((grid_step_ * ruler_zoom_) 35) {tick_koef_ 0.5;draw_text_ true;}meter_pen_ QPen(Qt::green, 0.0);drawAScaleMeter(painter, rulerRect, grid_step_ * 5, static_castdouble(Ruler::Width) * 0.3);draw_text_ false;// 绘制整刻度线meter_pen_ QPen(Qt::red, 0.0);drawAScaleMeter(painter, rulerRect, grid_step_ * 10, static_castdouble(Ruler::Width) * 0);// 绘制当前鼠标位置十字线drawMousePosTick(painter);// 在视图和标尺之间分割线 红色的线(看是否需要)if ((1)) {QPointF starPt((Qt::Horizontal orientation_) ? rulerRect.topLeft() : rulerRect.topRight());QPointF endPt((Qt::Horizontal orientation_) ? rulerRect.topRight() : rulerRect.bottomRight()); // FIXME same branches!!!!!!painter.setPen(QPen(Qt::red, 2));painter.drawLine(starPt, endPt);}QWidget::paintEvent(event);
}void Ruler::drawAScaleMeter(QPainter* painter, QRectF rulerRect, double scaleMeter, double startPosition)
{bool isHorzRuler Qt::Horizontal orientation_;scaleMeter scaleMeter * ruler_unit_ * ruler_zoom_;double rulerStartMark isHorzRuler ? rulerRect.left() : rulerRect.top();// Ruler rectangle ending markdouble rulerEndMark isHorzRuler ? rulerRect.right() : rulerRect.bottom();if (origin_ rulerStartMark origin_ rulerEndMark) {drawFromOriginTo(painter, rulerRect, origin_, rulerEndMark, 0, scaleMeter, startPosition);drawFromOriginTo(painter, rulerRect, origin_, rulerStartMark, 0, -scaleMeter, startPosition);} else if (origin_ rulerStartMark) {int tickNo int((rulerStartMark - origin_) / scaleMeter);drawFromOriginTo(painter, rulerRect, origin_ scaleMeter * tickNo,rulerEndMark, tickNo, scaleMeter, startPosition);} else if (origin_ rulerEndMark) {int tickNo int((origin_ - rulerEndMark) / scaleMeter);drawFromOriginTo(painter, rulerRect, origin_ - scaleMeter * tickNo,rulerStartMark, tickNo, -scaleMeter, startPosition);}
}void Ruler::drawFromOriginTo(QPainter* painter, QRectF rect, double startMark, double endMark, int startTickNo, double step, double startPosition)
{const auto isHorzRuler (Qt::Horizontal orientation_);// fixme 这个地方要修改成单位转换的const auto K grid_step_ * tick_koef_ * 1.0;QColor color(0xFFFFFFFF - QColor(Qt::black).rgb());painter-setPen(QPen(color, 0.0));painter-setFont(font());QVectorQLineF lines;lines.reserve(abs(ceil((endMark - startMark) / step)));constexpr double padding 3;for (double current startMark; (step 0 ? current endMark : current endMark); current step) {double x1, y1;lines.push_back(QLineF(x1 isHorzRuler ? current : rect.left() startPosition,y1 isHorzRuler ? rect.top() : current,/*x2*/ isHorzRuler ? current : rect.right(),/*y2*/ isHorzRuler ? rect.bottom() - startPosition : current));if (draw_text_) {painter-save();auto number { QString::number(startTickNo * K) };if (startTickNo)number ((isHorzRuler ^ (step 0.0)) ? - : ) number;QRectF textRect(QFontMetricsF(font()).boundingRect(number));textRect.setWidth(textRect.width() 1);if (isHorzRuler) {painter-translate(x1 padding, textRect.height());painter-drawText(textRect, Qt::AlignCenter, number);} else {painter-translate(textRect.height() - padding, y1 - padding);painter-rotate(-90);painter-drawText(textRect, number);}painter-restore();}startTickNo;}painter-setPen(meter_pen_);painter-drawLines(lines.data(), lines.size());
}void Ruler::drawMousePosTick(QPainter* painter)
{QPoint starPt cursor_pos_;QPoint endPt;if (Qt::Horizontal orientation_) {starPt.setY(this-rect().top());endPt.setX(starPt.x());endPt.setY(this-rect().bottom());} else {starPt.setX(this-rect().left());endPt.setX(this-rect().right());endPt.setY(starPt.y());}painter-drawLine(starPt, endPt);
}好了本篇先介绍到这里接下来我会写下一篇让我们一起去实现后续的效果。