Qt中鏈接信號和槽的方法
最近又在用Qt寫東西,順便翻了翻Qt的文檔,發現鏈接信號和槽的方法有不少,Qt5和C++11的組合也帶來了一些不小變化。
所以有此一篇文章,對其稍加總結。
記日前硬盤故障
先描述一下我的計算機概況:
500G硬盤,前大約100G是OEM的Windows7及其預置恢復分區(sda1-sda3),後邊全給了linux用。除了/boot,文件系統格式全是ext4,/home(sda6)有200G+。
發行版是arch,引導器是grub2。
某日,需要進Windows做點事,於是重啓電腦。到了引導項選擇那裏,由於我設置的是隱藏菜單(默認進arch),故而需要按ESC使菜單顯示出來。然而,我手快了些,在5s的倒計時開始前就按了ESC。然後只見硬盤持續亮着,引導菜單沒有出現。等了一會,實在不耐煩,於是長按電源,再開。這次刻意等到讀秒開始之後再按的ESC,引導項選擇也出現了,選擇Windows,確認。Windows的啓動用了很長時間,輸入密碼之後那個“歡迎”也持續旋轉很久。我以爲是上次安裝了一些更新所致,也沒有在意,之後使用沒什麼奇怪現象。
等到處理完,重啓進Arch時候,看到問題了:一直在報sda6一些塊讀取錯誤。看到這裏,大概判斷是硬盤有了壞塊,於是長按電源鍵,關掉了計算機。再次啓動時候是用U盤引導(上次給人裝Arch時候直接把鏡像dd到U盤),繼續看到sda6某某塊讀取出錯,不過這次倒是進了live系統。
然後用badblocks檢測,發現還真是有,主要分佈在sda6上,sda7也有少量。這下就令人着急了——數據可是無價的。幸好手上有個1T且只裝了一半的移動硬盤,可以將數據轉入。
然而,嘗試只讀掛載sda6的時候,提示superblock讀取失敗,拒絕掛載。上網一查,發現ext4會創建備份的superblock,於是按照網上說的,掛載時加上-o sb=xxx參數指定副superblock的塊號。這裏我man了一下mount,發現這個參數是以1K爲標準的。我創建的文件系統塊大小是4K,所以第32768塊的備份superblock應該用-o sb=131072參數。
這樣,連起來的掛載命令就是
mount -r -o sb=131072 -t ext4 /dev/sda6 /mnt/sda6
滿懷期待得讓命令執行,卻再次得到信息說“掛載失敗,可能是錯誤的文件系統類型”。看到這點時候真的有些慌,因爲我確認它是ext4,而掛載時候卻告訴我失敗,莫非數據真的無法挽救?之後又嘗試用其他的副superblock,結果卻沒有什麼變化。
這時候我抱着試試看的心態敲了一下dmesg,在最下邊看到journal讀取失敗,於是拒絕掛載。心情一下清平了,再掛載時候加上norecovery參數,掛載成功,備份數據。
最終的命令如下:
mount -r -o sb=131072,norecovery -t ext4 /dev/sda6 /mnt/sda6
慘痛的經歷再次說明經常備份數據的重要性。
以及,若有遇到類似情況的,望可告知產生原因是什麼……
(某想不通究竟是因爲斷電導致的還是確實是grub的bug……更不想再試試……)
自記初習Qt
百無聊賴,於是就拿出Qt Creator來學一下Qt(C++)。若有錯誤,還望指正。
Qt使用標準的C++,只是加入了預處理器(moc)以及一些宏。這就意味着可以使用任意的C++編譯器來編譯Qt程序。
文件
Qt圖形界面程序一般有三個文件,通常狀態下後綴名爲:.ui、.h、.cpp。
.ui文件是界面的設計文件,本質是xml文件。在構建時會依據其產生一些源文件以進行編譯。
.h文件是標準的c++頭文件,內部包含各類聲明等內容。需要特別注意的是,如果要使用Qt的信號/槽機制,需要在類聲明代碼塊首部分加入Q_OBJECT宏。
.cpp文件顯然是普通的C++程序源代碼。
從預設的組織結構看,Qt中的.h文件中僅僅寫聲明,定義在相應的.cpp文件中書寫。這點看起來比微軟的VS中那些亂七八糟的強許多。
然而打開進行編譯的臨時目錄,其中依據.ui文件自動生成的.h文件中卻包含了一些定義。不過這似乎是有意爲之,而且沒有直接暴露在編程者面前,也算是很不錯的做法。
信號/槽
相傳Qt最引以爲傲的就是它的“信號/槽”機制。該機制是一個解耦程度極高的組件通信方案,允許組件間在互不相知的狀態下進行通信。
如名稱所示,“信號”表示發送消息的部分,“槽”表示接收消息的部分。當一個信號發出時,其相關聯的槽會被立即(實際上仍有極其微小的延遲)調用。
信號或者槽連同它的參數類型表,一起被稱爲其“簽名”,意即其標識。Qt通過簽名類辨識信號或槽,也通過簽名來判斷一個信號是否能和槽相關聯。事實上,一個信號可和多個槽對應,一個槽也可和多個信號對應,甚至信號也可以引發另一個信號,這也就爲編程帶來了極大的靈活性。
爲了便於編程,Qt已對其組件(例如PushButton)定義好了一些信號(例如“按下”),編程時可直接使用。
信號
信號是一個不需要定義的函數。它應當在類中聲明,並在代碼中被引發。
聲明信號,只需在類聲明中寫入
signals: void 信號名( 參數類型表 );
槽
除了聲明方法不同以及槽可以用於信號/槽機制通信外,槽的一切均和普通函數一致。
聲明槽,應當在類聲明中加入:
訪問方式 slots: 返回值類型 槽名( 參數表 );
之後在相應的.cpp文件中定義即可。
其中訪問方式可爲public、protected、private中任一種,並會導致槽的作用域不同(和C++標準概念中一致)。
關聯信號和槽
關聯信號和槽的通常做法是
connect(發送者, SIGNAL(信號(參數類型表)), 接受者/*即槽所在*/, SLOT(槽(參數類型表)));
其中SIGNAL和SLOT均爲宏,其參數中實際上要寫的是信號或者槽的簽名。之前說過,我們可以將一個信號和另一個信號關聯起來,這只需將上面的槽換成信號即可。
其中信號的參數可以比槽的多,多餘的部分在槽被激活(調用)時會被忽略掉。
實際上,關聯信號與槽的方法還有一些變體,具體可參考我的這篇文章。
解除信號和槽的關聯
解除信號和槽的關聯需要用到disconnect函數,其使用方法和connect幾乎一致:
disconnect(發送者, SIGNAL(信號(參數類型表)), 接受者, SLOT(槽(參數類型表)));
實際上,該函數中除了發送者外,其餘部分均可以填“0”,表示“任意”。
手動發送信號
有時候Qt自帶的信號無法滿足我們的需求,這時候我們就需要知道如何手動發送自己定義的信號。
手動發送信號,只需要在想要發送信號的地方使用emit關鍵字發送你的信號:
emit 信號( 參數 );
實際上,emit是一個宏,並且其內容爲空。這也就意味着完全可以不使用emit來發送一個信號。但是個人推薦使用emit來明確標識這是一個信號。
RQNOJ-43·飞翔·乱解
昨天有人问我RQNOJ的第43题,,,就是那道 飞翔 ,当时没思路,晚上回家的时候想到了方案,今天早上给打了出来,A掉了……//其实我今天早上打的跟昨晚想的不太一样,我昨晚想的那个是个的做法,,不过早上发现更优方案了