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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

affine工程难点、亮点汇总

發(fā)布時間:2023/12/15 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 affine工程难点、亮点汇总 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

工程存放的路徑在:Examples\Qt-XX.XX.XX\widgets\painting\affine

其中XX.XX.XX為Qt的版本號,如:5.14.1。

該工程有如下個人認為的難點、亮點:

  • 《利用QPainter、QColor繪制黑白棋盤功能》
  • 《QPushButton延時單擊功能》
  • 《利用QCommonStyle繪制自定義的窗體部件》

對XFormView類的drawPixmapType函數(shù)解釋:

void XFormView::drawPixmapType(QPainter *painter) {QPointF center(m_pixmap.width() / qreal(2), m_pixmap.height() / qreal(2));/* 將painter移動(ctrlPoints.at(0) - center)距離,也即將HoverPoints類對象pts的第一個控制點移動到位圖的中心位置,以便第一個控制點繪制在位圖的中心*/painter->translate(ctrlPoints.at(0) - center);// 進行變換,以便位圖繞著自己的中心軸旋轉(zhuǎn)painter->translate(center);painter->rotate(m_rotation);painter->scale(m_scale, m_scale);painter->shear(0, m_shear);painter->translate(-center);.............................// 其它代碼 }

如果被繪制對象(本例是指m_pixmap對象表示的圖片)的中心點和繪圖對象painter的中心點不重合,這時候旋轉(zhuǎn)被繪制對象,則這個對象會圍繞painter設(shè)置的中心點進行旋轉(zhuǎn),會轉(zhuǎn)一個大圈。那么怎么做才能讓它在任何位置的時候,都圍繞自己的軸心進行旋轉(zhuǎn)?解決思路如下:

1. 先保存物體在世界坐標系下的坐標,即物體在世界坐標系下的中心點坐標
2. 再將物體移動到世界坐標系的原點。
3. 在世界坐標系的原點旋轉(zhuǎn)好后,再移動回原來的位置,即步驟1中的提到的坐標。

上段代碼的思路就是按這樣來的。

  • drawTextType函數(shù)同drawPixmapType函數(shù),不再贅述。
  • HoverPoints分析

構(gòu)造函數(shù)分析:

HoverPoints::HoverPoints(QWidget *widget, PointShape shape): QObject(widget) {m_widget = widget;widget->installEventFilter(this);widget->setAttribute(Qt::WA_AcceptTouchEvents);m_connectionType = CurveConnection;m_sortType = NoSort;m_shape = shape;m_pointPen = QPen(QColor(255, 255, 255, 191), 1);m_connectionPen = QPen(QColor(255, 255, 255, 127), 2);m_pointBrush = QBrush(QColor(191, 191, 191, 127));m_pointSize = QSize(11, 11);m_currentIndex = -1;m_editable = true;m_enabled = true;connect(this, &HoverPoints::pointsChanged,m_widget, QOverload<>::of(&QWidget::update)); }

構(gòu)造函數(shù)初始化了一些變量,對各個變量作用解釋如下:

  • ?m_widget:外層傳入的窗體部件對象,也即HoverPoints類附屬在哪個窗體上,通過通讀代碼可以發(fā)現(xiàn)其是指外層的XFormView窗體對象。
  • 函數(shù)的第5行為外層傳入的窗體部件對象安裝了事件過濾器。installEventFilter函數(shù)的用法請參見《installEventFilter、eventFilter函數(shù)理解》
  • 第6行設(shè)置觸屏事件,從后續(xù)代碼可以看到本例支持觸屏操作。
  • m_connectionType:連接類型。取值為:直線、曲線(貝塞爾曲線)和無
  • m_sortType:?點的排序類型。取值為:不排序、按點的x坐標排序、按點的y坐標排序。
  • m_shape:懸浮點的現(xiàn)狀。取值為:圓形、矩形。
  • m_pointPen:用于畫懸浮點的畫筆。
  • m_connectionPen:用于畫連接線的畫筆。
  • m_pointBrush:用于點的畫刷。
  • m_pointSize:點所在形狀的外圍矩形寬高。
  • m_currentIndex:當前在控制點容器m_points中的點的索引。
  • m_editable:是否可編輯。
  • m_enabled:是否可用。

? ? ?eventFilter 中的鼠標左鍵按下流程分析:

? ? ?如下鼠標左鍵按下時的流程:(圖片下載到本地放大看):

說明:

  • ?本程序不僅支持鼠標還支持觸摸顯示屏操作。上述流程是支持鼠標且鼠標左鍵按下流程。
  • 程序根據(jù)m_sortType來決定坐標排序類型,在構(gòu)造函數(shù)中默認不排序。當排序類型為XSort? ??時,則從控制點的容器m_points中找到第1個橫坐標大于鼠標按下時鼠標單擊點的橫坐標的點。當排序類型為YSort?時,則從控制點的容器m_points中找到第1個縱坐標大于鼠標按下時鼠標單擊點的縱坐標的點,并記錄該點在m_points中的索引號。這樣做的效果是為了實現(xiàn)如下效果:
  • 起始m_points中只有A、B兩個控制點,則畫出上圖類似的A到B的線,當鼠標左鍵單擊的點為C時,且C在A、B之間時,則畫出如下直線:

    ?當C在B 的右側(cè)時,則畫出如下的:

    3 .鼠標左鍵按下時,當步驟2完成后,將符合條件的鼠標左鍵單擊時所在的點插入m_points、m_locks,插入的索引號為步驟2記錄的索引號然后調(diào)用?firePointChange函數(shù)。

    ? ?4. firePointChange函數(shù)中根據(jù)m_sortType表示的排序規(guī)則將m_points排序。排序后再次從m_points中找到步驟2鼠標左鍵按下時的點在m_points中的索引m_currentIndex,然后發(fā)送pointsChanged信號。

    eventFilter 中的觸摸事件流程分析:

    QEvent::TouchBegin、QEvent::TouchUpdate、Qt::TouchPointReleased、QEvent::TouchEnd事件。其中最需要搞懂的是QEvent::TouchBegin、QEvent::TouchUpdate中的Qt::TouchPointPressed,實現(xiàn)代碼如下:

    // find the point, move it const auto mappedPoints = m_fingerPointMapping.values(); QSet<int> activePoints = QSet<int>(mappedPoints.begin(), mappedPoints.end()); int activePoint = -1; qreal distance = -1; const int pointsCount = m_points.size(); const int activePointCount = activePoints.size(); if (pointsCount == 2 && activePointCount == 1) { // only two pointsactivePoint = activePoints.contains(0) ? 1 : 0; } else {for (int i = 0; i < pointsCount; ++i) {if (activePoints.contains(i))continue;qreal d = QLineF(touchPoint.pos(), m_points.at(i)).length();if ((distance < 0 && d < 12 * pointSize) || d < distance) {distance = d;activePoint = i;}} } if (activePoint != -1) {m_fingerPointMapping.insert(touchPoint.id(), activePoint);movePoint(activePoint, touchPoint.pos()); }

    其基本思路是:將觸摸點的id和觸摸點索引值作為QHash的鍵值對插入到類型為QHash的成員變量m_fingerPointMapping,以后移動、釋放都從m_fingerPointMapping取出相應(yīng)的點進行操作。

    eventFilter 中的QEvent::Paint流程分析:

    代碼如下:

    QWidget *that_widget = m_widget;m_widget = nullptr;QCoreApplication::sendEvent(object, event);m_widget = that_widget;paintPoints();return true;

    這里需要著重說明的是:在發(fā)送QEvent::Paint事件之前必須m_widget將先保存起來(第1句代碼),然后將m_widget設(shè)置為nullptr(第2句),發(fā)送完后再將m_widget恢復為原來的。設(shè)置為nullptr是避免eventFilter 函數(shù)無限遞歸調(diào)用導致棧耗盡程序崩潰,恢復回來為了下次再次發(fā)送QEvent::Paint事件。

    updateCtrlPoints函數(shù)分析:

    void XFormView::updateCtrlPoints(const QPolygonF &points) {QPointF trans = points.at(0) - ctrlPoints.at(0);if (qAbs(points.at(0).x() - points.at(1).x()) < 10&& qAbs(points.at(0).y() - points.at(1).y()) < 10)pts->setPoints(ctrlPoints);if (!trans.isNull()) {ctrlPoints[0] = points.at(0);ctrlPoints[1] += trans;pts->setPoints(ctrlPoints);}ctrlPoints = points;QLineF line(ctrlPoints.at(0), ctrlPoints.at(1));m_rotation = 360 - QLineF(0, 0, 1, 0).angleTo(line);if (trans.isNull())emit rotationChanged(int(m_rotation * 10)); }

    第3行代碼:就是計算通過信號pointsChanged發(fā)送過來的m_points第一個控制點和當前的第一個控制點的在x、y坐標上的偏移量。

    第5-7行:檢測m_points第1個控制點、第2個控制點x、y坐標上的偏移量都小于10,如果是,則控制點還是設(shè)置為當前控制點。

    第8-11行:如果第3句代碼算出的偏移量不為空,則當前第1個控制點更新為m_points中的第1個點,注意:在m_sortType為NoSort時,且鼠標左鍵按下時,m_points中的第1個點為鼠標左鍵按下時的點,而在XFormView類的paint函數(shù)繪制函數(shù)如:drawPixmapType、drawTextType經(jīng)過如下代碼將繪制中心移動到了被繪制對象(如:圖片、文本)的中心了(參見前面對drawPixmapType的分析):

    painter->translate(ctrlPoints.at(0) - center);

    ? ? ? ? 所以這樣造成的現(xiàn)象是鼠標左鍵在哪單擊,被繪制物體的中心就移動到鼠標左鍵單擊的點。

    當前第2個控制點累加第3行產(chǎn)生的偏移量,并重新設(shè)置HoverPoints類的控制點容器m_points

    第15-16行根據(jù)更新后的控制點ctrlPoints算出其和水平線的夾角,并設(shè)置旋轉(zhuǎn)成員變量m_rotation。根據(jù)Qt的元系統(tǒng)屬性技術(shù)( 屬性通過Q_PROPERTY關(guān)鍵字標識),一旦m_rotation被更新,則XFormView類的setRotation函數(shù)會自動調(diào)用,從而導致被繪制的物體呈現(xiàn)旋轉(zhuǎn)。

    程序bug說明:

    HoverPoints類的m_sortType 為 XSort或YSort時,會崩潰,更改如下:

    movePoint函數(shù)加入如下代碼:

    void HoverPoints::movePoint(int index, const QPointF &point, bool emitUpdate) {// 索引越界了,要判斷下if ((index < 0) || (m_points.size() <= index) ){return;}if ( m_locks.size() <= index ){return;}// 其它代碼 }

    firePointChange()函數(shù)加入如下代碼:

    void HoverPoints::firePointChange() {// printf("HoverPoints::firePointChange(), current=%d\n", m_currentIndex);if (m_sortType != NoSort) {QPointF oldCurrent;/*if (m_currentIndex != -1) {*/// 這里有崩潰,索引越界了,要判斷下if ((0 <= m_currentIndex) && (m_currentIndex < m_points.size())){oldCurrent = m_points[m_currentIndex];}// 其它代碼}

    ?XFormView類的updateCtrlPoints函數(shù)加入如下判斷:

    void XFormView::updateCtrlPoints(const QPolygonF &points) {// 右鍵在控制點單擊時,這里有崩潰,索引越界了,要判斷下if (points.size() < 2){return;}.................. }

    未完,待續(xù)!

    總結(jié)

    以上是生活随笔為你收集整理的affine工程难点、亮点汇总的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。