Qt中的测试 枚举与 QFlags详解
傳統的 C++ 編程中,通常使用整數來保存 enum 的邏輯運算結果 (與、或、非、異或等),在進行邏輯運算的時候沒有進行類型檢查,一個枚舉類型可以和其他的枚舉類型進行邏輯運算,運算的結果可以直接傳遞給接收參數為整數的函數。
Qt 中,模板類?QFlags<Enum>?提供了類型安全的方式保存 enum 的邏輯運算結果解決上面的這幾個問題,這種方式在 Qt 里很常見,例如設置 QLabel 對齊方式的函數是?QLabel::setAlignment(Qt::Alignment)?(typedef QFlags<Qt::AlignmentFlag> Qt::Alignment),這就意味著傳給 setAlignment 的參數只能是枚舉?Qt::AlignmentFlag?的變量、它們的邏輯運算結果或者 0,如果傳入其他的枚舉類型或者非 0 值,編譯時就會報錯:
label->setAlignment(0); label->setAlignment(Qt::AlignLeft | Qt::AlignTop); label->setAlignment(Qt::WA_Hover); // Error: 編譯時報錯想要把我們定義的枚舉類型和 QFlags 一起使用,需要用到兩個宏:
- Q_DECLARE_FLAGS(Flags, Enum):
- Enum 是已經定義好的枚舉類型
- 展開的結果為?typedef QFlags<Enum> Flags
- Q_DECLARE_OPERATORS_FOR_FLAGS(Flags):
- Flags 就是類型?QFlags<Enum>
- 給 Flags 定義了運算符?|,使得 Enum 和 Enum,Enum 和 Flags 能夠使用或運算符?|,結果為 Flags
使用 QFlags 時需要留意以下幾點:
- QFlags 其實就是用于位操作,設置它保存的數值的某一位為 1 或者為 0,所以和 QFlags 一起使用的枚舉類型,其變量的值需要是 2 的 n 次方,即 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, …,它們的特點是其二進制位中只有一位是 1,其他位全部為 0,這樣每一個枚舉值只會和 QFlags 中的某一個位對應,不會出現交叉的情況
- 調用函數?QFlags::setFlag(Enum flag, bool on = true),on 為 true 時設置 flag 對應的位為 1,on 為 false 時設置 flag 對應的位為 0,設置 flag 對應的位為 1 還可以使用運算符?|
- 調用函數?QFlags::testFlag(Enum flag)?測試 flag 對應的位是否為 1
- 整數轉為 QFlags: 把整數作為 QFlags 構造函數的參數創建一個 QFlags 變量
- QFlags 轉為整數: 調用?int(flags)?把 QFlags 變量轉換為整數值
下面就演示一下怎么使用 QFlags:
// Flag 的變量值是 2^n, 每個值的二進制只有一個位是 1,其他全為 0 enum class Flag {Js = 0x01, // 1 : 0000 0001Go = 0x02, // 2 : 0000 0010Cpp = 0x04, // 4 : 0000 0100Php = 0x08, // 8 : 0000 1000Java = 0x10, // 16 : 0001 0000Scala = 0x20, // 32 : 0010 0000}; Q_DECLARE_FLAGS(Flags, Flag) Q_DECLARE_OPERATORS_FOR_FLAGS(Flags) // 使得 Flag 和 Flag,Flag 和 Flags 能夠使用或運算符 |,結果為 Flagsint main() {Flags flags(0x08); // int to Flagsflags |= Flag::Js; // 添加 Jsflags |= Flag::Go; // 添加 Goflags.setFlag(Flag::Cpp, true); // 添加 Cppflags.setFlag(Flag::Cpp, false); // 刪除 CppqDebug() << flags; // 輸出: QFlags(0x1|0x2|0x8)qDebug() << flags.testFlag(Flag::Js); // 輸出: trueqDebug() << flags.testFlag(Flag::Cpp); // 輸出: falseqDebug() << int(flags); // 輸出: 11 (Flags to int)flags = Flag::Java | Flag::Scala; // 使用 | 同時設置多個 flagqDebug() << flags; // 輸出: QFlags(0x10|0x20)// flags = Flag::Java | Qt::AlignLeft; // Error: no viable overloaded =return 0; }在日常開發中,權限也可以使用 QFlags 來實現,下面只演示 3 種權限的使用,實際開發中根據具體業務需求修改枚舉類型 Permission 的變量即可:
#include <QMap> #include <QStringList>// 權限 enum class Permission {Readable = 0x01,Writable = 0x02,Excutable = 0x04, };Q_DECLARE_FLAGS(Permissions, Permission) Q_DECLARE_OPERATORS_FOR_FLAGS(Permissions)// 權限的值和其對應的 label 的 map QMap<Permission, QString> PermissionLabelMap {{ Permission::Readable, "可讀" },{ Permission::Writable, "可寫" },{ Permission::Excutable, "可執行" }, };// 用戶 class User { public:User(const QString &username, int psValue) {this->username = username;this->ps = Permissions(psValue);}bool hasPermission(Permission p) const {return ps.testFlag(p);}void addPermission(Permission p) {ps.setFlag(p, true);}void addPermissions(Permissions ps) {this->ps |= ps;}void removePermission(Permission p) {ps.setFlag(p, false);}void removePermissions(Permissions ps) {this->ps &= Permissions(~(int(ps)));}Permissions getPermissions() const {return ps;}QStringList getPermissionLabels() const {QStringList labels;for (auto i = PermissionLabelMap.cbegin(); i != PermissionLabelMap.cend(); ++i) {if (ps.testFlag(i.key())) {labels << i.value();}}return labels;}private:Permissions ps;QString username; };int main() {User user("Bob", 0);user.addPermissions(Permission::Readable | Permission::Excutable);qDebug() << user.getPermissions(); // 輸出: QFlags(0x1|0x4)qDebug() << user.getPermissionLabels(); // 輸出: ("可讀", "可執行")qDebug() << user.hasPermission(Permission::Readable); // 輸出: trueqDebug() << user.hasPermission(Permission::Writable); // 輸出: falseuser.addPermission(Permission::Writable);qDebug() << user.getPermissions(); // 輸出: QFlags(0x1|0x2|0x4)qDebug() << int(user.getPermissions()); // 輸出: 7user.removePermissions(Permission::Readable | Permission::Excutable);qDebug() << user.getPermissionLabels(); // 輸出: ("可寫")return 0; }又例如:
class CComGeneralTableWidget : public QTableWidget {Q_OBJECTpublic:CComGeneralTableWidget(QWidget *parent = Q_NULLPTR);~CComGeneralTableWidget();/* 顯示屬性枚舉(注意:id、設計id、可見性、顯示名是所有元件都有且必須在屬性對話框中顯示的,這里就不再列出了,這里僅僅是列出有些元件需要顯示的,有些元件不需要顯示的,通過枚舉值來控制哪些顯示,哪些不顯示),枚舉值必須為0、2的N次方(N >= 0)*/enum class EmShowProp{eNotShowAllProp = 0, // 畫筆顏色、畫筆寬度、畫刷顏色、線型、旋轉信息都不顯示eShowRotateProp = 1, // 顯示旋轉信息eShowPenColorProp = 2, // 顯示畫筆顏色eShowPenWidthProp = 4, // 顯示畫筆寬度eShowBrushColorProp = 8, // 顯示畫刷顏色eShowLineTypeProp = 16, // 顯示線型// 顯示所有eShowAllProp = eShowRotateProp | eShowPenColorProp | eShowPenWidthProp | eShowBrushColorProp | eShowLineTypeProp};Q_DECLARE_FLAGS(EmShowProps, EmShowProp)....// 類的其它代碼 private:EmShowProps m_eShowProp{ EmShowProp::eShowAllProp }; };Q_DECLARE_OPERATORS_FOR_FLAGS(CComGeneralTableWidget::EmShowProps)注意:通過上述Q_DECLARE_FLAGS聲明后, 定義枚舉不再是EmShowProp(最原始的用enum聲明的、宏的第二個參數)而是?EmShowProps(宏的第一個參數),如果還用enum聲明的枚舉名定義枚舉變量,則會報如下錯誤:
error C2228: “.testFlag”的左邊必須有類/結構/聯合總結
以上是生活随笔為你收集整理的Qt中的测试 枚举与 QFlags详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 好用的思维导图推荐:百度思维脑图及使用方
- 下一篇: QTablewidget只显示横分割线,