《C++ GUI Qt 4 編程》 筆記(四)

博客搬家自 http://zhouyuanchao.com/wordpress/archives/71


第12章 輸入與輸出

QIODevice 基類

QFile
QTemporaryFile 臨時文件
QBuffer 從QByteArray中讀取或寫入數據
QProcess 運行外部程序並處理進程間通信
QTcpSocket
QUdpSocket
QSslSocket 利用SSL/TLS在網絡上傳輸加密數據流
後4個爲順序存儲設備
前3個爲隨機存儲設備 seek()

QDataStream 讀寫二進制數據
QTextStream 讀寫文本數據
這2個類考慮了字節順序和文本編碼等問題
比標準c++更加方便,標準c++將這些問題留給了程序員來處理。

QFile QDir QFileInfo

通過QFile打開文件
然後通過QDataStream對象存取

// 寫數據
QImage image("philip.png");
QMap<QString, QColor> map;
	// ...
QFile file("facts.dat");
if (!file.open(QIODevice::WriteOnly)) // file.errorString()
	return;
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_3);
out << quint32(0x12345678) << image << map;

// 讀數據
quint32 n;
QImage image;
QMap<QString, QColor> map;
QFile file("facts.dat");
if (!file.open(QIODevice::ReadOnly))
	return;
QDataStream in(&file);
in.setVersion(QDataStream::Qt_4_3);
in >> n >> image >> map;
// in.status() 錯誤檢查

qRegisterMetaTypeStreamOperators<T>()

// 保存時
out << quint16(out.version());
// 讀取時
quint16 streamVersion;
in >> streamVersion;
if (streamVersion > in.version)
	return false;

QIODevice

QFile sourceFile(source);
QFile destFile(dest);
sourceFile.open(QIODevice::ReadOnly);
dest.open(QIODevice::WriteOnly);
destFile.write(source.readAll());

壓縮解壓縮
qCompress()
qUncompress()
QtIOCompressor()

QTextStream
處理了字符集編碼轉換、不同的行尾符的轉換
還支持C++基本數字類型,處理數字與字符串之間的轉換

QTextStream out(&file);
out << 123 << endl;

stream.setCodec("UTF-8");

可以應用在QString上
QString str;
QTextStream(&str);

QDir 提供與平臺無關的遍歷目錄並獲得有關文件信息的方法

// 計算目錄下所有圖片的大小
qlonglong imageSpace(const QString& path)
{
	QDir dir(path);
	qlonglong size = 0;

	QStringList filters;
	foreach (QByteArray format, QImageReader::supportedImageFormats())
		filter += "*." + format;

	foreach (QString file, dir.entryList(filters, QDir::Files))
		size += QFileInfo(dir, file).size();

	foreach (QString subDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
		size += imageSpace(path + QDir::separator() + subDir);
	
	return size;
}

QDir::convertSeparators() 將斜線轉爲針對具體平臺的正確的分隔符

QDir::currentPath();
QDir::homePath();
QCoreApplication app(argc, argv);
QStringList args = QCoreApplication::arguments();

dir.entryInfoList();
dir.rename();
dir.exists();
dir.mkdir();
dir.rmdir();

QFile::remove();
QFile::exists();

QFileSystemWatcher可以通過發送directoryChanged()、fileChanged()信號,在目錄或者文件發生任何改變時通知我們。

QFile可以使用嵌入資源 e.g. :/datafiles/file.dat

進程間通信
QProcess允許我們執行外部程序並且和它們進行交互,這個類時異步工作的,且它在後臺完成的工作。當外部進程得到數據或者已經完成時,QProcess會發出信號通知我們。

QDir::toNativeSeparators(fileName);

QProcess process;
process.start("convert", args);
process.readAllStandardError();

QProcess信號
readyReadStandardError()
finished()
error()

QTemporaryFile臨時文件

QProcss::execute()靜態函數運行外部程序並等待外部進程完成。

process.waitForStarted()
process.waitForFinished()

Windows下的ActiveQt擴展程序

如果想啓動用戶喜歡的網頁瀏覽器或電子郵件客戶端程序,只需要:
QDesktopServices::openUrl()

第13章 數據庫

1. QSqlQuery 提供了一種直接執行任意SQL語句並處理其結果的方式
2. QSqlTableModel QSqlRelationalTableModel

數據庫驅動
QMYSQL
QOCI 甲骨文公司
QODBC
QSQLITE SQLite3
QSQLITE2

bool createConnection()
{
	QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
	db.setHostName("mozart.konkordia.edu");
	db.setDatabaseName("musicdb");
	db.setUserName("gbatstone");
	db.setPassword("T17aV44");
	if (!db.open())
		return false;
	return true;
}

// 如果結果集爲空或查詢失敗,next()第一調用將返回false
QSqlQuery query; // QSqlQuery query("...");
query.exec("select title, year from cd where year >= 1998");
while (query.next())
{
	QString title = query.value(0).toString();
	int year = query.value(1).toInt();
}

// 對於某些數據庫,以下函數可能會比next()更慢或更耗內存
query.first();
query.last();
query.previous();
query.seek();

// 在操作大數據集時,爲了便於優化調用以下函數然後只使用next()
QSqlQuery::setForwardOnly(true);

query.numRowsAffected()

Oracle風格語法

QSqlQuery query;
query.prepare("insert into cd (id, artistid, title, year)"
	      "values (:id, :artistid, :title, :year)");
query.bindValue(":id", 203);
// ...
query.exec();

ODBC風格語法

QSqlQuery query;
query.prepare("insert into cd (id, artistid, title, year)"
"values (?, ?, ?, ?)");
query.addBindValue(203);
// ...
query.exec();
// 事務處理
// 對於不支持事務處理的數據庫,事務處理函數什麼都不做
QSqlDatabase::database().transaction();
QSqlQuery query;
query.exec("...");
QSqlDatabase::database().commit(); // rollback()

// 測試數據庫特徵
QSqlDriver* driver = QSqlDatabase::database().driver();
driver->hasFeature(QSqlDriver::Transactions);

// 使用多個數據庫連接時
// 第二個參數命名
QSqlDatabase db = QSqlDatabase::addDatabase("QMySql", "mysql");
// 查詢時指定數據庫
QSqlQuery query(QSqlDatabase::database("mysql"));

高級界面接口 QSqlTableModel

QSqlTableModel model;
model.setTable("cd");
model.setFilter("year >= 1998");
model.select();
for (int i=0; i<model.rowCount(); ++i)
{
	QSqlRecord record = model.record(i);
	QString title = record.value("title").toString(); 
	// record.indexOf("title");
	// record.value(titleIndex).toString();
}
// 插入記錄
int row = 0;
model.insertRows(row, 1);
model.setData(model.index(row, 0), 113);
model.setData(model.index(row, 1), "Shanghai My Heart");
// ...
model.submitAll(); // 寫入數據庫,插入失敗返回false

// 更新記錄
record.setValue("title", "Melody A.M.");
// ...
model.setRecord(0, record);
model.submitAll();
// or
model.setData(model.index(0, 1), "Melody A.M.");
// ...
model.submitAll();

// 刪除記錄
model.removeRows(0, 1); // 第二個參數,記錄的個數
model.submitAll();

.pro文件
QT += sql

QDataWidgetMapper
第14章 多線程

子類化QThread並且重新實現run()函數

QThread成員函數
terminate() 終止線程執行
isRunning()
wait()

線程同步

QMutex 每次只能有一個線程可以訪問同一個變量
QReadWriteLock 允許執行多個讀取訪問而不會影響性能
QSemaphore 用於保護(guard)一定數量的相同資源
QWaitCondition 允許一個線程在滿足一定的條件下觸發其他多個線程

QMutex mutex;
mutex.lock();
mutex.unlock();
mutex.tryLock();

QMutexLocker 構造函數鎖定,析構函數解鎖
QMutexLocker locker(&mutex);

QReadWriteLock lock;
lock.lockForRead();
lock.lockForWrite();

QReadLocker
QWriteLocker

// 等價關係
QSemaphore semaphore(1); // QMutex mutex;
semaphore.acquire();     // mutex.lock();
semaphore.release();     // mutex.unlock();

semaphore信號量的一個典型應用場景是:
當兩個線程間傳遞一定量的數據時,這兩個線程會使用某一特定大小的共享喚醒緩衝器。

QWaitCondition w;
w.wait();
w.wakeAll();

線程局部存儲
QThreadStorage<T>
.hasLocalData()
.setLocalData()
.localData()

與主線程通信
主線程是唯一允許創建QApplication或QCoreApplication對象,並且可以對創建的對象調用exec()的線程。

以上同步技術沒有一個可以用來與主線程進行通信,因爲它們會鎖住事件循環並且會凍結用戶界面。

使用信號和槽與主線程通信
例如:
在一個圖像處理軟件中,圖像保存在線程對象中,在界面上選擇對圖像應用的變換時,主線程將包含該事務的事務對象添加到線程對象的事務列表中,線程對象後臺對圖像進行變換,當變換完成後通知主線程

connect(&thread, SIGNAL(transactionStarted(const QString&)),
	startusBar(), SLOT(showMessage(const QString&)));
connect(&thread, SIGNAL(allTransactionsDone()),this, SLOT(allTransactionsDone()));

如果需要刪除一個存在於不同線程中的QObject對象,則必須調用線程安全的QObject::deleteLater()函數,它可以置入一個延期刪除事件。

事件循環
QThread::exec()
QProcess::waitForFinished()
QAbstractSocket::waitForDisconnected()

可重入類:
類的多個實例可以安全地在多個線程中訪問。
QWidget和它的子類都是不可重入的,這樣造成的後果之一就是我們不能在一個來自次線程的窗口部件上直接調用函數。
可以通過發送信號或者:

QMetaObject::invokeMethod(label, SLOT(setText(const QString&)),
	Q_ARG(QString, "Hello"));

發佈了34 篇原創文章 · 獲贊 21 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章