Qt學習-- Qt Designer自定義插件(QSwitchButton)

Qt Designer自定義插件(QSwitchButton)

創建自定義插件

在使用Qt Designer設計窗體界面時,我們可以使用Widget Box裏的窗體控件非常方便的繪製界面,比如拖進去一個按鈕,一個文本編輯器等。雖然Qt Designer裏的控件可以滿足我們大部分的需求,但是有時候,也會產生一些自定義的需要,比如Switch開關。

img

下面就以此爲例,講解一下如何創建自定義的窗體控件。

第一步:創建QtDesigner自定義控件工程

​ 打開Qt Creator,創建一個Qt4 設計師自定義控件,如下圖所示:

![Qt Designer自定義插件(QSwitchButton)](F:\技術資料\Blog\圖片\Qt Designer自定義插件(QSwitchButton).PNG)

添加自定義類QSwitchButton

![Qt Designer自定義插件(QSwitchButton)1](F:\技術資料\Blog\圖片\Qt Designer自定義插件(QSwitchButton)1.PNG)

添加說明(可以忽略,函數中可以修改):

組:該控件所屬的組中的Qt Designer的小工具盒;

工具提示:一個簡短的說明,以幫助用戶識別Qt Designer中的部件;

這是什麼:爲Qt Designer用戶設計的部件一個較長的描述

![Qt Designer自定義插件(QSwitchButton)1-1](F:\技術資料\Blog\圖片\Qt Designer自定義插件(QSwitchButton)1-1.PNG)

dom XML:描述了部件的屬性,例如:對象名稱、大小提示,以及其它標準的QWidget屬性的描述。

![Qt Designer自定義插件(QSwitchButton)1-2](F:\技術資料\Blog\圖片\Qt Designer自定義插件(QSwitchButton)1-2.PNG)

定義插件名稱:

![Qt Designer自定義插件(QSwitchButton)2](F:\技術資料\Blog\圖片\Qt Designer自定義插件(QSwitchButton)2.PNG)

第二步:編譯控件工程

​ 爲了淌通整個自定義控件的編寫流程,我們先不做任何更改,切換爲Release版本,直接編譯一下。

第三步:部署插件

​ 編譯完成後,在輸出目錄下,將生成的dll文件和lib文件一起拷貝到Qt designer和Qt Creator的插件目錄下,以我使用的Qt爲例完整路徑爲:

C:\Qt\Qt5.9.1\5.9.1\msvc2015\plugins\designer。

C:\Qt\Tools\QtCreator\bin\plugins\designer

之後,啓動Qt designer或Qt Creator,創建一個窗體,此時就會發現在左側的Buttons裏出現了我們自己的Switch Button控件,我們可以像使用其它控件一樣,把我們自己的控件拖繪到窗體上,如下圖所示:

switchButton1

switchButton2

如果自定義控件沒有出現在Widgetbox裏,那麼此時你可以通過【幫助-關於插件】菜單,打開插件信息對話框,點擊刷新按鈕,只要你沒有忘記把dll和lib文件拷貝到正確的位置,插件都會自動識別並加載。對於其它版本的Qt也一樣,比如我自己的電腦裏安裝了好幾個版本的Qt,對於其它版本的Qt,做法也是一樣,只需要把插件工程生成的dll和lib文件放置到相應版本的插件目錄下去即可。

插件接口

在自動向導創建的插件QSwitchButtonPlugin繼承了接口QDesignerCustomWidgetInterface

#ifndef QSWITCHBUTTONPLUGIN_H
#define QSWITCHBUTTONPLUGIN_H

#include <QDesignerCustomWidgetInterface>

class QSwitchButtonPlugin : public QObject, public QDesignerCustomWidgetInterface
{
    Q_OBJECT
    Q_INTERFACES(QDesignerCustomWidgetInterface)
#if QT_VERSION >= 0x050000
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface")
#endif // QT_VERSION >= 0x050000

public:
    QSwitchButtonPlugin(QObject *parent = 0);

    //true表示部件將用來保存子部件,否則爲false
    bool isContainer() const;
    //如果該部件已被初始化,則返回true;否則返回false
    bool isInitialized() const;
    //Qt Designer的插件箱中小窗口的圖標
    QIcon icon() const;
    //描述了部件的屬性,例如:對象名稱、大小提示,以及其它標準的QWidget屬性的描述。
    QString domXml() const;
    //該控件所屬的組中的Qt Designer的小工具盒
    QString group() const;
    //頭文件必須包含在使用該插件的應用程序的。此信息存儲在UI文件中,並將由UIC創建用於包含自定義插件形式的代碼合適的#includes語句。
    QString includeFile() const;
    //提供了插件的類名稱
    QString name() const;
    //一個簡短的說明,以幫助用戶識別Qt Designer中的部件
    QString toolTip() const;
    //爲Qt Designer用戶設計的部件一個較長的描述
    QString whatsThis() const;
    //一個指向自定義窗口小部件的QWidget指針實例,構建了所提供的父母。注:createWidget()是一個工廠方法,只負責創建小部件的功能。自定義窗口小部件的屬性將不可用,直到load()返回。
    QWidget *createWidget(QWidget *parent);
    //設置了自定義窗口部件擴展等功能
    void initialize(QDesignerFormEditorInterface *core);


private:
    bool m_initialized;
};

#endif

可以看到我們在嚮導中設置的一些信息在相應的函數中體現出來了。

domXml() 函數的注意事項:

domXml()函數返回一個UI文件代碼段,使用Qt Designer窗口工廠創建一個自定義窗口部件和使用特性。從Qt4.4開始,Qt Designer的窗口部件中允許一個完整的UI文件來描述一個自定義窗口部件。UI文件可以使用標籤加載。指定標籤允許添加元素,其中包含自定義窗口部件的其它信息。如果不需要更多的信息,那麼標籤已經足夠了。如果自定義窗口部件不提供合理的尺寸,有必要通過在子類的domXml()函數返回的字符串中指定默認的位置大小(geometry)。

domXml()函數的另一個特點是,如果它返回一個空字符串,部件不會安裝在Qt Designer的窗口部件盒中。然而,它仍然可以被其它形式的部件所使用。這個特性用來隱藏部件,不應由用戶顯式地創建,但需要其它部件創建。

標籤的屬性:

屬性 呈現形式 內容
language 可選項 “c++”,”jambi” 這個屬性指定了自定義窗口部件提供的語言。主要有防止C++插件出現在Qt Jambi中。
displayname 可選項 類名 屬性的值將出現在小工具框,可以用來剝去命名空間。

如一個基本的domxml:

QString QSwitchButtonPlugin::domXml() const
{
    return QLatin1String("<ui displayname = \"Switch Button\"\n<widget class=\"QSwitchButton\" name=\"qSwitchButton\">\n</widget>\n</ui>\n");
}

QSwitchButton實現

直接上代碼

qswitchbutton.h

注意:

  1. 自定義控件類頭文件引入,Qt5.7以下版本爲#include <QtDesigner/QDesignerExportWidget> 以上版本爲#include <QtUiPlugin/QDesignerExportWidget>
  2. 類名前必須加入 QDESIGNER_WIDGET_EXPORT 宏。否則集成到Qt Creator 中編譯會報錯。不加的話可以在設計器中加載,但是編譯會報錯
#ifndef QSWITCHBUTTON_H
#define QSWITCHBUTTON_H

#include <QWidget>
#include <QTimer>
#include <QtUiPlugin/QDesignerExportWidget>
#include <QPainter>
namespace Ui {
class QSwitchButton;
}
class QDESIGNER_WIDGET_EXPORT QSwitchButton : public QWidget
{
    Q_OBJECT

public:
    QSwitchButton(QWidget *parent = 0);
    ~QSwitchButton();
private:
    Ui::QSwitchButton *ui;
public:
    // 返回開關狀態 - 打開:true 關閉:false
    bool isToggled() const;
    // 設置開關狀態
    void setToggle(bool checked);
    // 設置背景顏色
    void setBackgroundColor(QColor color);
    // 設置選中顏色
    void setCheckedColor(QColor bcolor, QColor tcolor);
    // 設置不可用顏色
    void setDisbaledColor(QColor color);
    //設置畫筆
    void setDrawPen(QPen bpen = Qt::NoPen, QPen tpen = Qt::NoPen);
protected:
    // 繪製開關
    void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
    void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    // 大小改變事件
    void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
    // 缺省大小
    QSize sizeHint() const Q_DECL_OVERRIDE;
    QSize minimumSizeHint() const Q_DECL_OVERRIDE;
signals:
    // 狀態改變時,發射信號
    void toggled(bool checked);

private slots:
private:
    bool m_bChecked;         // 是否選中
    QColor m_background;     // 背景顏色
    QColor m_checkedColor;   // 選中顏色
    QColor m_disabledColor;  // 不可用顏色
    QColor m_thumbColor;     // 拇指顏色
    QColor m_thumbcheckedColor;
    qreal m_radius0, m_radius1;          // 圓角
    qint16 m_width, m_height;
    qreal m_nX;              // x點座標
    qreal m_nY;              // y點座標
    qint16 m_nMargin;        // 外邊距
    QPen backPen, thumbPen;
    int direction; //開關方向 ,1水平,-1豎直
    void TogglePosition(void);
};

#endif

qswitchbutton.cpp

#include "qswitchbutton.h"
#include <QMouseEvent>
#include "ui_qswitchbutton.h"
QSwitchButton::QSwitchButton(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::QSwitchButton),
    m_width(16),
    m_height(16),
    m_bChecked(false),
    m_radius0(8.0),
    m_radius1(5.0),
    m_nMargin(3),
    m_checkedColor(75, 216, 99),
    m_thumbcheckedColor(Qt::white),
    m_thumbColor(Qt::white),
    m_disabledColor(190, 190, 190),
    m_background(Qt::gray),
    backPen(Qt::NoPen),
    thumbPen(Qt::NoPen),
    direction(1)
{
    ui->setupUi(this);
    // 鼠標滑過光標形狀 - 手型
    setCursor(Qt::PointingHandCursor);

}

QSwitchButton::~QSwitchButton()
{
    delete ui;
}

bool QSwitchButton::isToggled() const
{
    return m_bChecked;
}

void QSwitchButton::setToggle(bool checked)
{
    m_bChecked = checked;
    TogglePosition();
}

void QSwitchButton::setBackgroundColor(QColor color)
{
    m_background = color;
}

void QSwitchButton::setCheckedColor(QColor bcolor, QColor tcolor)
{
    m_checkedColor = bcolor;
    m_thumbcheckedColor = tcolor;
}

void QSwitchButton::setDisbaledColor(QColor color)
{
    m_disabledColor = color;
}

void QSwitchButton::setDrawPen(QPen bpen, QPen tpen)
{
    backPen = bpen;
    thumbPen = tpen;
}

void QSwitchButton::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);

    QPainter painter(this);
    backPen = QPen(QColor(Qt::black),1);
    painter.setRenderHint(QPainter::Antialiasing);

    QPainterPath path;
    QColor background;
    QColor thumbColor;
    qreal dOpacity;
    if (isEnabled())
    { // 可用狀態
        if (m_bChecked)
        { // 打開狀態
            background = m_checkedColor;
            thumbColor = m_thumbcheckedColor;
            dOpacity = 0.600;
        }
        else
        { //關閉狀態
            background = m_background;
            thumbColor = m_thumbColor;
            dOpacity = 0.800;
        }
    }
    else
    {  // 不可用狀態
        background = m_background;
        dOpacity = 0.260;
        thumbColor = m_disabledColor;
    }

    // 繪製大橢圓
    painter.setPen(backPen);
    painter.setBrush(background);
    painter.setOpacity(dOpacity);
    path.addRoundedRect(QRectF(m_nMargin, m_nMargin, m_width, m_height), m_radius0, m_radius0);
    painter.drawPath(path.simplified());


    // 繪製小橢圓
    painter.setPen(thumbPen);
    painter.setBrush(thumbColor);
    painter.setOpacity(1.0);
    painter.drawEllipse(QRectF(m_nX - m_radius1, m_nY - m_radius1, 2*m_radius1, 2*m_radius1));
}

void QSwitchButton::mouseDoubleClickEvent(QMouseEvent *event)
{
    if (isEnabled())
    {
        if (event->button() == Qt::LeftButton)
        {
            event->accept();
            m_bChecked = !m_bChecked;
            emit toggled(m_bChecked);
            TogglePosition();
        }
        else
        {
            event->ignore();
        }
    }
}

void QSwitchButton::resizeEvent(QResizeEvent *event)
{
    m_width = event->size().width() - 2*m_nMargin;
    m_height = event->size().height() - 2*m_nMargin;
    if(m_width < m_height)
    {
        m_radius0 = 1.0*m_width/2;
        direction = -1;
    }
    else
    {
        m_radius0 = 1.0*m_height/2;
        direction = 1;

    }
    m_radius1 = m_radius0 - m_nMargin;

    TogglePosition();
    QWidget::resizeEvent(event);
}

QSize QSwitchButton::sizeHint() const
{
    return minimumSizeHint();
}

QSize QSwitchButton::minimumSizeHint() const
{
    return QSize(2 * (16 + m_nMargin), 16 + 2 * m_nMargin);
}


void QSwitchButton::TogglePosition(void )
{
    if(m_bChecked)
    {
        if(direction == 1)//水平方向
            {
            m_nX = m_width - m_radius1;
            m_nY = 2*m_nMargin+m_radius1;
        }
        else if(direction == -1)//豎直方向
        {
            m_nX = 2*m_nMargin+m_radius1;
            m_nY = m_height - m_radius1;
        }
        else
        {
            m_nX = 2*m_nMargin+m_radius1;
            m_nY = 2*m_nMargin+m_radius1;
        }

    }
    else
    {
        m_nX = 2*m_nMargin+m_radius1;
        m_nY = 2*m_nMargin+m_radius1;
    }
    update();
}

QSwitchButton的使用

創建一個Qt Widgets Application項目,並將qspinsliderplugin.lib, qspinsliderplugin.dll,qspinsliderplugin.h三個文件複製到當前項目目錄下,在.pro中添加qswitchbuttonplugin庫文件

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/./ -lqswitchbuttonplugin
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/./ -lqswitchbuttonplugin

INCLUDEPATH += $$PWD/.
DEPENDPATH += $$PWD/.

也可以將qspinsliderplugin.h複製到Qt的Include文件夾下,將qspinsliderplugin.lib複製到Qt的Lib文件夾下,將qspinsliderplugin.dll複製到Qt的Bin文件下,如

C:\Qt\Qt5.9.1\5.9.1\msvc2015\include
C:\Qt\Qt5.9.1\5.9.1\msvc2015\lib
C:\Qt\Qt5.9.1\5.9.1\msvc2015\bin

這樣就不必對每個項目分別進行設置了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章