概述
在使用Qt創建用戶界面時,特別是那些具有特殊控件和特性的界面時,開發人員有時需要創建新的數據類型,以便與Qt現有的值類型集一起使用或代替它們。
QSize、QColor和QString等標準類型都可以存儲在QVariant對象中,作為基于qobject的類的屬性類型,并在信號槽通信中發出。
在本文中,我們將使用一個自定義類型,并描述如何將其集成到Qt的對象模型中,以便它能夠以與標準Qt類型相同的方式存儲。然后,我們將展示如何注冊自定義類型以允許它在信號和插槽連接中使用。
創建自定義類型
在開始之前,我們需要確保創建的自定義類型滿足QMetaType的所有要求。換句話說,它必須提供:
- 一個公共的默認構造函數,
- 一個公共復制構造函數,以及
- 一個公共析構函數。
下面的Message類定義包含了這些成員:
class Message
{
public:Message() = default;~Message() = default;Message(const Message &) = default;Message &operator=(const Message &) = default;Message(const QString &body, const QStringList &headers);QString body() const;QStringList headers() const;private:QString m_body;QStringList m_headers;
};
這個類還提供了一個普通使用的構造函數,以及兩個用于獲取私有數據的公共成員函數。
用QMetaType聲明類型
Message類只需要適當的實現就可以使用。然而,Qt的類型系統將無法理解如何存儲,檢索和序列化這個類的實例,如果沒有一些幫助。例如,我們將無法在QVariant中存儲消息值。
Qt中負責自定義類型的類是QMetaType。為了讓這個類知道類型,我們在定義類的頭文件中調用Q_DECLARE_METATYPE()宏:
Q_DECLARE_METATYPE(Message);
這使得將消息值存儲在QVariant對象中并在以后檢索成為可能。有關演示這一點的代碼,請參閱自定義類型示例。
Q_DECLARE_METATYPE()宏也可以將這些值用作信號的參數,但只能在直接的信號槽連接中使用。為了使自定義類型通常可用于信號和插槽機制,我們需要執行一些額外的工作。
創建和銷毀自定義對象
雖然前一節中的聲明使該類型可用于直接的信號槽連接,但不能用于排隊的信號槽連接,例如不同線程中的對象之間的連接。這是因為元對象系統不知道如何在運行時處理自定義類型對象的創建和銷毀。
要在運行時創建對象,請調用qRegisterMetaType()模板函數將其注冊到元對象系統。這也使得該類型可用于排隊信號槽通信,只要您在創建第一個使用該類型的連接之前調用它。
排隊的自定義類型示例聲明了一個注冊在main.cpp文件中的塊類:
int main(int argc, char *argv[])
{QApplication app(argc, argv);...qRegisterMetaType<Block>();...return app.exec();
}
此類型稍后在window.cpp文件中的信號槽連接中使用:
Window::Window(QWidget *parent): QWidget(parent), thread(new RenderThread(this))
{...connect(thread, &RenderThread::sendBlock,this, &Window::addBlock);...setWindowTitle(tr("Queued Custom Type"));
}
如果在未注冊的情況下在排隊連接中使用了類型,則將在控制臺中打印警告;例如:
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);
自定義類型示例中Message類型的實現做了一些努力,使可打印的表示盡可能可讀:
QDebug operator<<(QDebug dbg, const Message &message)
{const QString body = message.body();QVector<QStringRef> pieces = body.splitRef(QLatin1String("\r\n"), Qt::SkipEmptyParts);if (pieces.isEmpty())dbg.nospace() << "Message()";else if (pieces.size() == 1)dbg.nospace() << "Message(" << pieces.first() << ")";elsedbg.nospace() << "Message(" << pieces.first() << " ...)";return dbg.maybeSpace();
}
當然,發送到調試流的輸出可以按照您的喜好設置為簡單或復雜。請注意,該函數返回的值是QDebug對象本身,盡管這通常是通過調用QDebug的maybeSpace()成員函數獲得的,該函數用空格字符填充流,使其更具可讀性。
Creating Custom Qt Types | Qt Core 5.15.17