日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

QGraphicsView图形视图框架使用(三)位移变换和图元定位

發布時間:2023/12/14 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 QGraphicsView图形视图框架使用(三)位移变换和图元定位 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 位移變換
    • 圖片渲染
    • 圖元定位
    • 場景聚焦

當視圖框架中的圖元比較多且位置比較散亂的時候,為了操作某個特定的圖元,我們需要對圖元進行位移變換和定位,而從更加方便的操作管理圖元。這里就介紹一下圖元的位移變換和定位。

位移變換

在圖形視圖框架中我們可以通過QTransform和setTransform(const QTransform &matrix, bool combine = false),對來圖元進行位移變換,包括移動、旋轉、縮放等等。有的時候,我們在變換一些圖形的時候,希望視圖中一部分保持不變。這時候,我們就需要讓某些圖元忽略整個視圖的位移變換。下面介紹一下忽略位移變化的實現方式:

繪制固定線寬的直線
我們可以通過 QPen::setCosmetic(true)開啟固定線寬功能,這樣繪制出來的直線在位移變化的過程中就可以保持固定寬度了。參考示例如下:

#pragma execution_character_set("utf-8") #include <QApplication> #include <QGraphicsView> #include <QGraphicsItem> #include <QTransform> #include <QGraphicsProxyWidget>int main(int argc, char *argv[]) {QApplication a(argc, argv);QGraphicsScene scene;scene.addLine(-50, 0, 50, 0);//讓繪制的線保持固定寬度QGraphicsLineItem* row_item = scene.addLine(0, -50, 0, 50);QPen pen;pen.setCosmetic(true);row_item->setPen(pen);QGraphicsView view(&scene);view.setRenderHint(QPainter::Antialiasing);view.scale(4,4);view.show();return a.exec(); }

顯示效果如下:

繪制位置保持不變的文字
這里以一個兩端是圓形的啞鈴狀圖元為例進行說明,在默認狀態下所有視圖中的圖元都會受位移變換的影響,對應的示例如下:

#pragma execution_character_set("utf-8") #include <QApplication> #include <QGraphicsView> #include <QGraphicsItem> #include <QTransform> #include <QGraphicsProxyWidget>QGraphicsLineItem *createComplexItem(qreal width, qreal radius,bool is_fixed) {QLine main_line(QPoint(-width/2,20),QPoint(width/2,20));//繪制直線QGraphicsLineItem *parent = new QGraphicsLineItem(main_line);QPen pen = parent->pen();pen.setCosmetic(true);parent->setPen(pen);QRectF circleBoundary(-radius, -radius, 2 * radius, 2 * radius);for(int index=1;index<=2; ++index){QGraphicsEllipseItem * child = new QGraphicsEllipseItem(circleBoundary, parent);child->setBrush(QBrush(Qt::black));if(index == 1){child->setPos(main_line.p1());}else{child->setPos(main_line.p2());}QGraphicsSimpleTextItem *text = new QGraphicsSimpleTextItem(QString::number(index));text->setParentItem(child);text->setBrush(Qt::green);text->setPos(-text->boundingRect().width() / 2, -text->boundingRect().height() / 2);}return parent; }int main(int argc, char *argv[]) {QApplication a(argc, argv);//繪制原始圖元QGraphicsScene scene;QGraphicsLineItem *item1 = createComplexItem(100,10,false);scene.addItem(item1);//繪制旋轉之后的圖元QGraphicsLineItem *item2 = createComplexItem(100,10,true);scene.addItem(item2);item2->setPos(150, 0);item2->setRotation(20);QGraphicsView view(&scene);view.show();view.setRenderHint(QPainter::Antialiasing);//對整個視圖進行縮放view.scale(2, 2);return a.exec(); }

顯示效果如下:

為了查看方便,我們并不希望圖形中的文字跟隨著視圖的位移變化發生變化,我們可以通過下面的配置讓圖形中的文字保持不變。

text->setFlag(QGraphicsItem::ItemIgnoresTransformations);

顯示效果如下:

雖然圖形中的文字沒有發生位移變化,但也出現了一個非常詭異的問題。那就是文字不再顯示在圖形中央了。這是因為setPos()使用的是父對象的坐標系,而boundingRect()使用的是當前對象的坐標系,兩個坐標系本來是重合的,但是設置了對應屬性并發生位移變化之后,兩個坐標系不再重合才引起了該問題。

解決方案一
ItemIgnoresTransformations不會影響Item自身的位移變化。我們可以通過下面的位移變化方式來移動圖元,也就是用setTransform()替代setPos()。setTransform()依據自身的坐標系作為基準進行移動,不會受父對象的坐標系變化影響。

QTransform transform; transform.translate(-text->boundingRect().width() / 2,-text->boundingRect().height() / 2); text->setTransform(transform); text->setFlag(QGraphicsItem::ItemIgnoresTransformations);

顯示效果如下:

解決方案二
方案二是通過添加一個輔助的矩形圖元來進行定位的。矩形圖元和文本圖元的boundingRect()保持一致。然后對應的文本內容只要顯示在輔助定位的圖元左上角就可以了。其實方案二和方案一的本質相同,都是不再直接使用父對象圖元的坐標系,改用自身坐標系了。

QGraphicsLineItem *createComplexItem(qreal width, qreal radius,bool is_fixed) {QLine main_line(QPoint(-width/2,20),QPoint(width/2,20));//繪制直線QGraphicsLineItem *parent = new QGraphicsLineItem(main_line);QPen pen = parent->pen();pen.setCosmetic(true);parent->setPen(pen);//繪制兩頭的圓形QRectF circleBoundary(-radius, -radius, 2 * radius, 2 * radius);for(int index=1;index<=2; ++index){QGraphicsEllipseItem * child = new QGraphicsEllipseItem(circleBoundary, parent);child->setBrush(QBrush(Qt::black));if(index == 1){child->setPos(main_line.p1());}else{child->setPos(main_line.p2());}//添加一個矩形圖元輔助定位QGraphicsSimpleTextItem *text = new QGraphicsSimpleTextItem(QString::number(index));QRectF textRect = text->boundingRect();textRect.translate(-textRect.center());QGraphicsRectItem *rectItem = new QGraphicsRectItem(textRect, child);rectItem->setPen(QPen(Qt::green));rectItem->setFlag(QGraphicsItem::ItemIgnoresTransformations);text->setParentItem(rectItem);text->setPos(textRect.topLeft());text->setBrush(Qt::green);}return parent; }

顯示效果如下:

輔助矩形圖元,在實際使用的時候是不需要顯示的,我們可以過下面的配置隱藏矩形外邊框的顯示。

//隱藏矩形邊框 rectItem->setPen(Qt::NoPen);

圖片渲染

在開發一些類似于2D游戲的應用的時候,我們有時候需要將整個Scene場景內容進行圖片保存。QGraphicsView實現圖片保存還是比較簡單的,只需要將真個場景的內容渲染到圖片容器中即可,對應的實現如下:

QGraphicsView view(&scene); view.show(); QRect rect = scene.sceneRect().toAlignedRect(); QImage image(rect.size(), QImage::Format_ARGB32); //背景設置為透明 image.fill(Qt::transparent); //將渲染的繪圖保存到本地 QPainter painter(&image); scene.render(&painter); image.save("picture.png");

圖元定位

在QGraphicsView應用中,我們有時候需要通過圖元的坐標來對圖元進行定位。圖元視圖中也提供了對應的接口,如果我們只想要最頂層的圖元可以調用itemAt()接口,對應的實現如下:

//如果存在返回對象,沒有的話返回空 //@1坐標點 //@2View位移變化矩陣,如果圖形中包含忽略transform的圖元的時候,需要提供該矩陣。 QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position, const QTransform &deviceTransform) const

很多時候視圖框架中位于某個坐標點的圖元,可能有很多,如果我們需要所有圖元的話,那么就得用到items()接口了,QGraphicsScene中存在很多同名的重載函數,可以根據自己的需要選擇使用,對應的接口定義如下:

//return: 返回圖元對象的列表 //@1:坐標點 //@2:選擇模式 //@3:排序方式 Qt::DescendingOrder降序排列 最上層的圖元顯示在列表的首位 //Qt::AscendingOrder增序排列,排列方式相反,最上層的圖元顯示在列表的末尾 //@4View位移變化矩陣,如果圖形中包含忽略transform的圖元的時候,需要提供該矩陣。 QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape, Qt::SortOrder order = Qt::DescendingOrder, const QTransform &deviceTransform = QTransform()) const

items()參數2的選擇模式有四種,如下表所示:

模式說明
Qt::ContainsItemBoundingRect被選擇的圖元的外邊界矩形,必須完全在選擇范圍內,才會被選中
Qt::IntersectsItemBoundingRect與選擇范圍有交集,但并不完全包含的圖元,也會被選中。
Qt::ContainsItemShape被選擇的圖元的外邊界矩形,必須完全在選擇范圍內,才會被選中。此種模式下,邊界計算更加精確,但是也更耗費計算能力。
Qt::IntersectsItemShape與Qt::IntersectsItemBoundingRect類似,但是邊界計算精度更高。

場景聚焦

在一些特殊的場景下,我們需要顯示Scene的一部分區域,或者某個特定的圖元,這時候視圖View的焦點就聚焦到了Scene的一部分。我們可以通過控制滾動條來實現對應的功能,但是控制精度有限。圖形視圖框架提供了對應的接口,通過對應的接口我們可以很輕松的實現場景聚焦。對應的接口如下。

centerOn()可以通過滾動View中場景,使某個坐標或者某個Item顯示在視圖中央。如果某個Item離邊界特別近,或者在邊界之外,centerOn()只會讓該元素可見,并不會將其移動到視圖中央。

//聚焦到某個坐標點 QGraphicsView::centerOn(const QPointF &pos) //聚焦到某個Item QGraphicsView::centerOn(const QGraphicsItem *item) //注意:點坐標是float類型,而scrollbar步長是int類型的,所以center是一個近似值

ensureVisible()功能和centerOn()功能類似,通過滾動View中的內容使某個Rect或者Item可見,同時還可以指定該范圍與邊界的最小margin。如果聚焦的范圍超出了邊界,那么View視圖會定位到距離這個范圍最近的位置。

//讓某個矩形范圍可見 QGraphicsView::ensureVisible(const QRectF &rect, int xmargin = 50, int ymargin = 50) //讓某個Item可見 QGraphicsView::ensureVisible(const QGraphicsItem *item, int xmargin = 50, int ymargin = 50)

fitInView()功能是通過對視圖View進行縮放和移動,讓某個固定范圍或者Item,完全顯示在View當中,使用fitInView()的時候要確保指定的范圍必須包含在Scene中。fitInView()無法保證指定的范圍完整的顯示在View中。

通常在實現resizeEvent()的時候調用fitInView()來確保視圖View尺寸變化的時候,對應的場景內容能自動的填充滿對應的的范圍。但有一點需要注意,如果在位移變換的過程中啟動了滾動條的自動狀態,在ResizeEvent()調用fitInView(),會出現遞歸調用,從而引發崩潰。可以將滾動條狀態設置為Always On 或者 Always Off來避免該問題。

//聚焦某個范圍 QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio) //聚焦某個控件 QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio)

下面以一個例子來說明一下幾種場景聚焦的接口的用法,例子中通過按鍵事件來聚焦不同的圖元:

//graphicsview.h #ifndef GRAPHICSVIEW_H #define GRAPHICSVIEW_H#include <QWidget> #include <QGraphicsScene>namespace Ui { class GraphicsView; }class GraphicsView : public QWidget {Q_OBJECTpublic:explicit GraphicsView(QWidget *parent = 0);~GraphicsView(); public:bool eventFilter(QObject* watched, QEvent* event) override;private://場景QGraphicsScene m_scene;//矩形圖元QGraphicsRectItem* m_rect_item;//圖片圖元QGraphicsPixmapItem* m_pixmap_item;//橢圓圖元QGraphicsEllipseItem* m_ellipse_item;Ui::GraphicsView *ui; };#endif // GRAPHICSVIEW_H //graphicsview.cpp #include "graphicsview.h" #include "ui_graphicsview.h" #include <QGraphicsItem> #include <QKeyEvent> #include <QDebug>GraphicsView::GraphicsView(QWidget *parent) :QWidget(parent),ui(new Ui::GraphicsView) {ui->setupUi(this);this->setWindowTitle("GraphicsView");//在Scene中心添加一個十字坐標m_scene.addLine(-1000, 0, 1000, 0);m_scene.addLine(0, -1000, 0, 1000);//添加矩形圖元m_rect_item = m_scene.addRect(-50,-50,150,150);m_rect_item->setBrush(QBrush(QColor("#4D9CF8")));m_rect_item->setFlag(QGraphicsItem::ItemIsSelectable,true);m_rect_item->setFlag(QGraphicsItem::ItemIsMovable,true);//添加橢圓圖元m_ellipse_item = m_scene.addEllipse(QRectF(QPointF(150,100),QPointF(250,50)),QPen("#0000FF"),QBrush(QColor("#4D00F8")));m_ellipse_item->setZValue(10);m_ellipse_item->setFlag(QGraphicsItem::ItemIsSelectable,true);m_ellipse_item->setFlag(QGraphicsItem::ItemIsMovable,true);//繪制圖片m_pixmap_item = m_scene.addPixmap(QPixmap(":/demo.png"));m_pixmap_item->setPos(-300,-50);m_pixmap_item->setZValue(20);ui->graphicsView->setScene(&m_scene);m_ellipse_item->setFlag(QGraphicsItem::ItemIsSelectable,true);m_ellipse_item->setFlag(QGraphicsItem::ItemIsMovable,true);//抗鋸齒效果ui->graphicsView->setRenderHint(QPainter::Antialiasing);installEventFilter(this);//添加fillModeui->fit_mode_combo->addItems(QStringList() << "IgnoreAspectRation" << "KeepAspectRatio" << "KeepAspectRationByExpanding");ui->fit_mode_combo->setItemData(0,Qt::IgnoreAspectRatio);ui->fit_mode_combo->setItemData(1,Qt::KeepAspectRatio);ui->fit_mode_combo->setItemData(2,Qt::KeepAspectRatioByExpanding);ui->fit_mode_combo->setFocusPolicy(Qt:: NoFocus);}GraphicsView::~GraphicsView() {delete ui; }bool GraphicsView::eventFilter(QObject *watched, QEvent *event) {if (event->type() == QEvent::KeyPress){QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);Qt::KeyboardModifiers modifier = keyEvent->modifiers();int change_mode = ui->fit_mode_combo->currentData().toInt();qDebug() << change_mode;//center()聚焦圖元if(keyEvent->key() == Qt::Key_R && modifier == Qt::NoModifier){ui->graphicsView->centerOn(m_rect_item);}else if(keyEvent->key() == Qt::Key_P && modifier == Qt::NoModifier){ui->graphicsView->centerOn(m_pixmap_item);}else if(keyEvent->key() == Qt::Key_E && modifier == Qt::NoModifier){ui->graphicsView->centerOn(m_ellipse_item);}//ensureVisible()聚焦圖元else if(keyEvent->key() == Qt::Key_R && modifier == Qt::ControlModifier){ui->graphicsView->ensureVisible(m_rect_item,50,50);}else if(keyEvent->key() == Qt::Key_P && modifier == Qt::ControlModifier){ui->graphicsView->ensureVisible(m_pixmap_item,100,100);}else if(keyEvent->key() == Qt::Key_E && modifier == Qt::ControlModifier){ui->graphicsView->ensureVisible(m_ellipse_item,20,20);}//fitInView()聚焦圖元else if(keyEvent->key() == Qt::Key_S && modifier == Qt::NoModifier){ui->graphicsView->fitInView(m_ellipse_item,Qt::AspectRatioMode(change_mode));}}return QObject::eventFilter(watched, event); }

使用CenterOn聚焦圖元的效果如下所示:

使用ensureVisible()聚焦圖元的效果如下所示:

fitInView()有四種縮放策略,分別如下:

模式說明
Qt::IgnoreAspectRatio自由縮放,不受長寬比限制,盡可能的填充View
Qt::KeepAspectRatio在保持長寬比的情況下,在View范圍內縮放,使View中顯示的范圍盡可能的大。
Qt::KeepAspectRatioByExpanding在保持長寬比的情況下,通過縮放讓處于View范圍之外的面積盡可能的小。

官方文檔中各種縮放模式的示例圖如下所示:

在例子中對應的效果如下所示:

總結

以上是生活随笔為你收集整理的QGraphicsView图形视图框架使用(三)位移变换和图元定位的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。