Qt によるカスタム コントロールの作成 - ユニバーサル ボーダレス

I.はじめに

前回の記事で、ウィジェット コントロールを渡すために使用され、親コンテナ内で自由に移動できるユニバーサル モバイル コントロールを書きました。この記事で書こうとしているのは一般的なボーダレスクラスについてですが、正確にはコントロールではなくコンポーネントといいますが、コントロールとは目に見えて描画要件があり、その必要性がコントロールに付いているものです。私たちが普段行っているプロジェクトでは、U インターフェイスを美しくするために、タイトル バー自体を美しくするためにカスタム ボーダレス フォームを使用するプロジェクトが多くありますが、カスタム ボーダレス フォームを設定した後、誰もが同じ問題に直面します。フォームの移動や伸縮を自分で処理するには、ボーダレス フォームが複数ある場合、多くの人は、移動や伸縮を実現するために、それぞれの場所に繰り返しコードを記述することを考えるでしょう。この関数を完成させるクラスをカプセル化して、直接渡すだけではどうでしょうか。形状。

QDialog フォームでは、プロパティ sizeGripEnabled を設定することで右下隅を伸ばすことができます。これだけではすべてのニーズを満たすのに十分ではありません。多くの場合、上下左右の 4 隅のサイズも伸ばす必要があります。これを書き、イベントフィルターを設置し、マウスが一定の領域に移動したことを認識し、マウスの形状が自動的に変化し、押されたかどうかを識別し、押された場合は対応する処理を行うという繰り返しが必要です。対応する処理コアは、フォームの XY 軸座標とサイズをリセットします。

オープンソースのアドレス: https://gitee.com/feiyangqingyun/QWidgetDemo https://github.com/feiyangqingyun/QWidgetDemo

2. 実装された機能

  • 1: フチなしにする必要があるウィジェットを指定できます
  • 2:枠線を中心とした8方向を自由に伸ばすことができます。
  • 3: より広い領域を識別するために、対応する位置のマージンを設定できます
  • 4: ドラッグを許可するかどうかを設定できます
  • 5:ストレッチを許可するかどうかを設定できます

3.エフェクト描画

4. ヘッダファイルのコード

#ifndef FRAMELESSWIDGET_H
#define FRAMELESSWIDGET_H

/**
 * 无边框窗体类 作者:feiyangqingyun(QQ:517216493) 2019-10-03
 * 1:可以指定需要无边框的widget
 * 2:边框四周八个方位都可以自由拉伸
 * 3:可设置对应位置的边距,以便识别更大区域
 * 4:可设置是否允许拖动
 * 5:可设置是否允许拉伸
 */

#include <QWidget>

#ifdef quc
#if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
#include <QtDesigner/QDesignerExportWidget>
#else
#include <QtUiPlugin/QDesignerExportWidget>
#endif

class QDESIGNER_WIDGET_EXPORT FramelessWidget : public QObject
#else
class FramelessWidget : public QObject
#endif

{
    Q_OBJECT
public:
    explicit FramelessWidget(QObject *parent = 0);

protected:
    bool eventFilter(QObject *watched, QEvent *event);

private:
    int padding;                    //边距
    bool moveEnable;                //可移动
    bool resizeEnable;              //可拉伸
    QWidget *widget;                //无边框窗体

    bool pressed;                   //鼠标按下
    bool pressedLeft;               //鼠标按下左侧
    bool pressedRight;              //鼠标按下右侧
    bool pressedTop;                //鼠标按下上侧
    bool pressedBottom;             //鼠标按下下侧
    bool pressedLeftTop;            //鼠标按下左上侧
    bool pressedRightTop;           //鼠标按下右上侧
    bool pressedLeftBottom;         //鼠标按下左下侧
    bool pressedRightBottom;        //鼠标按下右下侧

    int rectX, rectY, rectW, rectH; //窗体坐标+宽高
    QPoint lastPos;                 //鼠标按下处坐标

    QRect rectLeft;                 //左侧区域
    QRect rectRight;                //右侧区域
    QRect rectTop;                  //上侧区域
    QRect rectBottom;               //下侧区域
    QRect rectLeftTop;              //左上侧区域
    QRect rectRightTop;             //右上侧区域
    QRect rectLeftBottom;           //左下侧区域
    QRect rectRightBottom;          //右下侧区域

public Q_SLOTS:
    //设置边距
    void setPadding(int padding);
    //设置是否可拖动+拉伸
    void setMoveEnable(bool moveEnable);
    void setResizeEnable(bool resizeEnable);

    //设置要无边框的窗体
    void setWidget(QWidget *widget);
};

#endif // FRAMELESSWIDGET_H


この記事の特典として、Qt 開発学習パッケージと技術ビデオ (C++ 言語の基礎、Qt プログラミングの概要、QT シグナルとスロットのメカニズム、QT インターフェイス開発イメージの描画、QT ネットワーク、QT データベース) を無料で受け取ることができます。プログラミング、QT プロジェクトの実践、QT 組み込み開発、Quick モジュールなど) ↓↓↓↓下記参照↓↓料金受け取り記事下部をクリック↓↓

5. コアコード

#include "framelesswidget.h"
#include "qevent.h"
#include "qdebug.h"

FramelessWidget::FramelessWidget(QObject *parent) : QObject(parent)
{
    padding = 8;
    moveEnable = true;
    resizeEnable = true;
    widget = 0;

    pressed = false;
    pressedLeft = false;
    pressedRight = false;
    pressedTop = false;
    pressedBottom = false;
    pressedLeftTop = false;
    pressedRightTop = false;
    pressedLeftBottom = false;
    pressedRightBottom = false;

    //如果父类是窗体则直接设置
    if (parent->isWidgetType()) {
        setWidget((QWidget *)parent);
    }
}

bool FramelessWidget::eventFilter(QObject *watched, QEvent *event)
{
    if (widget != 0 && watched == widget) {
        if (event->type() == QEvent::Resize) {
            //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内
            int width = widget->width();
            int height = widget->height();

            //左侧描点区域
            rectLeft = QRect(0, padding, padding, height - padding * 2);
            //上侧描点区域
            rectTop = QRect(padding, 0, width - padding * 2, padding);
            //右侧描点区域
            rectRight = QRect(width - padding, padding, padding, height - padding * 2);
            //下侧描点区域
            rectBottom = QRect(padding, height - padding, width - padding * 2, padding);

            //左上角描点区域
            rectLeftTop = QRect(0, 0, padding, padding);
            //右上角描点区域
            rectRightTop = QRect(width - padding, 0, padding, padding);
            //左下角描点区域
            rectLeftBottom = QRect(0, height - padding, padding, padding);
            //右下角描点区域
            rectRightBottom = QRect(width - padding, height - padding, padding, padding);
        } else if (event->type() == QEvent::HoverMove) {
            //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别
            QHoverEvent *hoverEvent = (QHoverEvent *)event;
            QPoint point = hoverEvent->pos();
            if (resizeEnable) {
                if (rectLeft.contains(point)) {
                    widget->setCursor(Qt::SizeHorCursor);
                } else if (rectRight.contains(point)) {
                    widget->setCursor(Qt::SizeHorCursor);
                } else if (rectTop.contains(point)) {
                    widget->setCursor(Qt::SizeVerCursor);
                } else if (rectBottom.contains(point)) {
                    widget->setCursor(Qt::SizeVerCursor);
                } else if (rectLeftTop.contains(point)) {
                    widget->setCursor(Qt::SizeFDiagCursor);
                } else if (rectRightTop.contains(point)) {
                    widget->setCursor(Qt::SizeBDiagCursor);
                } else if (rectLeftBottom.contains(point)) {
                    widget->setCursor(Qt::SizeBDiagCursor);
                } else if (rectRightBottom.contains(point)) {
                    widget->setCursor(Qt::SizeFDiagCursor);
                } else {
                    widget->setCursor(Qt::ArrowCursor);
                }
            }

            //根据当前鼠标位置,计算XY轴移动了多少
            int offsetX = point.x() - lastPos.x();
            int offsetY = point.y() - lastPos.y();

            //根据按下处的位置判断是否是移动控件还是拉伸控件
            if (moveEnable) {
                if (pressed) {
                    widget->move(widget->x() + offsetX, widget->y() + offsetY);
                }
            }

            if (resizeEnable) {
                if (pressedLeft) {
                    int resizeW = widget->width() - offsetX;
                    if (widget->minimumWidth() <= resizeW) {
                        widget->setGeometry(widget->x() + offsetX, rectY, resizeW, rectH);
                    }
                } else if (pressedRight) {
                    widget->setGeometry(rectX, rectY, rectW + offsetX, rectH);
                } else if (pressedTop) {
                    int resizeH = widget->height() - offsetY;
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(rectX, widget->y() + offsetY, rectW, resizeH);
                    }
                } else if (pressedBottom) {
                    widget->setGeometry(rectX, rectY, rectW, rectH + offsetY);
                } else if (pressedLeftTop) {
                    int resizeW = widget->width() - offsetX;
                    int resizeH = widget->height() - offsetY;
                    if (widget->minimumWidth() <= resizeW) {
                        widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH);
                    }
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH);
                    }
                } else if (pressedRightTop) {
                    int resizeW = rectW + offsetX;
                    int resizeH = widget->height() - offsetY;
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH);
                    }
                } else if (pressedLeftBottom) {
                    int resizeW = widget->width() - offsetX;
                    int resizeH = rectH + offsetY;
                    if (widget->minimumWidth() <= resizeW) {
                        widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH);
                    }
                    if (widget->minimumHeight() <= resizeH) {
                        widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH);
                    }
                } else if (pressedRightBottom) {
                    int resizeW = rectW + offsetX;
                    int resizeH = rectH + offsetY;
                    widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH);
                }
            }
        } else if (event->type() == QEvent::MouseButtonPress) {
            //记住当前控件坐标和宽高以及鼠标按下的坐标
            QMouseEvent *mouseEvent = (QMouseEvent *)event;
            rectX = widget->x();
            rectY = widget->y();
            rectW = widget->width();
            rectH = widget->height();
            lastPos = mouseEvent->pos();

            //判断按下的手柄的区域位置
            if (rectLeft.contains(lastPos)) {
                pressedLeft = true;
            } else if (rectRight.contains(lastPos)) {
                pressedRight = true;
            } else if (rectTop.contains(lastPos)) {
                pressedTop = true;
            } else if (rectBottom.contains(lastPos)) {
                pressedBottom = true;
            } else if (rectLeftTop.contains(lastPos)) {
                pressedLeftTop = true;
            } else if (rectRightTop.contains(lastPos)) {
                pressedRightTop = true;
            } else if (rectLeftBottom.contains(lastPos)) {
                pressedLeftBottom = true;
            } else if (rectRightBottom.contains(lastPos)) {
                pressedRightBottom = true;
            } else {
                pressed = true;
            }
        } else if (event->type() == QEvent::MouseMove) {
            //改成用HoverMove识别
        } else if (event->type() == QEvent::MouseButtonRelease) {
            //恢复所有
            pressed = false;
            pressedLeft = false;
            pressedRight = false;
            pressedTop = false;
            pressedBottom = false;
            pressedLeftTop = false;
            pressedRightTop = false;
            pressedLeftBottom = false;
            pressedRightBottom = false;
            widget->setCursor(Qt::ArrowCursor);
        }
    }

    return QObject::eventFilter(watched, event);
}

void FramelessWidget::setPadding(int padding)
{
    this->padding = padding;
}

void FramelessWidget::setMoveEnable(bool moveEnable)
{
    this->moveEnable = moveEnable;
}

void FramelessWidget::setResizeEnable(bool resizeEnable)
{
    this->resizeEnable = resizeEnable;
}

void FramelessWidget::setWidget(QWidget *widget)
{
    if (this->widget == 0) {
        this->widget = widget;
        //设置鼠标追踪为真
        this->widget->setMouseTracking(true);
        //绑定事件过滤器
        this->widget->installEventFilter(this);
        //设置悬停为真,必须设置这个,不然当父窗体里边还有子窗体全部遮挡了识别不到MouseMove,需要识别HoverMove
        this->widget->setAttribute(Qt::WA_Hover, true);        
    }
}


6. コントロールの導入

  1. さまざまなダッシュボード、プログレスバー、プログレスボール、コンパス、カーブ、ルーラー、温度計、ナビゲーションバー、ナビゲーションバー、フラットイ、ハイライトボタン、スライドセレクター、旧暦などをカバーする160以上の絶妙なコントロール。qwt によって統合されるコントロールの数をはるかに上回ります。
  2. 各クラスは結合なしで個別のコントロールに独立して形成できます。各コントロールにはヘッダー ファイルと実装ファイルがあり、他のファイルに依存しません。単一のコントロールを次の形式でプロジェクトに統合するのが便利です。ソースコードの量が減り、コードが減ります。qwt のコントロール クラスは連動しており、高度に結合されているため、いずれかのコントロールを使用する場合は、すべてのコードを含める必要があります。
  3. すべて純粋な Qt で書かれ、QWidget+QPainter によって描画され、Qt4.6 から Qt5.13 までの任意の Qt バージョンをサポートし、mingw、msvc、gcc などのコンパイラをサポートし、Windows+Linux+Mac などのオペレーティング システムをサポートします。 +埋め込み Linux など、コード化けせずに Qt Creator に直接統合でき、組み込みコントロールのように使用でき、ほとんどのエフェクトはいくつかのプロパティを設定するだけで済み、非常に便利です。
  4. 各コントロールには、簡単に参照して使用できるように、コントロールのソース コードを含む対応する個別のデモがあります。また、すべてのコントロールで使用される統合デモも提供します。
  5. 各コントロールのソース コードには詳細な中国語のコメントがあり、統一された設計仕様に従って記述されているため、カスタム コントロールの作成方法を簡単に学ぶことができます。
  6. 各コントロールのデフォルトのカラーマッチングとデモに対応したカラーマッチングが非常に絶妙です。
  7. 130 を超える表示コントロールと 6 つの非表示コントロール。
  8. 一部のコントロールでは、複数のスタイルの選択肢と複数のインジケーター スタイルの選択肢が提供されます。
  9. すべてのコントロールはフォームストレッチの変化に適応します。
  10. 統合されたカスタム コントロール属性デザイナー、ドラッグ アンド ドロップ デザイン、WYSIWYG をサポートし、XML 形式のインポートとエクスポートをサポートします。
  11. ActiveX コントロールのデモが付属しており、すべてのコントロールを IE ブラウザで直接実行できます。
  12. fontawesome グラフィック フォントと Alibaba iconfont が収集した何百ものグラフィック フォントを統合し、グラフィック フォントがもたらす楽しさをお楽しみください。
  13. すべてのコントロールは最終的にダイナミック ライブラリ ファイル (dll など) を生成します。これは、ドラッグ アンド ドロップ設計のために qtcreator に直接統合できます。
  14. qml バージョンはすでにありますが、ユーザーからの要望が多ければ pyqt バージョンも検討される予定です。
  15. カスタム コントロール プラグインは、バックドアや制限なしでダイナミック ライブラリを自由に使用できます (永久無料)。お気軽にご利用ください。
  16. 現在、32 バージョンの dll が提供されており、その中で qt_5_7_0_mingw530_32 バージョンは常に最新かつ完全であることが保証されます。
  17. コントロールは随時追加および改善され、SDK も随時更新されます。ご提案は大歓迎です。ありがとうございます。
  18. Qt の入門書としては、Huo Yafei 著の『Qt Creator によるクイック スタート』および『Qt5 プログラミング入門』を、Qt の上級書籍については、公式の『C++ GUI Qt4 Programming』をお勧めします。
  19. プログラマー向けの自己啓発本と計画本シリーズ『偉そうなプログラマー』、『プログラマーの成長講座』、『悩み解消プログラマー』シリーズはとてもためになり、一生役に立つのでとてもおすすめです。
  20. SDK アドレス: https://gitee.com/feiyangqingyun/QUCSDK https://github.com/feiyangqingyun/qucsdk

この記事の特典として、Qt 開発学習パッケージと技術ビデオ (C++ 言語の基礎、Qt プログラミングの概要、QT シグナルとスロットのメカニズム、QT インターフェイス開発イメージの描画、QT ネットワーク、QT データベース) を無料で受け取ることができます。プログラミング、QT プロジェクトの実践、QT 組み込み開発、Quick モジュールなど) ↓↓↓↓下記参照↓↓料金受け取り記事下部をクリック↓↓

おすすめ

転載: blog.csdn.net/hw5230/article/details/132864878