摘要: 簡述 當使用Qt創建用戶界面時,特別是那些帶有特殊控制和特征的界面時,開發者通常需要創建新數據類型來擴展或替換Qt現有的的值類型集合。 標准類型,比如:QSize、QColor和QString都可以被存儲到QVariant對象中,在基於QObject的類中可用作屬性的類型,並且可以在信號-槽通信時發射。 下面,我會創建一個自定義類型,並且說明如何將它集成到Qt的對象模型
簡述
當使用Qt創建用戶界面時,特別是那些帶有特殊控制和特征的界面時,開發者通常需要創建新數據類型來擴展或替換Qt現有的的值類型集合。
標准類型,比如:QSize、QColor和QString都可以被存儲到QVariant對象中,在基於QObject的類中可用作屬性的類型,並且可以在信號-槽通信時發射。
下面,我會創建一個自定義類型,並且說明如何將它集成到Qt的對象模型中,以便能夠以與其他Qt標准類型相同的方式被存儲。接着會展示如何注冊自定義類型,使其可以在信號槽的連接中使用。
創建一個自定義類型
在開始之前,需要確保創建的這個自定義類型符合QMetaType的規定的所有要求。換句話說,它必須提供:
- 一個公有的默認構造函數
- 一個公有的拷貝構造函數
- 一個公有的析構函數
下面的Message類的定義包含了這些成員:
class Message
{
public: Message(); Message(const Message &other); ~Message(); Message(const QString &body, const QStringList &headers); QString body() const; QStringList headers() const; private: QString m_body; QStringList m_headers; };
這個類同時還提供了一個經常使用的構造函數,以及兩個用於獲取私有數據的共有成員函數。
使用QMetaType聲明類型
Message類僅需要一個合適的實現,以便可以使用。然而,如果沒有其他輔助信息,Qt類型系統將無法理解如何存儲、檢索和序列化該類的實例。例如:我們無法將Message的值保存到QVariant中。
Qt中負責自定義類型的類是QMetaType。為了讓這個類識別該類型,當定義這個類時,需要頭文件中使用Q_DECLARE_METATYPE()宏:
Q_DECLARE_METATYPE(Message);
這樣,就可以將Message的值保存在QVariant對象中,並在以后讀取。完整代碼可參見Custom Type Example中的示范代碼。
所述Q_DECLARE_METATYPE()宏同時也使得這些值可以被用作信號的參數,但是僅限於direct信號槽連接。為了能在信號槽機制中使用自定義類型,我們需要做一些另外的工作。
創建和銷毀自定義對象
雖然上面部分中的聲明使類型可以在direct信號槽連接中使用,但是無法用於queued信號槽連接中,例如:在不同線程的對象之間所建立的連接。這是因為元對象系統不知道如何在運行時處理自定義類型對象的創建和銷毀操作。
為了可以在運行時創建對象,需要調用qRegisterMetaType()模板函數在元對象系統中注冊此類型。只要在使用此類型的第一次連接建立前調用注冊函數,該類型可被用於queued信號槽連接。
Queued Custom Type Example示例中在main.cpp文件中聲明了一個Block類:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
... qRegisterMetaType<Block>(); ... return app.exec(); }
這個類型后來在文件window.cpp中被用於一個信號-槽連接:
Window::Window()
{
thread = new RenderThread();
... connect(thread, SIGNAL(sendBlock(Block)), this, SLOT(addBlock(Block))); ... setWindowTitle(tr("Queued Custom Type")); }
如果一個沒有被注冊的類型被用於queued連接中,在控制台中會輸出一條警告信息。例如:
QObject::connect: Cannot queue arguments of type ‘Block’ (Make sure ‘Block’ is registered using qRegisterMetaType().)
使類型可打印
出於調試目的,使一個自定義類型可打印是非常有用的,就像下面的代碼一樣:
Message message(body, headers); qDebug() << "Original:" << message;
可以通過為此類型創建流操作符來達到目的,這通常定義在該類型的頭文件中:
QDebug operator<<(QDebug dbg, const Message &message);
在Custom Type Example的Message類實現中,我們努力讓可打印的內容盡可能的通俗易讀:
QDebug operator<<(QDebug dbg, const Message &message) { const QString body = message.body(); QVector<QStringRef> pieces = body.splitRef("\r\n", QString::SkipEmptyParts); if (pieces.isEmpty()) dbg.nospace() << "Message()"; else if (pieces.size() == 1) dbg.nospace() << "Message(" << pieces.first() << ")"; else dbg.nospace() << "Message(" << pieces.first() << " ...)"; return dbg.maybeSpace(); }
當然,輸出到debug流的信息是簡單還是復雜都隨你的意思。需要注意的是這個函數的返回值是QDebug對象本身,盡管它通常通過調用QDebug的成員函數maybeSpace()來獲得,用空白字符填充流,以使其更具可讀性。
轉自:https://yq.aliyun.com/articles/62082