最近在研究QT,并嘗試寫了一個小程序。程序的功能很簡單,就是進行一些文件的操作:
假如說我有兩個文件,分別是文件A,文件B。其中文件A中的前n個字節的數據都為0,這樣的文件A我把它視為一個“壞文件”。我要做的就是在文件B中找到A的值為0的數據所對應的數據,并用它替換掉A的“壞數據”。為了保護現場,我將替換好的A保存為文件C。
之所以開發這個程序,是因為最近在下載電視劇的時候,常常發現出現“渲染失敗”而不能播放的文件。而補救方法之一就是用一個好的文件替換那個壞了的文件的頭n個值為0的字節數據。這個目的現在已經達到了(至少我已經拿他修復了幾集電視劇)。
對文件的操作,我使用的是STL中的“流”,然后GUI就使用了現在那個喊得很響的跨平臺的開源c++項目--QT。
首先是搭建環境。其實也挺簡單的,現在大家都喜歡DEV-C++和Qt4.2結合使用嗎,我這里也搭建了一個同樣的環境。使用Qt+Dev cpp環境配置這篇隨筆中提到了那個老外的模板,拷貝到DEV-C++目錄下的Template目錄下,就可以了。然后打開開發環境,就可以創建QT項目了。當然如果實在是喜歡用記事本開發,也可以完成代碼之后,用命令行編譯、鏈接程序,在幫助文檔里面說的很清楚,在你的src目錄下依次執行
qmake -project
qmake
make
就可以了。當然更深入的情況下,往往需要對生成的makefile做點手腳。
現在回到DEV-C++。打開程序,新建項目
然后確定,創建項目,就生成了我的qt項目。因為我的qt程序中還需要對于QT3的支持,所以需要在編譯命令中添加對QT3的支持。如下:
-O2 -O2 -frtti -fexceptions -Wall -DUNICODE -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_THREAD_SUPPORT -DQT_NEEDS_QMAIN -I"G:/Qt/4.3.0/include/QtGui" -I"G:/Qt/4.3.0/include/QtCore" -I"G:/Qt/4.3.0/include" -I"." -I"G:/Qt/4.3.0/include/Qt3Support" -I"G:/Qt/4.3.0/include/ActiveQt" -I"tmp\moc\release_shared" -I"." -I"G:\Qt\4.3.0\mkspecs\win32-g++"
注意黑體部分就是新添加的。當然也可以修改一下模板,讓以后的所有程序都具備對于QT3的支持。
不光要修改編譯指令,還需要修改鏈接指令:
-mthreads -Wl,-enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -Wl, -Wl, -Wl,-subsystem,windows -L"G:\Qt\4.3.0\lib" -L"G:\Qt\4.3.0\lib" -lmingw32 -lqtmain -lQtCore4 -lQtGui4 -lQt3Support4
好了,這樣就提供了QT3的支持。
首先,我需要創建文件選擇窗口,在這里,我創建兩個打開文件窗口和一個保存文件窗口:
1 QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
2 QApplication app(argc, argv);
3 QWidget w;
4 QString sErrorFile = Q3FileDialog::getOpenFileName(
5 "/",
6 "RMVB (*.rmvb)",
7 &w,
8 "open file dialog",
9 "選擇要修復的文件");
10 QString sTemplateFile=Q3FileDialog::getOpenFileName(
11 "/",
12 "RMVB (*.rmvb)",
13 &w,
14 "open file dialog",
15 "選擇參照文件");
16 QString sOutputFile=Q3FileDialog::getSaveFileName(
17 "/",
18 "RMVB (*.rmvb)",
19 &w,
20 "open file dialog",
21 "選擇輸出文件");
注意第一句讓我的程序能夠支持中文。
而要使用Q3FileDialog,則要添加Q3支持。
對于文件操作類,這里就不詳細列出代碼了.類圖如下:

因為文件操作是一個比較耗費資源的操作,所以這里我把它放到一個線程里面去。我創建了一個類MainOperation,讓它從QThread繼承,并處理有關文件的操作:
1 class MainOperation:public QThread
2 {
3 Q_OBJECT
4 public:
5 MainOperation(QString errorFile,QString templateFile,QString outputFile)
6 {
7
8 _errorFile=copyQStringToCharArray(errorFile);
9 _templateFile=copyQStringToCharArray(templateFile);
10 _outputFile=copyQStringToCharArray(outputFile);
11
12 }
13 ~MainOperation()
14 {
15 delete[] _errorFile;
16 delete[] _templateFile;
17 delete[] _outputFile;
18 }
19 protected:
20 void run()
21 {
22 FileWriter fw1(_errorFile);
23 FileReviser fr(_templateFile);
24 fw1.AppendReviser(&fr);
25 emit fileSizedRecognized(fw1.GetFileSize());
26 while(!fw1.IsOK())
27 {
28 fw1.Save(_outputFile);
29 emit posChanged(fw1.GetPos());
30 }
31 emit finished();
32 }
33 signals:
34 void fileSizedRecognized(int fileSize);
35 void posChanged(int pos);
36 void finished();
37
38 private:
39 char * copyQStringToCharArray(QString qstr)
40 {
41 QByteArray array=qstr.toAscii ();
42 char* resultChar=new char[strlen(array.data())];
43 strcpy(resultChar,array.data());
44 return resultChar;
45 }
46 char* _errorFile;
47 char* _templateFile;
48 char* _outputFile;
49
50 };
因為一直在搞c#和java的開發,所以最近寫出來的c++代碼都是inline的,這確實不符合c++的標準編程習慣。
注意這里不光從QThread繼承,還寫了這樣一個類似宏的東西:Q_OBJECT。其實它還是一個Meta-Object標記。如果這樣編譯程序的話,編譯器會告訴你連接錯誤。這時候你需要通過命令moc來創建另外一個類,并把這個類引入你的項目,讓你的程序加入這個新類的頭文件定義。就像這樣(類MainOperation被放在文件MainObject中):
meta MainObject.h -o moc_MainObject.h
然后在main.cpp 里面加上:
#include "moc_MainObject.h"
除去#include "MainObject.h"
現在開始設計界面了。
打開Designer

保存設計好的文件,存好的文件是一個后綴為ui的文件。這里的文件名是Repairing.ui.
然后通過uic命令把Repairing.ui轉變為頭文件Repairing.h。
uic Repairing.ui -o Repairing.h
這樣就生成了頭文件。引入它。這個頭文件看上去是這樣的:
1 class Ui_Form:public QObject
2 {
3 public:
4 QProgressBar *progressBar;
5 QPushButton *pushButton_end;
6 QLabel *label;
7 QLabel *label_ErrorFile;
8 QLabel *label_3;
9 QLabel *label_TemplateFile;
10 QLabel *label_5;
11 QLabel *label_OutputFile;
12 QPushButton *pushButton_Begin;
13
14 void setupUi(QWidget *Form)
15 {
16 if (Form->objectName().isEmpty())
17 Form->setObjectName(QString::fromUtf8("Form"));
18 QSize size(346, 146);
19 size = size.expandedTo(Form->minimumSizeHint());
20 Form->resize(size);
21 Form->setContextMenuPolicy(Qt::NoContextMenu);
22 Form->setWindowIcon(QIcon(QString::fromUtf8("D:/my pic/\347\273\217\345\205\270\345\233\276\346\240\207/\347\273\217\345\205\270\346\260\264\346\231\266\345\233\276\346\240\207/OS/OS14.jpg")));
23 progressBar = new QProgressBar(Form);
24 progressBar->setObjectName(QString::fromUtf8("progressBar"));
25 progressBar->setGeometry(QRect(40, 90, 281, 23));
26 //progressBar->setValue(0);
27 pushButton_end = new QPushButton(Form);
28 pushButton_end->setObjectName(QString::fromUtf8("pushButton_end"));
29 pushButton_end->setEnabled(true);
30 pushButton_end->setGeometry(QRect(180, 120, 75, 23));
31 label = new QLabel(Form);
32 label->setObjectName(QString::fromUtf8("label"));
33 label->setGeometry(QRect(40, 20, 61, 16));
34 label_ErrorFile = new QLabel(Form);
35 label_ErrorFile->setObjectName(QString::fromUtf8("label_ErrorFile"));
36 label_ErrorFile->setGeometry(QRect(100, 20, 161, 16));
37 label_3 = new QLabel(Form);
38 label_3->setObjectName(QString::fromUtf8("label_3"));
39 label_3->setGeometry(QRect(40, 40, 54, 14));
40 label_TemplateFile = new QLabel(Form);
41 label_TemplateFile->setObjectName(QString::fromUtf8("label_TemplateFile"));
42 label_TemplateFile->setGeometry(QRect(100, 40, 161, 16));
43 label_5 = new QLabel(Form);
44 label_5->setObjectName(QString::fromUtf8("label_5"));
45 label_5->setGeometry(QRect(40, 60, 54, 14));
46 label_OutputFile = new QLabel(Form);
47 label_OutputFile->setObjectName(QString::fromUtf8("label_OutputFile"));
48 label_OutputFile->setGeometry(QRect(100, 60, 161, 16));
49 pushButton_Begin = new QPushButton(Form);
50 pushButton_Begin->setObjectName(QString::fromUtf8("pushButton_Begin"));
51 pushButton_Begin->setGeometry(QRect(80, 120, 75, 23));
52
53 retranslateUi(Form);
54
55 QMetaObject::connectSlotsByName(Form);
56 } // setupUi
57
58 void retranslateUi(QWidget *Form)
59 {
60 Form->setWindowTitle(QApplication::translate("Form", "\344\277\256\345\244\215", 0, QApplication::UnicodeUTF8));
61 pushButton_end->setText(QApplication::translate("Form", "\351\200\200\345\207\272", 0, QApplication::UnicodeUTF8));
62 label->setText(QApplication::translate("Form", "\345\274\202\345\270\270\346\226\207\344\273\266", 0, QApplication::UnicodeUTF8));
63 label_ErrorFile->setText(QString());
64 label_3->setText(QApplication::translate("Form", "\345\217\202\347\205\247\346\226\207\344\273\266", 0, QApplication::UnicodeUTF8));
65 label_TemplateFile->setText(QString());
66 label_5->setText(QApplication::translate("Form", "\350\276\223\345\207\272\346\226\207\344\273\266", 0, QApplication::UnicodeUTF8));
67 label_OutputFile->setText(QString());
68 pushButton_Begin->setText(QApplication::translate("Form", "\345\274\200\345\247\213", 0, QApplication::UnicodeUTF8));
69 Q_UNUSED(Form);
70 } // retranslateUi
71
72 };
73 namespace Ui {
74 class Form: public Ui_Form
75 {
76
77 };
78 } // namespace Ui
整個調用過程是這樣的。下圖有不完善的地方。實際上更改進度條狀態的動作在處理文件時不停的發出。

這里我不直接讓UI去調用MainOperation的方法,也不讓MainOperation直接回調。這里采用Qt的信號/插槽機制:
在MainOperation中:
1 signals:
2 void fileSizedRecognized(int fileSize);
3 void posChanged(int pos);
4 void finished();
這三個信號映射到Form的三個Slot:
1 public slots:
2 void prepareFile(int fileSize)
3 {
4 pushButton_Begin->setEnabled(false);
5 pushButton_end->setEnabled(false);
6 _fileSize=fileSize;
7 progressBar->setMinimum(0);
8 progressBar->setMaximum(_fileSize);
9 }
10 void setProgressBarPos(int pos)
11 {
12 emit ProgressbarPositionChanged(pos);
13 }
14 void finish()
15 {
16 pushButton_end->setEnabled(true);
17 }
而Form的信號ProgressbarPositionChanged又被映射到Progressbar的setvalue的Slot。這個插接動作在main.cpp里面完成如:
1 Ui::Form ui;
2 ui.setupUi(&w);
3 ui.label_ErrorFile->setText(sErrorFile);
4 ui.label_TemplateFile->setText(sTemplateFile);
5 ui.label_OutputFile->setText(sOutputFile);
6 MainOperation mainOp(sErrorFile,sTemplateFile,sOutputFile);
7 QObject::connect(ui.pushButton_Begin,SIGNAL(clicked()),&mainOp,SLOT(start()));
8 QObject::connect(&mainOp,SIGNAL(fileSizedRecognized(int)),&ui,SLOT(prepareFile(int)));
9 QObject::connect(&mainOp,SIGNAL(posChanged(int)),&ui,SLOT(setProgressBarPos(int)));
10 QObject::connect(&ui,SIGNAL(ProgressbarPositionChanged(int)),ui.progressBar,SLOT(setValue(int)));
11 QObject::connect(&mainOp,SIGNAL(finished()),&ui,SLOT(finish()));
12 QObject::connect(ui.pushButton_end, SIGNAL(clicked()), &app, SLOT(quit()));
因為在Repairing.h中使用了自定義的Signal/Slot,即:
1 class Form: public Ui_Form
2 {
3 Q_OBJECT
4 private:
5 int _fileSize;
6 QMutex mutex;
7 public slots:
8 void prepareFile(int fileSize)
9 {
10 pushButton_Begin->setEnabled(false);
11 pushButton_end->setEnabled(false);
13 _fileSize=fileSize;
14 progressBar->setMinimum(0);
15 progressBar->setMaximum(_fileSize);
17 }
18 void setProgressBarPos(int pos)
19 {
20 emit ProgressbarPositionChanged(pos);
31 }
32 void finish()
33 {
34 pushButton_end->setEnabled(true);
35 }
37 signals:
38 void ProgressbarPositionChanged(int pos);
39 };
所以仍然使用moc導出另外一個.h文件:
moc Repairing.h -o moc_Repairing.h
在main.cpp中include這個文件,除去對Repairing.h文件的包含。這樣整個程序就算完成了。
注意為什么這里Form與ProgressBar之間仍然要使用Signal/Slot呢?因為在多線程操作里面,不能夠直接使用setValue更改ProgressBar的進度條位置。這個問題曾經困擾了我好幾天。
代碼
posted on 2007-07-31 23:33
littlegai 閱讀(3552)
評論(1) 編輯 收藏 引用 所屬分類:
我的代碼玩具